mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-07 04:26:32 +08:00
Without this patch, we only substituted single identifiers. With the patch, we substitute in arbitrary expressions. (Still only as an experiment.) Bug: chromium:1297047 Change-Id: Ifae5cab1a42823559751fbd9dc515edfae02255e Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3487866 Reviewed-by: Kim-Anh Tran <kimanh@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
248 lines
7.6 KiB
TypeScript
248 lines
7.6 KiB
TypeScript
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import * as Common from '../../core/common/common.js';
|
|
import * as FormatterActions from '../../entrypoints/formatter_worker/FormatterActions.js'; // eslint-disable-line rulesdir/es_modules_import
|
|
|
|
const MAX_WORKERS = Math.min(2, navigator.hardwareConcurrency - 1);
|
|
|
|
let formatterWorkerPoolInstance: FormatterWorkerPool;
|
|
|
|
export class FormatterWorkerPool {
|
|
private taskQueue: Task[];
|
|
private workerTasks: Map<Common.Worker.WorkerWrapper, Task|null>;
|
|
|
|
constructor() {
|
|
this.taskQueue = [];
|
|
this.workerTasks = new Map();
|
|
}
|
|
|
|
static instance(): FormatterWorkerPool {
|
|
if (!formatterWorkerPoolInstance) {
|
|
formatterWorkerPoolInstance = new FormatterWorkerPool();
|
|
}
|
|
|
|
return formatterWorkerPoolInstance;
|
|
}
|
|
|
|
private createWorker(): Common.Worker.WorkerWrapper {
|
|
const worker = Common.Worker.WorkerWrapper.fromURL(
|
|
new URL('../../entrypoints/formatter_worker/formatter_worker-entrypoint.js', import.meta.url));
|
|
worker.onmessage = this.onWorkerMessage.bind(this, worker);
|
|
worker.onerror = this.onWorkerError.bind(this, worker);
|
|
return worker;
|
|
}
|
|
|
|
private processNextTask(): void {
|
|
if (!this.taskQueue.length) {
|
|
return;
|
|
}
|
|
|
|
let freeWorker = [...this.workerTasks.keys()].find(worker => !this.workerTasks.get(worker));
|
|
if (!freeWorker && this.workerTasks.size < MAX_WORKERS) {
|
|
freeWorker = this.createWorker();
|
|
}
|
|
if (!freeWorker) {
|
|
return;
|
|
}
|
|
|
|
const task = this.taskQueue.shift();
|
|
if (task) {
|
|
this.workerTasks.set(freeWorker, task);
|
|
freeWorker.postMessage({method: task.method, params: task.params});
|
|
}
|
|
}
|
|
|
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
private onWorkerMessage(worker: Common.Worker.WorkerWrapper, event: MessageEvent<any>): void {
|
|
const task = this.workerTasks.get(worker);
|
|
if (!task) {
|
|
return;
|
|
}
|
|
if (task.isChunked && event.data && !event.data['isLastChunk']) {
|
|
task.callback(event.data);
|
|
return;
|
|
}
|
|
|
|
this.workerTasks.set(worker, null);
|
|
this.processNextTask();
|
|
task.callback(event.data ? event.data : null);
|
|
}
|
|
|
|
private onWorkerError(worker: Common.Worker.WorkerWrapper, event: Event): void {
|
|
console.error(event);
|
|
const task = this.workerTasks.get(worker);
|
|
worker.terminate();
|
|
this.workerTasks.delete(worker);
|
|
|
|
const newWorker = this.createWorker();
|
|
this.workerTasks.set(newWorker, null);
|
|
this.processNextTask();
|
|
if (task) {
|
|
task.callback(null);
|
|
}
|
|
}
|
|
|
|
private runChunkedTask(
|
|
methodName: string, params: {
|
|
[x: string]: string,
|
|
},
|
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
callback: (arg0: boolean, arg1: any) => void): void {
|
|
const task = new Task(methodName, params, onData, true);
|
|
this.taskQueue.push(task);
|
|
this.processNextTask();
|
|
|
|
function onData(data: Object|null): void {
|
|
if (!data) {
|
|
callback(true, null);
|
|
return;
|
|
}
|
|
const isLastChunk = 'isLastChunk' in data && Boolean(data['isLastChunk']);
|
|
const chunk = 'chunk' in data && data['chunk'];
|
|
callback(isLastChunk, chunk);
|
|
}
|
|
}
|
|
|
|
private runTask(methodName: FormatterActions.FormatterActions, params: {
|
|
[x: string]: string|string[][],
|
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
}): Promise<any> {
|
|
return new Promise(resolve => {
|
|
const task = new Task(methodName, params, resolve, false);
|
|
this.taskQueue.push(task);
|
|
this.processNextTask();
|
|
});
|
|
}
|
|
|
|
format(mimeType: string, content: string, indentString: string): Promise<FormatterActions.FormatResult> {
|
|
const parameters = {mimeType: mimeType, content: content, indentString: indentString};
|
|
return this.runTask(FormatterActions.FormatterActions.FORMAT, parameters) as Promise<FormatterActions.FormatResult>;
|
|
}
|
|
|
|
javaScriptIdentifiers(content: string): Promise<{
|
|
name: string,
|
|
offset: number,
|
|
}[]> {
|
|
return this.runTask(FormatterActions.FormatterActions.JAVASCRIPT_IDENTIFIERS, {content: content})
|
|
.then(ids => ids || []);
|
|
}
|
|
|
|
javaScriptSubstitute(expression: string, mapping: Map<string, string>): Promise<string> {
|
|
return this
|
|
.runTask(
|
|
FormatterActions.FormatterActions.JAVASCRIPT_SUBSTITUTE,
|
|
{content: expression, mapping: Array.from(mapping.entries())})
|
|
.then(result => result || '');
|
|
}
|
|
|
|
evaluatableJavaScriptSubstring(content: string): Promise<string> {
|
|
return this.runTask(FormatterActions.FormatterActions.EVALUATE_JAVASCRIPT_SUBSTRING, {content: content})
|
|
.then(text => text || '');
|
|
}
|
|
|
|
parseCSS(content: string, callback: (arg0: boolean, arg1: Array<CSSRule>) => void): void {
|
|
this.runChunkedTask(FormatterActions.FormatterActions.PARSE_CSS, {content: content}, onDataChunk);
|
|
|
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
function onDataChunk(isLastChunk: boolean, data: any): void {
|
|
const rules = (data || [] as CSSRule[]);
|
|
callback(isLastChunk, rules);
|
|
}
|
|
}
|
|
|
|
outlineForMimetype(content: string, mimeType: string, callback: (arg0: boolean, arg1: Array<OutlineItem>) => void):
|
|
boolean {
|
|
switch (mimeType) {
|
|
case 'text/html':
|
|
this.runChunkedTask(FormatterActions.FormatterActions.HTML_OUTLINE, {content: content}, callback);
|
|
return true;
|
|
case 'text/javascript':
|
|
this.runChunkedTask(FormatterActions.FormatterActions.JAVASCRIPT_OUTLINE, {content: content}, callback);
|
|
return true;
|
|
case 'text/css':
|
|
this.parseCSS(content, cssCallback);
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
function cssCallback(isLastChunk: boolean, rules: CSSRule[]): void {
|
|
callback(isLastChunk, rules.map(rule => {
|
|
const title = 'selectorText' in rule ? rule.selectorText : rule.atRule;
|
|
return {line: rule.lineNumber, subtitle: undefined, column: rule.columnNumber, title};
|
|
}));
|
|
}
|
|
}
|
|
|
|
argumentsList(content: string): Promise<string[]> {
|
|
return this.runTask(FormatterActions.FormatterActions.ARGUMENTS_LIST, {content}) as Promise<string[]>;
|
|
}
|
|
}
|
|
|
|
class Task {
|
|
method: string;
|
|
params: {
|
|
[x: string]: string|string[][],
|
|
};
|
|
callback: (arg0: MessageEvent|null) => void;
|
|
isChunked: boolean|undefined;
|
|
constructor(
|
|
method: string, params: {
|
|
[x: string]: string|string[][],
|
|
},
|
|
callback: (arg0: MessageEvent|null) => void, isChunked?: boolean) {
|
|
this.method = method;
|
|
this.params = params;
|
|
this.callback = callback;
|
|
this.isChunked = isChunked;
|
|
}
|
|
}
|
|
|
|
interface CSSProperty {
|
|
name: string;
|
|
nameRange: TextRange;
|
|
value: string;
|
|
valueRange: TextRange;
|
|
range: TextRange;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export function formatterWorkerPool(): FormatterWorkerPool {
|
|
return FormatterWorkerPool.instance();
|
|
}
|
|
|
|
export interface OutlineItem {
|
|
line: number;
|
|
column: number;
|
|
title: string;
|
|
subtitle?: string;
|
|
}
|
|
|
|
export interface CSSStyleRule {
|
|
selectorText: string;
|
|
styleRange: TextRange;
|
|
lineNumber: number;
|
|
columnNumber: number;
|
|
properties: CSSProperty[];
|
|
}
|
|
|
|
export interface CSSAtRule {
|
|
atRule: string;
|
|
lineNumber: number;
|
|
columnNumber: number;
|
|
}
|
|
|
|
export type CSSRule = CSSStyleRule|CSSAtRule;
|
|
|
|
export interface TextRange {
|
|
startLine: number;
|
|
startColumn: number;
|
|
endLine: number;
|
|
endColumn: number;
|
|
}
|