From 4c793fa104cc3392a62a4ebcbfd7465ac9ec2a74 Mon Sep 17 00:00:00 2001 From: otariidae Date: Sun, 18 Jun 2023 19:27:49 +0900 Subject: [PATCH 1/9] feat(script-compiler): add error responses --- .../src/CodeGenerator/worker-codegen.ts | 26 +++++- packages/textchecker-element/index.ts | 80 +++++++++++++++---- .../src/attach-to-text-area.ts | 12 ++- .../app/scripts/background/textlint.ts | 35 ++++++-- 4 files changed, 126 insertions(+), 27 deletions(-) diff --git a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts index 82ded81..c8c5848 100644 --- a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts +++ b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts @@ -40,10 +40,22 @@ export type TextlintWorkerCommandResponseFix = { command: "fix:result"; result: TextlintFixResult; }; +export type TextlintWorkerCommandResponseLintError = { + id: MessageId | undefined; + command: "lint:error"; + error: any; +}; +export type TextlintWorkerCommandResponseFixError = { + id: MessageId | undefined; + command: "fix:error"; + error: any; +}; export type TextlintWorkerCommandResponse = | TextlintWorkerCommandResponseInit | TextlintWorkerCommandResponseLint - | TextlintWorkerCommandResponseFix; + | TextlintWorkerCommandResponseFix + | TextlintWorkerCommandResponseLintError + | TextlintWorkerCommandResponseFixError; export const generateCode = async (config: TextlintConfigDescriptor) => { // macro replacement @@ -139,6 +151,12 @@ self.addEventListener('message', (event) => { command: "lint:result", result }); + }).catch(error => { + return self.postMessage({ + id: data.id, + command: "lint:error", + error + }); }); case "fix": return kernel.fixText(data.text, { @@ -153,6 +171,12 @@ self.addEventListener('message', (event) => { command: "fix:result", result }); + }).catch(error => { + return self.postMessage({ + id: data.id, + command: "fix:error", + error + }); }); default: console.log("Unknown command: " + data.command); diff --git a/packages/textchecker-element/index.ts b/packages/textchecker-element/index.ts index 2c3bbb9..eb83312 100644 --- a/packages/textchecker-element/index.ts +++ b/packages/textchecker-element/index.ts @@ -41,16 +41,40 @@ const generateMessageId = () => crypto.randomUUID(); const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { const lintText: LintEngineAPI["lintText"] = async ({ text }: { text: string }): Promise => { updateStatus("linting..."); - return new Promise((resolve, _reject) => { + return new Promise((resolve, reject) => { const id = generateMessageId(); - worker.addEventListener("message", function handler(event) { - const data: TextlintWorkerCommandResponse = event.data; - if (data.command === "lint:result" && data.id === id) { - resolve([data.result]); - worker.removeEventListener("message", handler); + function onMessage(event: MessageEvent) { + const data = event.data; + if ("id" in data && data.id === id) { + if (data.command === "lint:error") { + reject(data.error); + updateStatus("failed to lint"); + } else if (data.command === "lint:result") { + resolve([data.result]); + updateStatus("linted"); + } + worker.removeEventListener("message", onMessage); + worker.removeEventListener("messageerror", onMessageError); + worker.removeEventListener("error", onError); } - updateStatus("linted"); - }); + } + function onMessageError(event: MessageEvent) { + reject(event.data); + updateStatus("failed to lint"); + worker.removeEventListener("message", onMessage); + worker.removeEventListener("messageerror", onMessageError); + worker.removeEventListener("error", onError); + } + function onError(event: ErrorEvent) { + reject(event); + updateStatus("failed to lint"); + worker.removeEventListener("message", onMessage); + worker.removeEventListener("messageerror", onMessageError); + worker.removeEventListener("error", onError); + } + worker.addEventListener("message", onMessage); + worker.addEventListener("messageerror", onMessageError); + worker.addEventListener("error", onError); return worker.postMessage({ id, command: "lint", @@ -67,16 +91,40 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { message?: TextlintMessage; }): Promise => { updateStatus("fixing..."); - return new Promise((resolve, _reject) => { + return new Promise((resolve, reject) => { const id = generateMessageId(); - worker.addEventListener("message", function handler(event) { - const data: TextlintWorkerCommandResponse = event.data; - if (data.command === "fix:result" && data.id === id) { - resolve(data.result); - worker.removeEventListener("message", handler); + function onMessage(event: MessageEvent) { + const data = event.data; + if ("id" in data && data.id === id) { + if (data.command === "fix:error") { + reject(data.error); + updateStatus("failed to fix"); + } else if (data.command === "fix:result") { + resolve(data.result); + updateStatus("fixed"); + } + worker.removeEventListener("message", onMessage); + worker.removeEventListener("messageerror", onMessageError); + worker.removeEventListener("error", onError); } - updateStatus("fixed"); - }); + } + function onMessageError(event: MessageEvent) { + reject(event.data); + updateStatus("failed to fix"); + worker.removeEventListener("message", onMessage); + worker.removeEventListener("messageerror", onMessageError); + worker.removeEventListener("error", onError); + } + function onError(event: ErrorEvent) { + reject(event); + updateStatus("failed to fix"); + worker.removeEventListener("message", onMessage); + worker.removeEventListener("messageerror", onMessageError); + worker.removeEventListener("error", onError); + } + worker.addEventListener("message", onMessage); + worker.addEventListener("messageerror", onMessageError); + worker.addEventListener("error", onError); return worker.postMessage({ id, command: "fix", diff --git a/packages/textchecker-element/src/attach-to-text-area.ts b/packages/textchecker-element/src/attach-to-text-area.ts index 0b74ccd..8f8a873 100644 --- a/packages/textchecker-element/src/attach-to-text-area.ts +++ b/packages/textchecker-element/src/attach-to-text-area.ts @@ -155,9 +155,15 @@ export const attachToTextArea = ({ // dismiss card before update annotations // dismissCards(); const text = textAreaElement.value; - const results = await lintEngine.lintText({ - text - }); + let results; + try { + results = await lintEngine.lintText({ + text + }); + } catch (e) { + debug("lint error", e); + results = [] as const; + } debug("lint results", results); const updateText = async (newText: string, card: TextCheckerCard) => { const currentText = textAreaElement.value; diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index e4458a4..87a6140 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -56,15 +56,36 @@ export const createTextlintWorker = (script: Script) => { const defaultWorker = new Worker(workerUrl); const workerRef = createWorkerRef(defaultWorker); const lintText = async ({ text, ext }: { text: string; ext: string }): Promise => { - return new Promise((resolve, _reject) => { + return new Promise((resolve, reject) => { const id = generateMessageId(); - workerRef.current.addEventListener("message", function handler(event) { - const data: TextlintWorkerCommandResponse = event.data; - if (data.command === "lint:result" && data.id === id) { - resolve([data.result]); - workerRef.current.removeEventListener("message", handler); + function onMessage(event: MessageEvent) { + const data = event.data; + if ("id" in data && data.id === id) { + if (data.command === "lint:error") { + reject(data.error); + } else if (data.command === "lint:result") { + resolve([data.result]); + } + workerRef.current.removeEventListener("message", onMessage); + workerRef.current.removeEventListener("messageerror", onMessageError); + workerRef.current.removeEventListener("error", onError); } - }); + } + function onMessageError(event: MessageEvent) { + reject(event.data); + workerRef.current.removeEventListener("message", onMessage); + workerRef.current.removeEventListener("messageerror", onMessageError); + workerRef.current.removeEventListener("error", onError); + } + function onError(event: ErrorEvent) { + reject(event); + workerRef.current.removeEventListener("message", onMessage); + workerRef.current.removeEventListener("messageerror", onMessageError); + workerRef.current.removeEventListener("error", onError); + } + workerRef.current.addEventListener("message", onMessage); + workerRef.current.addEventListener("messageerror", onMessageError); + workerRef.current.addEventListener("error", onError); return workerRef.current.postMessage({ id, command: "lint", From cd14cf6257d3c9d20c140167d597758537f35e8e Mon Sep 17 00:00:00 2001 From: otariidae Date: Sun, 18 Jun 2023 22:06:54 +0900 Subject: [PATCH 2/9] refactor: add missing error handling --- .../app/scripts/background/textlint.ts | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index 87a6140..394d6d2 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -106,15 +106,36 @@ export const createTextlintWorker = (script: Script) => { ext: string; message?: TextlintMessage; }): Promise => { - return new Promise((resolve, _reject) => { + return new Promise((resolve, reject) => { const id = generateMessageId(); - workerRef.current.addEventListener("message", function handler(event) { - const data: TextlintWorkerCommandResponse = event.data; - if (data.command === "fix:result" && data.id === id) { - resolve(data.result); - workerRef.current.removeEventListener("message", handler); + function onMessage(event: MessageEvent) { + const data = event.data; + if ("id" in data && data.id === id) { + if (data.command === "fix:error") { + reject(data.error); + } else if (data.command === "fix:result") { + resolve(data.result); + } + workerRef.current.removeEventListener("message", onMessage); + workerRef.current.removeEventListener("messageerror", onMessageError); + workerRef.current.removeEventListener("error", onError); } - }); + } + function onMessageError(event: MessageEvent) { + reject(event.data); + workerRef.current.removeEventListener("message", onMessage); + workerRef.current.removeEventListener("messageerror", onMessageError); + workerRef.current.removeEventListener("error", onError); + } + function onError(event: ErrorEvent) { + reject(event); + workerRef.current.removeEventListener("message", onMessage); + workerRef.current.removeEventListener("messageerror", onMessageError); + workerRef.current.removeEventListener("error", onError); + } + workerRef.current.addEventListener("message", onMessage); + workerRef.current.addEventListener("messageerror", onMessageError); + workerRef.current.addEventListener("error", onError); return workerRef.current.postMessage({ id, command: "fix", From 1bc8669fde0647bb4e0b6ad5b5af985923abd74b Mon Sep 17 00:00:00 2001 From: otariidae Date: Sun, 18 Jun 2023 22:18:16 +0900 Subject: [PATCH 3/9] feat: unite error types --- .../src/CodeGenerator/worker-codegen.ts | 16 +++++----------- packages/textchecker-element/index.ts | 4 ++-- .../app/scripts/background/textlint.ts | 4 ++-- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts index c8c5848..5bfe5e9 100644 --- a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts +++ b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts @@ -40,22 +40,16 @@ export type TextlintWorkerCommandResponseFix = { command: "fix:result"; result: TextlintFixResult; }; -export type TextlintWorkerCommandResponseLintError = { +export type TextlintWorkerCommandResponseError = { id: MessageId | undefined; - command: "lint:error"; - error: any; -}; -export type TextlintWorkerCommandResponseFixError = { - id: MessageId | undefined; - command: "fix:error"; + command: "error"; error: any; }; export type TextlintWorkerCommandResponse = | TextlintWorkerCommandResponseInit | TextlintWorkerCommandResponseLint | TextlintWorkerCommandResponseFix - | TextlintWorkerCommandResponseLintError - | TextlintWorkerCommandResponseFixError; + | TextlintWorkerCommandResponseError; export const generateCode = async (config: TextlintConfigDescriptor) => { // macro replacement @@ -154,7 +148,7 @@ self.addEventListener('message', (event) => { }).catch(error => { return self.postMessage({ id: data.id, - command: "lint:error", + command: "error", error }); }); @@ -174,7 +168,7 @@ self.addEventListener('message', (event) => { }).catch(error => { return self.postMessage({ id: data.id, - command: "fix:error", + command: "error", error }); }); diff --git a/packages/textchecker-element/index.ts b/packages/textchecker-element/index.ts index eb83312..af9b826 100644 --- a/packages/textchecker-element/index.ts +++ b/packages/textchecker-element/index.ts @@ -46,7 +46,7 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { - if (data.command === "lint:error") { + if (data.command === "error") { reject(data.error); updateStatus("failed to lint"); } else if (data.command === "lint:result") { @@ -96,7 +96,7 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { - if (data.command === "fix:error") { + if (data.command === "error") { reject(data.error); updateStatus("failed to fix"); } else if (data.command === "fix:result") { diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index 394d6d2..285b3b0 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -61,7 +61,7 @@ export const createTextlintWorker = (script: Script) => { function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { - if (data.command === "lint:error") { + if (data.command === "error") { reject(data.error); } else if (data.command === "lint:result") { resolve([data.result]); @@ -111,7 +111,7 @@ export const createTextlintWorker = (script: Script) => { function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { - if (data.command === "fix:error") { + if (data.command === "error") { reject(data.error); } else if (data.command === "fix:result") { resolve(data.result); From 74f6fe2c34f563b9e5f849203188034e57e2c133 Mon Sep 17 00:00:00 2001 From: otariidae Date: Tue, 20 Jun 2023 01:53:16 +0900 Subject: [PATCH 4/9] refactor: use AbortController to remove event listeners --- packages/textchecker-element/index.ts | 38 +++++++------------ .../app/scripts/background/textlint.ts | 38 +++++++------------ 2 files changed, 28 insertions(+), 48 deletions(-) diff --git a/packages/textchecker-element/index.ts b/packages/textchecker-element/index.ts index af9b826..f49b441 100644 --- a/packages/textchecker-element/index.ts +++ b/packages/textchecker-element/index.ts @@ -43,6 +43,7 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { updateStatus("linting..."); return new Promise((resolve, reject) => { const id = generateMessageId(); + const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { @@ -53,28 +54,22 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { resolve([data.result]); updateStatus("linted"); } - worker.removeEventListener("message", onMessage); - worker.removeEventListener("messageerror", onMessageError); - worker.removeEventListener("error", onError); + controller.abort(); } } function onMessageError(event: MessageEvent) { reject(event.data); updateStatus("failed to lint"); - worker.removeEventListener("message", onMessage); - worker.removeEventListener("messageerror", onMessageError); - worker.removeEventListener("error", onError); + controller.abort(); } function onError(event: ErrorEvent) { reject(event); updateStatus("failed to lint"); - worker.removeEventListener("message", onMessage); - worker.removeEventListener("messageerror", onMessageError); - worker.removeEventListener("error", onError); + controller.abort(); } - worker.addEventListener("message", onMessage); - worker.addEventListener("messageerror", onMessageError); - worker.addEventListener("error", onError); + worker.addEventListener("message", onMessage, { signal: controller.signal }); + worker.addEventListener("messageerror", onMessageError, { signal: controller.signal }); + worker.addEventListener("error", onError, { signal: controller.signal }); return worker.postMessage({ id, command: "lint", @@ -93,6 +88,7 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { updateStatus("fixing..."); return new Promise((resolve, reject) => { const id = generateMessageId(); + const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { @@ -103,28 +99,22 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { resolve(data.result); updateStatus("fixed"); } - worker.removeEventListener("message", onMessage); - worker.removeEventListener("messageerror", onMessageError); - worker.removeEventListener("error", onError); + controller.abort(); } } function onMessageError(event: MessageEvent) { reject(event.data); updateStatus("failed to fix"); - worker.removeEventListener("message", onMessage); - worker.removeEventListener("messageerror", onMessageError); - worker.removeEventListener("error", onError); + controller.abort(); } function onError(event: ErrorEvent) { reject(event); updateStatus("failed to fix"); - worker.removeEventListener("message", onMessage); - worker.removeEventListener("messageerror", onMessageError); - worker.removeEventListener("error", onError); + controller.abort(); } - worker.addEventListener("message", onMessage); - worker.addEventListener("messageerror", onMessageError); - worker.addEventListener("error", onError); + worker.addEventListener("message", onMessage, { signal: controller.signal }); + worker.addEventListener("messageerror", onMessageError, { signal: controller.signal }); + worker.addEventListener("error", onError, { signal: controller.signal }); return worker.postMessage({ id, command: "fix", diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index 285b3b0..cdd5af8 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -58,6 +58,7 @@ export const createTextlintWorker = (script: Script) => { const lintText = async ({ text, ext }: { text: string; ext: string }): Promise => { return new Promise((resolve, reject) => { const id = generateMessageId(); + const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { @@ -66,26 +67,20 @@ export const createTextlintWorker = (script: Script) => { } else if (data.command === "lint:result") { resolve([data.result]); } - workerRef.current.removeEventListener("message", onMessage); - workerRef.current.removeEventListener("messageerror", onMessageError); - workerRef.current.removeEventListener("error", onError); + controller.abort(); } } function onMessageError(event: MessageEvent) { reject(event.data); - workerRef.current.removeEventListener("message", onMessage); - workerRef.current.removeEventListener("messageerror", onMessageError); - workerRef.current.removeEventListener("error", onError); + controller.abort(); } function onError(event: ErrorEvent) { reject(event); - workerRef.current.removeEventListener("message", onMessage); - workerRef.current.removeEventListener("messageerror", onMessageError); - workerRef.current.removeEventListener("error", onError); + controller.abort(); } - workerRef.current.addEventListener("message", onMessage); - workerRef.current.addEventListener("messageerror", onMessageError); - workerRef.current.addEventListener("error", onError); + workerRef.current.addEventListener("message", onMessage, { signal: controller.signal }); + workerRef.current.addEventListener("messageerror", onMessageError, { signal: controller.signal }); + workerRef.current.addEventListener("error", onError, { signal: controller.signal }); return workerRef.current.postMessage({ id, command: "lint", @@ -108,6 +103,7 @@ export const createTextlintWorker = (script: Script) => { }): Promise => { return new Promise((resolve, reject) => { const id = generateMessageId(); + const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; if ("id" in data && data.id === id) { @@ -116,26 +112,20 @@ export const createTextlintWorker = (script: Script) => { } else if (data.command === "fix:result") { resolve(data.result); } - workerRef.current.removeEventListener("message", onMessage); - workerRef.current.removeEventListener("messageerror", onMessageError); - workerRef.current.removeEventListener("error", onError); + controller.abort(); } } function onMessageError(event: MessageEvent) { reject(event.data); - workerRef.current.removeEventListener("message", onMessage); - workerRef.current.removeEventListener("messageerror", onMessageError); - workerRef.current.removeEventListener("error", onError); + controller.abort(); } function onError(event: ErrorEvent) { reject(event); - workerRef.current.removeEventListener("message", onMessage); - workerRef.current.removeEventListener("messageerror", onMessageError); - workerRef.current.removeEventListener("error", onError); + controller.abort(); } - workerRef.current.addEventListener("message", onMessage); - workerRef.current.addEventListener("messageerror", onMessageError); - workerRef.current.addEventListener("error", onError); + workerRef.current.addEventListener("message", onMessage, { signal: controller.signal }); + workerRef.current.addEventListener("messageerror", onMessageError, { signal: controller.signal }); + workerRef.current.addEventListener("error", onError, { signal: controller.signal }); return workerRef.current.postMessage({ id, command: "fix", From f2fcc2462cafd0e2b8cc192ca36d9e24977921f8 Mon Sep 17 00:00:00 2001 From: otariidae Date: Tue, 20 Jun 2023 02:06:02 +0900 Subject: [PATCH 5/9] wip: introduce TextlintError --- .../src/CodeGenerator/worker-codegen.ts | 38 ++++++++++++------- .../@textlint/script-compiler/src/index.ts | 4 +- packages/textchecker-element/index.ts | 24 ++++-------- .../app/scripts/background/textlint.ts | 16 ++------ 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts index 5bfe5e9..5a90bb8 100644 --- a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts +++ b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts @@ -40,16 +40,25 @@ export type TextlintWorkerCommandResponseFix = { command: "fix:result"; result: TextlintFixResult; }; -export type TextlintWorkerCommandResponseError = { - id: MessageId | undefined; - command: "error"; - error: any; -}; export type TextlintWorkerCommandResponse = | TextlintWorkerCommandResponseInit | TextlintWorkerCommandResponseLint - | TextlintWorkerCommandResponseFix - | TextlintWorkerCommandResponseError; + | TextlintWorkerCommandResponseFix; + +export type TextlintErrorOptions = { + id: MessageId | undefined; +} & ErrorOptions; +export class TextlintError extends Error { + public id: MessageId | undefined; + static { + this.prototype.name = "TextlintError"; + } + constructor(message: string, options: TextlintErrorOptions = { id: undefined }) { + const { id, ...errorOptions } = options; + super(message, errorOptions); + this.id = id; + } +} export const generateCode = async (config: TextlintConfigDescriptor) => { // macro replacement @@ -63,6 +72,7 @@ export const generateCode = async (config: TextlintConfigDescriptor) => { import { TextlintKernel } from "@textlint/kernel"; import { moduleInterop } from "@textlint/module-interop"; import { parseOptionsFromConfig } from "@textlint/config-partial-parser" +import { TextlintError } from "@textlint/script-compiler/module/CodeGenerator/worker-codegen.js"; // FIX ME: to make import smarter const kernel = new TextlintKernel(); const rules = ${stringify( config.rules.flatMap((rule) => { @@ -140,17 +150,19 @@ self.addEventListener('message', (event) => { filePath: "/path/to/README" + data.ext, ext: data.ext, }).then(result => { + // return Promise.reject(new TypeError("lintText is not supported")); // for debug + // throw new Error("something went wrong"); // for debug return self.postMessage({ id: data.id, command: "lint:result", result }); }).catch(error => { - return self.postMessage({ + const textlintError = new TextlintError("failed to lint text", { id: data.id, - command: "error", - error + cause: error }); + reportError(new ErrorEvent("textlint error", { error: textlintError, message: textlintError.message })); }); case "fix": return kernel.fixText(data.text, { @@ -166,11 +178,11 @@ self.addEventListener('message', (event) => { result }); }).catch(error => { - return self.postMessage({ + const textlintError = new TextlintError("failed to fix text", { id: data.id, - command: "error", - error + cause: error }); + reportError(new ErrorEvent("textlint error", { error: textlintError, message: textlintError.message })); }); default: console.log("Unknown command: " + data.command); diff --git a/packages/@textlint/script-compiler/src/index.ts b/packages/@textlint/script-compiler/src/index.ts index 988ea54..e9f3511 100644 --- a/packages/@textlint/script-compiler/src/index.ts +++ b/packages/@textlint/script-compiler/src/index.ts @@ -7,5 +7,7 @@ export type { TextlintWorkerCommandLint, TextlintWorkerCommandResponseFix, TextlintWorkerCommandResponseInit, - TextlintWorkerCommandResponseLint + TextlintWorkerCommandResponseLint, + TextlintErrorOptions, + TextlintError } from "./CodeGenerator/worker-codegen"; diff --git a/packages/textchecker-element/index.ts b/packages/textchecker-element/index.ts index f49b441..204a2f9 100644 --- a/packages/textchecker-element/index.ts +++ b/packages/textchecker-element/index.ts @@ -46,14 +46,9 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if ("id" in data && data.id === id) { - if (data.command === "error") { - reject(data.error); - updateStatus("failed to lint"); - } else if (data.command === "lint:result") { - resolve([data.result]); - updateStatus("linted"); - } + if (data.command === "lint:result" && data.id === id) { + resolve([data.result]); + updateStatus("linted"); controller.abort(); } } @@ -63,6 +58,7 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { controller.abort(); } function onError(event: ErrorEvent) { + debugger; reject(event); updateStatus("failed to lint"); controller.abort(); @@ -91,14 +87,9 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if ("id" in data && data.id === id) { - if (data.command === "error") { - reject(data.error); - updateStatus("failed to fix"); - } else if (data.command === "fix:result") { - resolve(data.result); - updateStatus("fixed"); - } + if (data.command === "fix:result" && data.id === id) { + resolve(data.result); + updateStatus("fixed"); controller.abort(); } } @@ -108,6 +99,7 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { controller.abort(); } function onError(event: ErrorEvent) { + debugger; reject(event); updateStatus("failed to fix"); controller.abort(); diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index cdd5af8..7e1fdf5 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -61,12 +61,8 @@ export const createTextlintWorker = (script: Script) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if ("id" in data && data.id === id) { - if (data.command === "error") { - reject(data.error); - } else if (data.command === "lint:result") { - resolve([data.result]); - } + if (data.command === "lint:result" && data.id === id) { + resolve([data.result]); controller.abort(); } } @@ -106,12 +102,8 @@ export const createTextlintWorker = (script: Script) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if ("id" in data && data.id === id) { - if (data.command === "error") { - reject(data.error); - } else if (data.command === "fix:result") { - resolve(data.result); - } + if (data.command === "fix:result" && data.id === id) { + resolve(data.result); controller.abort(); } } From 13756337eb631c7600d4b6cfb7f2cb648baae14b Mon Sep 17 00:00:00 2001 From: otariidae Date: Wed, 21 Jun 2023 08:32:52 +0900 Subject: [PATCH 6/9] feat: add error response as only error from Textlint worker --- .../src/CodeGenerator/worker-codegen.ts | 63 +++++++++++-------- .../@textlint/script-compiler/src/index.ts | 3 +- packages/textchecker-element/index.ts | 40 ++++-------- .../app/scripts/background/textlint.ts | 32 +++------- 4 files changed, 59 insertions(+), 79 deletions(-) diff --git a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts index 5a90bb8..1229868 100644 --- a/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts +++ b/packages/@textlint/script-compiler/src/CodeGenerator/worker-codegen.ts @@ -40,25 +40,16 @@ export type TextlintWorkerCommandResponseFix = { command: "fix:result"; result: TextlintFixResult; }; +export type TextlintWorkerCommandResponseError = { + id?: MessageId | undefined; + command: "error"; + error: Error; +}; export type TextlintWorkerCommandResponse = | TextlintWorkerCommandResponseInit | TextlintWorkerCommandResponseLint - | TextlintWorkerCommandResponseFix; - -export type TextlintErrorOptions = { - id: MessageId | undefined; -} & ErrorOptions; -export class TextlintError extends Error { - public id: MessageId | undefined; - static { - this.prototype.name = "TextlintError"; - } - constructor(message: string, options: TextlintErrorOptions = { id: undefined }) { - const { id, ...errorOptions } = options; - super(message, errorOptions); - this.id = id; - } -} + | TextlintWorkerCommandResponseFix + | TextlintWorkerCommandResponseError; export const generateCode = async (config: TextlintConfigDescriptor) => { // macro replacement @@ -72,7 +63,6 @@ export const generateCode = async (config: TextlintConfigDescriptor) => { import { TextlintKernel } from "@textlint/kernel"; import { moduleInterop } from "@textlint/module-interop"; import { parseOptionsFromConfig } from "@textlint/config-partial-parser" -import { TextlintError } from "@textlint/script-compiler/module/CodeGenerator/worker-codegen.js"; // FIX ME: to make import smarter const kernel = new TextlintKernel(); const rules = ${stringify( config.rules.flatMap((rule) => { @@ -134,6 +124,24 @@ const assignConfig = (textlintrc) => { }); } }; +self.addEventListener('error', (event) => { + self.postMessage({ + command: "error", + // wrapping any type error with Error + error: new Error("unexpected error", { + cause: event.error + }) + }) +}); +self.addEventListener('unhandledrejection', (event) => { + self.postMessage({ + command: "error", + // wrapping any type error with Error + error: new Error("unexpected unhandled promise rejection", { + cause: event.reason + }) + }) +}); self.addEventListener('message', (event) => { const data = event.data; const rules = data.ruleId === undefined @@ -150,19 +158,20 @@ self.addEventListener('message', (event) => { filePath: "/path/to/README" + data.ext, ext: data.ext, }).then(result => { - // return Promise.reject(new TypeError("lintText is not supported")); // for debug - // throw new Error("something went wrong"); // for debug return self.postMessage({ id: data.id, command: "lint:result", result }); }).catch(error => { - const textlintError = new TextlintError("failed to lint text", { + return self.postMessage({ id: data.id, - cause: error - }); - reportError(new ErrorEvent("textlint error", { error: textlintError, message: textlintError.message })); + command: "error", + // wrapping any type error with Error + error: new Error("failed to lint text", { + cause: error + }) + }) }); case "fix": return kernel.fixText(data.text, { @@ -178,11 +187,11 @@ self.addEventListener('message', (event) => { result }); }).catch(error => { - const textlintError = new TextlintError("failed to fix text", { + return self.postMessage({ id: data.id, - cause: error - }); - reportError(new ErrorEvent("textlint error", { error: textlintError, message: textlintError.message })); + command: "error", + error + }) }); default: console.log("Unknown command: " + data.command); diff --git a/packages/@textlint/script-compiler/src/index.ts b/packages/@textlint/script-compiler/src/index.ts index e9f3511..27edbb6 100644 --- a/packages/@textlint/script-compiler/src/index.ts +++ b/packages/@textlint/script-compiler/src/index.ts @@ -8,6 +8,5 @@ export type { TextlintWorkerCommandResponseFix, TextlintWorkerCommandResponseInit, TextlintWorkerCommandResponseLint, - TextlintErrorOptions, - TextlintError + TextlintWorkerCommandResponseError } from "./CodeGenerator/worker-codegen"; diff --git a/packages/textchecker-element/index.ts b/packages/textchecker-element/index.ts index 204a2f9..b5261aa 100644 --- a/packages/textchecker-element/index.ts +++ b/packages/textchecker-element/index.ts @@ -46,26 +46,18 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if (data.command === "lint:result" && data.id === id) { + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + updateStatus("failed to lint"); + controller.abort(); + } else if (data.command === "lint:result" && data.id === id) { resolve([data.result]); updateStatus("linted"); controller.abort(); } } - function onMessageError(event: MessageEvent) { - reject(event.data); - updateStatus("failed to lint"); - controller.abort(); - } - function onError(event: ErrorEvent) { - debugger; - reject(event); - updateStatus("failed to lint"); - controller.abort(); - } worker.addEventListener("message", onMessage, { signal: controller.signal }); - worker.addEventListener("messageerror", onMessageError, { signal: controller.signal }); - worker.addEventListener("error", onError, { signal: controller.signal }); return worker.postMessage({ id, command: "lint", @@ -87,26 +79,18 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if (data.command === "fix:result" && data.id === id) { + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + updateStatus("failed to fix"); + controller.abort(); + } else if (data.command === "fix:result" && data.id === id) { resolve(data.result); updateStatus("fixed"); controller.abort(); } } - function onMessageError(event: MessageEvent) { - reject(event.data); - updateStatus("failed to fix"); - controller.abort(); - } - function onError(event: ErrorEvent) { - debugger; - reject(event); - updateStatus("failed to fix"); - controller.abort(); - } worker.addEventListener("message", onMessage, { signal: controller.signal }); - worker.addEventListener("messageerror", onMessageError, { signal: controller.signal }); - worker.addEventListener("error", onError, { signal: controller.signal }); return worker.postMessage({ id, command: "fix", diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index 7e1fdf5..d94aeae 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -61,22 +61,16 @@ export const createTextlintWorker = (script: Script) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if (data.command === "lint:result" && data.id === id) { + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + controller.abort(); + } else if (data.command === "lint:result" && data.id === id) { resolve([data.result]); controller.abort(); } } - function onMessageError(event: MessageEvent) { - reject(event.data); - controller.abort(); - } - function onError(event: ErrorEvent) { - reject(event); - controller.abort(); - } workerRef.current.addEventListener("message", onMessage, { signal: controller.signal }); - workerRef.current.addEventListener("messageerror", onMessageError, { signal: controller.signal }); - workerRef.current.addEventListener("error", onError, { signal: controller.signal }); return workerRef.current.postMessage({ id, command: "lint", @@ -102,22 +96,16 @@ export const createTextlintWorker = (script: Script) => { const controller = new AbortController(); function onMessage(event: MessageEvent) { const data = event.data; - if (data.command === "fix:result" && data.id === id) { + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + controller.abort(); + } else if (data.command === "fix:result" && data.id === id) { resolve(data.result); controller.abort(); } } - function onMessageError(event: MessageEvent) { - reject(event.data); - controller.abort(); - } - function onError(event: ErrorEvent) { - reject(event); - controller.abort(); - } workerRef.current.addEventListener("message", onMessage, { signal: controller.signal }); - workerRef.current.addEventListener("messageerror", onMessageError, { signal: controller.signal }); - workerRef.current.addEventListener("error", onError, { signal: controller.signal }); return workerRef.current.postMessage({ id, command: "fix", From 35097e57d1282db6f484f1d9fb3bf18b32226c1a Mon Sep 17 00:00:00 2001 From: otariidae Date: Wed, 21 Jun 2023 09:02:01 +0900 Subject: [PATCH 7/9] refactor: simplify message handler by using promise chain --- packages/textchecker-element/index.ts | 84 ++++++++++++------- .../app/scripts/background/textlint.ts | 66 ++++++++------- 2 files changed, 90 insertions(+), 60 deletions(-) diff --git a/packages/textchecker-element/index.ts b/packages/textchecker-element/index.ts index b5261aa..9346877 100644 --- a/packages/textchecker-element/index.ts +++ b/packages/textchecker-element/index.ts @@ -41,23 +41,22 @@ const generateMessageId = () => crypto.randomUUID(); const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { const lintText: LintEngineAPI["lintText"] = async ({ text }: { text: string }): Promise => { updateStatus("linting..."); - return new Promise((resolve, reject) => { + const controller = new AbortController(); + const lintPromise = new Promise((resolve, reject) => { const id = generateMessageId(); - const controller = new AbortController(); - function onMessage(event: MessageEvent) { - const data = event.data; - // global error or ID-specified error - if (data.command === "error" && (!("id" in data) || data.id === id)) { - reject(data.error); - updateStatus("failed to lint"); - controller.abort(); - } else if (data.command === "lint:result" && data.id === id) { - resolve([data.result]); - updateStatus("linted"); - controller.abort(); - } - } - worker.addEventListener("message", onMessage, { signal: controller.signal }); + worker.addEventListener( + "message", + (event: MessageEvent) => { + const data = event.data; + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + } else if (data.command === "lint:result" && data.id === id) { + resolve([data.result]); + } + }, + { signal: controller.signal } + ); return worker.postMessage({ id, command: "lint", @@ -65,6 +64,17 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { ext: ext } as TextlintWorkerCommandLint); }); + lintPromise + .then(() => { + updateStatus("linted"); + }) + .catch(() => { + updateStatus("failed to lint"); + }) + .finally(() => { + controller.abort(); + }); + return lintPromise; }; const fixText = async ({ text, @@ -74,23 +84,22 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { message?: TextlintMessage; }): Promise => { updateStatus("fixing..."); - return new Promise((resolve, reject) => { + const controller = new AbortController(); + const fixPromise = new Promise((resolve, reject) => { const id = generateMessageId(); - const controller = new AbortController(); - function onMessage(event: MessageEvent) { - const data = event.data; - // global error or ID-specified error - if (data.command === "error" && (!("id" in data) || data.id === id)) { - reject(data.error); - updateStatus("failed to fix"); - controller.abort(); - } else if (data.command === "fix:result" && data.id === id) { - resolve(data.result); - updateStatus("fixed"); - controller.abort(); - } - } - worker.addEventListener("message", onMessage, { signal: controller.signal }); + worker.addEventListener( + "message", + (event: MessageEvent) => { + const data = event.data; + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + } else if (data.command === "fix:result" && data.id === id) { + resolve(data.result); + } + }, + { signal: controller.signal } + ); return worker.postMessage({ id, command: "fix", @@ -99,6 +108,17 @@ const createTextlint = ({ worker, ext }: { worker: Worker; ext: string }) => { ext: ext } as TextlintWorkerCommandFix); }); + fixPromise + .then(() => { + updateStatus("fixed"); + }) + .catch(() => { + updateStatus("failed to fix"); + }) + .finally(() => { + controller.abort(); + }); + return fixPromise; }; return { lintText, diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index d94aeae..ac40d45 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -56,21 +56,22 @@ export const createTextlintWorker = (script: Script) => { const defaultWorker = new Worker(workerUrl); const workerRef = createWorkerRef(defaultWorker); const lintText = async ({ text, ext }: { text: string; ext: string }): Promise => { - return new Promise((resolve, reject) => { + const controller = new AbortController(); + const lintPromise = new Promise((resolve, reject) => { const id = generateMessageId(); - const controller = new AbortController(); - function onMessage(event: MessageEvent) { - const data = event.data; - // global error or ID-specified error - if (data.command === "error" && (!("id" in data) || data.id === id)) { - reject(data.error); - controller.abort(); - } else if (data.command === "lint:result" && data.id === id) { - resolve([data.result]); - controller.abort(); - } - } - workerRef.current.addEventListener("message", onMessage, { signal: controller.signal }); + workerRef.current.addEventListener( + "message", + (event: MessageEvent) => { + const data = event.data; + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + } else if (data.command === "lint:result" && data.id === id) { + resolve([data.result]); + } + }, + { signal: controller.signal } + ); return workerRef.current.postMessage({ id, command: "lint", @@ -78,6 +79,10 @@ export const createTextlintWorker = (script: Script) => { ext } as TextlintWorkerCommandLint); }); + lintPromise.finally(() => { + controller.abort(); + }); + return lintPromise; }; // Note: currently does not use background implementation. // Just use @textlint/source-code-fixer @@ -91,21 +96,22 @@ export const createTextlintWorker = (script: Script) => { ext: string; message?: TextlintMessage; }): Promise => { - return new Promise((resolve, reject) => { + const controller = new AbortController(); + const fixPromise = new Promise((resolve, reject) => { const id = generateMessageId(); - const controller = new AbortController(); - function onMessage(event: MessageEvent) { - const data = event.data; - // global error or ID-specified error - if (data.command === "error" && (!("id" in data) || data.id === id)) { - reject(data.error); - controller.abort(); - } else if (data.command === "fix:result" && data.id === id) { - resolve(data.result); - controller.abort(); - } - } - workerRef.current.addEventListener("message", onMessage, { signal: controller.signal }); + workerRef.current.addEventListener( + "message", + (event: MessageEvent) => { + const data = event.data; + // global error or ID-specified error + if (data.command === "error" && (!("id" in data) || data.id === id)) { + reject(data.error); + } else if (data.command === "fix:result" && data.id === id) { + resolve(data.result); + } + }, + { signal: controller.signal } + ); return workerRef.current.postMessage({ id, command: "fix", @@ -114,6 +120,10 @@ export const createTextlintWorker = (script: Script) => { ext: ext } as TextlintWorkerCommandFix); }); + fixPromise.finally(() => { + controller.abort(); + }); + return fixPromise; }; const mergeConfig = async ({ textlintrc }: { textlintrc: TextlintRcConfig }): Promise => { return new Promise((resolve, _reject) => { From e181e81dd5be628e7134813662f39db5c3892628 Mon Sep 17 00:00:00 2001 From: otariidae Date: Wed, 21 Jun 2023 09:10:03 +0900 Subject: [PATCH 8/9] refactor: handle errors in wrapper functions --- .../src/attach-to-text-area.ts | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/textchecker-element/src/attach-to-text-area.ts b/packages/textchecker-element/src/attach-to-text-area.ts index 8f8a873..2ed725e 100644 --- a/packages/textchecker-element/src/attach-to-text-area.ts +++ b/packages/textchecker-element/src/attach-to-text-area.ts @@ -155,21 +155,15 @@ export const attachToTextArea = ({ // dismiss card before update annotations // dismissCards(); const text = textAreaElement.value; - let results; - try { - results = await lintEngine.lintText({ - text - }); - } catch (e) { - debug("lint error", e); - results = [] as const; - } + const results = await lintEngine.lintText({ + text + }); debug("lint results", results); const updateText = async (newText: string, card: TextCheckerCard) => { const currentText = textAreaElement.value; if (currentText === text && currentText !== newText) { textAreaElement.value = newText; - await update(); + await updateOrClearAnnotationsIfFailed(); textCheckerPopup.dismissCard(card); } }; @@ -234,7 +228,7 @@ export const attachToTextArea = ({ text, message }); - await update(); + await updateOrClearAnnotationsIfFailed(); }, onSeeDocument() { const id = message.ruleId.includes("/") @@ -273,24 +267,32 @@ export const attachToTextArea = ({ debug("annotations", annotations); textChecker.updateAnnotations(annotations); }, lintingDebounceMs); + const updateOrClearAnnotationsIfFailed = async () => { + try { + await update(); + } catch (error) { + debug("update error", error); + textChecker.updateAnnotations([]); + } + }; // Events // when resize element, update annotation const resizeObserver = new ResizeObserver(() => { debug("ResizeObserver do update"); textCheckerPopup.dismissCards(); textChecker.resetAnnotations(); - update(); + updateOrClearAnnotationsIfFailed(); }); resizeObserver.observe(textAreaElement); // when scroll window, update annotation const onScroll = () => { textCheckerPopup.dismissCards(); textChecker.resetAnnotations(); - update(); + updateOrClearAnnotationsIfFailed(); }; const onFocus = () => { textCheckerPopup.dismissCards(); - update(); + updateOrClearAnnotationsIfFailed(); }; const onBlur = (event: FocusEvent) => { // does not dismiss on click popup items(require tabindex) @@ -308,7 +310,7 @@ export const attachToTextArea = ({ window.addEventListener("scroll", onScroll); // when scroll the element, update annotation textAreaElement.addEventListener("scroll", onScroll); - update(); + updateOrClearAnnotationsIfFailed(); return () => { window.removeEventListener("scroll", onScroll); textAreaElement.removeEventListener("scroll", onScroll); From 7ea7580305b38eb42f7d20adb69bdc114d5702ff Mon Sep 17 00:00:00 2001 From: otariidae Date: Thu, 22 Jun 2023 02:04:28 +0900 Subject: [PATCH 9/9] refactor: simplify redundant Promise handling --- .../webextension/app/scripts/background/textlint.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/webextension/app/scripts/background/textlint.ts b/packages/webextension/app/scripts/background/textlint.ts index ac40d45..df9741f 100644 --- a/packages/webextension/app/scripts/background/textlint.ts +++ b/packages/webextension/app/scripts/background/textlint.ts @@ -57,7 +57,7 @@ export const createTextlintWorker = (script: Script) => { const workerRef = createWorkerRef(defaultWorker); const lintText = async ({ text, ext }: { text: string; ext: string }): Promise => { const controller = new AbortController(); - const lintPromise = new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const id = generateMessageId(); workerRef.current.addEventListener( "message", @@ -78,11 +78,9 @@ export const createTextlintWorker = (script: Script) => { text, ext } as TextlintWorkerCommandLint); - }); - lintPromise.finally(() => { + }).finally(() => { controller.abort(); }); - return lintPromise; }; // Note: currently does not use background implementation. // Just use @textlint/source-code-fixer @@ -97,7 +95,7 @@ export const createTextlintWorker = (script: Script) => { message?: TextlintMessage; }): Promise => { const controller = new AbortController(); - const fixPromise = new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const id = generateMessageId(); workerRef.current.addEventListener( "message", @@ -119,11 +117,9 @@ export const createTextlintWorker = (script: Script) => { ruleId: message?.ruleId, ext: ext } as TextlintWorkerCommandFix); - }); - fixPromise.finally(() => { + }).finally(() => { controller.abort(); }); - return fixPromise; }; const mergeConfig = async ({ textlintrc }: { textlintrc: TextlintRcConfig }): Promise => { return new Promise((resolve, _reject) => {