From 79f8a0adfcdac2a731861fbd81f17c2485f7a4cd Mon Sep 17 00:00:00 2001 From: azu Date: Sun, 2 Aug 2020 13:32:02 +0900 Subject: [PATCH] feat(webextension): support multiple workers --- .../webextension/app/scripts/background.ts | 52 +++++++++++++++---- .../app/scripts/background/textlint.ts | 35 ++++++++++--- .../webextension/app/scripts/contentScript.ts | 29 ++++++++--- .../app/scripts/install-dialog.ts | 2 +- 4 files changed, 93 insertions(+), 25 deletions(-) diff --git a/packages/webextension/app/scripts/background.ts b/packages/webextension/app/scripts/background.ts index 45489b9..7e0e650 100644 --- a/packages/webextension/app/scripts/background.ts +++ b/packages/webextension/app/scripts/background.ts @@ -3,6 +3,8 @@ import { createBackgroundEndpoint, isMessagePort } from "comlink-extension"; import * as Comlink from "comlink"; import { createTextlintWorker } from "./background/textlint"; import { openDatabase } from "./background/openDatabase"; +import { LintEngineAPI } from "textchecker-element"; +import { TextlintFixResult, TextlintMessage, TextlintResult } from "@textlint/types"; browser.runtime.onInstalled.addListener((details) => { console.log("previousVersion", details.previousVersion); @@ -63,10 +65,8 @@ type ThenArg = T extends PromiseLike ? U : T; type DataBase = ReturnType; export type backgroundExposedObject = { - lintText: ReturnType["lintText"]; - fixText: ReturnType["fixText"]; addScript: ThenArg["addScript"]; -}; +} & LintEngineAPI; browser.runtime.onConnect.addListener(async (port) => { if (isMessagePort(port)) { return; @@ -78,22 +78,56 @@ browser.runtime.onConnect.addListener(async (port) => { const blob = new Blob([script.code], { type: "application/javascript" }); return createTextlintWorker(URL.createObjectURL(blob)); }); - // TODO multiple worker? - const lintText:ReturnType["lintText"] = () + console.log("[Background] workers", workers); + // Support multiple workers + const ext = ".md"; + const lintEngine: LintEngineAPI = { + async lintText({ text }: { text: string }): Promise { + console.log("[Background] text", text); + const allLintResults = await Promise.all( + workers.map((worker) => { + return worker.createLintEngine({ ext }).lintText({ text }); + }) + ); + return allLintResults.flat(); + }, + async fixText({ text, message }): Promise<{ output: string }> { + if (!message.fix || !message.fix.range) { + return { output: text }; + } + // replace fix.range[0, 1] with fix.text + return { + output: text.slice(0, message.fix.range[0]) + message.fix.text + text.slice(message.fix.range[1]) + }; + }, + async fixAll({ text }: { text: string }): Promise { + return workers.slice(1).reduce((promise, worker) => { + return promise.then(() => { + return worker.createLintEngine({ ext }).fixAll({ text }); + }); + }, workers[0].createLintEngine({ ext }).fixAll({ text })); + }, + fixRule({ text, message }: { text: string; message: TextlintMessage }): Promise { + return workers.slice(1).reduce((promise, worker) => { + return promise.then(() => { + return worker.createLintEngine({ ext }).fixRule({ text, message }); + }); + }, workers[0].createLintEngine({ ext }).fixRule({ text, message })); + } + }; const backgroundExposedObject: backgroundExposedObject = { - lintText: textlint.lintText, - fixText: textlint.fixText, + ...lintEngine, addScript: (script) => { return db.addScript(script); } }; port.onDisconnect.addListener(() => { - console.log("dispose worker"); + console.log("[Background] dispose worker"); workers.forEach((worker) => { worker.dispose(); }); }); - console.log(port.sender); + console.log("[Background] content port", port); await Promise.all(workers.map((worker) => worker.ready())); Comlink.expose(backgroundExposedObject, createBackgroundEndpoint(port)); }); diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index f73b552..880e9ef 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -1,5 +1,6 @@ import { browser } from "webextension-polyfill-ts"; -import { TextlintFixResult, TextlintMessage, TextlintResult } from "@textlint/types"; +import type { TextlintFixResult, TextlintMessage, TextlintResult } from "@textlint/types"; +import type { LintEngineAPI } from "textchecker-element"; import { TextlintWorkerCommandFix, TextlintWorkerCommandLint, TextlintWorkerCommandResponse } from "@textlint/compiler"; browser.runtime.onInstalled.addListener((details) => { @@ -49,14 +50,14 @@ const createWorkerRef = (worker: Worker) => { export const createTextlintWorker = (defaultWorkerUrl: string | URL = "download/textlint.js") => { const defaultWorker = new Worker(defaultWorkerUrl); const workerRef = createWorkerRef(defaultWorker); - const lintText = async ({ text, ext }: { text: string; ext: string }): Promise => { + const lintText = async ({ text, ext }: { text: string; ext: string }): Promise => { return new Promise((resolve, _reject) => { workerRef.current.addEventListener( "message", function (event) { const data: TextlintWorkerCommandResponse = event.data; if (data.command === "lint:result") { - resolve(data.result); + resolve([data.result]); } }, { @@ -77,7 +78,7 @@ export const createTextlintWorker = (defaultWorkerUrl: string | URL = "download/ }: { text: string; ext: string; - message: TextlintMessage; + message?: TextlintMessage; }): Promise => { return new Promise((resolve, _reject) => { workerRef.current.addEventListener( @@ -95,14 +96,34 @@ export const createTextlintWorker = (defaultWorkerUrl: string | URL = "download/ return workerRef.current.postMessage({ command: "fix", text, - ruleId: message.ruleId, + ruleId: message?.ruleId, ext: ext } as TextlintWorkerCommandFix); }); }; return { - lintText, - fixText, + createLintEngine({ ext }: { ext: string }) { + const lintEngine: LintEngineAPI = { + lintText: ({ text }) => lintText({ text, ext }), + fixText: async ({ text, message }): Promise<{ output: string }> => { + if (!message.fix || !message.fix.range) { + return { output: text }; + } + // replace fix.range[0, 1] with fix.text + return { + output: + text.slice(0, message.fix.range[0]) + message.fix.text + text.slice(message.fix.range[1]) + }; + }, + fixAll({ text }: { text: string }): Promise { + return fixText({ text, ext }); + }, + fixRule({ text, message }: { text: string; message: TextlintMessage }): Promise { + return fixText({ text, message, ext }); + } + }; + return lintEngine; + }, ready() { return workerRef.ready(); }, diff --git a/packages/webextension/app/scripts/contentScript.ts b/packages/webextension/app/scripts/contentScript.ts index 62bd8ab..a9c574c 100644 --- a/packages/webextension/app/scripts/contentScript.ts +++ b/packages/webextension/app/scripts/contentScript.ts @@ -1,19 +1,32 @@ // @ts-ignore - replace webcomponent to shim import "@webcomponents/custom-elements"; import { browser } from "webextension-polyfill-ts"; -import { attachToTextArea } from "textchecker-element"; +import { attachToTextArea, LintEngineAPI } from "textchecker-element"; import { createEndpoint } from "comlink-extension"; import * as Comlink from "comlink"; import type { backgroundExposedObject } from "./background"; const port = Comlink.wrap(createEndpoint(browser.runtime.connect())); const targetElement = document.querySelectorAll("textarea"); -targetElement.forEach((element) => { - const extOfTextarea = ".md"; - return attachToTextArea({ - textAreaElement: element, - lintText: (args) => port.lintText({ ...args, ext: extOfTextarea }), - fixText: (args) => port.fixText({ ...args, ext: extOfTextarea }), - lintingDebounceMs: 200 + +async function contentScriptMain() { + const lintEngine: LintEngineAPI = { + lintText: port.lintText, + fixText: port.fixText, + fixAll: port.fixAll, + fixRule: port.fixRule + }; + console.log("[ContentScript]", lintEngine); + targetElement.forEach((element) => { + return attachToTextArea({ + textAreaElement: element, + lintingDebounceMs: 200, + lintEngine: lintEngine + }); }); +} + +console.log("[ContentScript]", "main"); +contentScriptMain().catch((error) => { + console.error("[texlint editor ContentScriptError]", error); }); diff --git a/packages/webextension/app/scripts/install-dialog.ts b/packages/webextension/app/scripts/install-dialog.ts index d1f3ff4..861faba 100644 --- a/packages/webextension/app/scripts/install-dialog.ts +++ b/packages/webextension/app/scripts/install-dialog.ts @@ -16,7 +16,7 @@ async function installHandler() { console.log(content); // Save to DB await port.addScript({ - name: "default", + name: location.href, code: content, pattern: "**/*" });