From 25acad5154f46128efc39d2a503960692856abe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Wed, 10 Dec 2025 15:04:34 +0100 Subject: [PATCH] :wrench: Add formatting rules to the TextEditor --- frontend/package.json | 4 +- frontend/playwright/helpers/Clipboard.js | 14 ++--- frontend/playwright/helpers/Transit.js | 10 ++- frontend/playwright/ui/pages/BasePage.js | 2 +- frontend/playwright/ui/pages/WorkspacePage.js | 4 +- .../playwright/ui/specs/render-wasm.spec.js | 2 +- .../ui/specs/text-editor-v2.spec.js | 62 ++++++++++--------- frontend/playwright/ui/specs/tokens.spec.js | 2 +- frontend/text-editor/src/editor/Event.js | 4 +- frontend/text-editor/src/editor/TextEditor.js | 12 +++- .../text-editor/src/editor/clipboard/paste.js | 28 +++++---- .../editor/commands/deleteContentBackward.js | 14 ++--- .../editor/commands/deleteContentForward.js | 15 +++-- .../src/editor/content/Text.test.js | 24 ++++--- .../src/editor/content/dom/Color.js | 2 +- .../src/editor/content/dom/Content.js | 9 +-- .../src/editor/content/dom/Paragraph.test.js | 6 +- .../src/editor/content/dom/Root.test.js | 8 ++- .../src/editor/content/dom/Style.js | 11 ++-- .../src/editor/content/dom/TextNode.js | 6 +- .../editor/content/dom/TextNodeIterator.js | 26 ++++---- .../editor/controllers/ChangeController.js | 2 +- .../src/editor/controllers/SafeGuard.js | 10 +-- .../editor/controllers/SelectionController.js | 22 ++++--- .../controllers/SelectionController.test.js | 58 +++++++++-------- .../editor/controllers/StyleDeclaration.js | 7 ++- .../editor/debug/SelectionControllerDebug.js | 23 ++++--- frontend/text-editor/src/playground/geom.js | 15 +---- frontend/text-editor/src/playground/shape.js | 6 +- frontend/text-editor/src/playground/style.js | 3 +- .../text-editor/src/playground/viewport.js | 6 +- .../text-editor/src/test/TextEditorMock.js | 17 +++-- frontend/text-editor/vite.config.js | 20 +++--- 33 files changed, 243 insertions(+), 211 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index cefd507cbd..86c1ad4d0b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -32,8 +32,8 @@ "e2e:server": "node ./scripts/e2e-server.js", "fmt:clj": "cljfmt fix --parallel=true src/ test/", "fmt:clj:check": "cljfmt check --parallel=false src/ test/", - "fmt:js": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -w", - "fmt:js:check": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js", + "fmt:js": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -c text-editor/**/*.js -w", + "fmt:js:check": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js text-editor/**/*.js", "lint:clj": "clj-kondo --parallel --lint src/", "lint:scss": "yarn run prettier -c resources/styles -c src/**/*.scss", "lint:scss:fix": "yarn run prettier -c resources/styles -c src/**/*.scss -w", diff --git a/frontend/playwright/helpers/Clipboard.js b/frontend/playwright/helpers/Clipboard.js index 046b298632..3a5dda2287 100644 --- a/frontend/playwright/helpers/Clipboard.js +++ b/frontend/playwright/helpers/Clipboard.js @@ -1,12 +1,12 @@ export class Clipboard { static Permission = { - ONLY_READ: ['clipboard-read'], - ONLY_WRITE: ['clipboard-write'], - ALL: ['clipboard-read', 'clipboard-write'] - } + ONLY_READ: ["clipboard-read"], + ONLY_WRITE: ["clipboard-write"], + ALL: ["clipboard-read", "clipboard-write"], + }; static enable(context, permissions) { - return context.grantPermissions(permissions) + return context.grantPermissions(permissions); } static writeText(page, text) { @@ -18,8 +18,8 @@ export class Clipboard { } constructor(page, context) { - this.page = page - this.context = context + this.page = page; + this.context = context; } enable(permissions) { diff --git a/frontend/playwright/helpers/Transit.js b/frontend/playwright/helpers/Transit.js index f56292ed6a..30d692d8b0 100644 --- a/frontend/playwright/helpers/Transit.js +++ b/frontend/playwright/helpers/Transit.js @@ -1,18 +1,16 @@ export class Transit { static parse(value) { - if (typeof value !== 'string') - return value + if (typeof value !== "string") return value; - if (value.startsWith('~')) - return value.slice(2) + if (value.startsWith("~")) return value.slice(2); - return value + return value; } static get(object, ...path) { let aux = object; for (const name of path) { - if (typeof name !== 'string') { + if (typeof name !== "string") { if (!(name in aux)) { return undefined; } diff --git a/frontend/playwright/ui/pages/BasePage.js b/frontend/playwright/ui/pages/BasePage.js index e2d91bdc7e..07bcaaa7a1 100644 --- a/frontend/playwright/ui/pages/BasePage.js +++ b/frontend/playwright/ui/pages/BasePage.js @@ -9,7 +9,7 @@ export class BasePage { */ static async mockRPCs(page, paths, options) { for (const [path, jsonFilename] of Object.entries(paths)) { - await this.mockRPC(page, path, jsonFilename, options) + await this.mockRPC(page, path, jsonFilename, options); } } diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index c91a431325..728f313416 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -1,7 +1,7 @@ import { expect } from "@playwright/test"; -import { readFile } from 'node:fs/promises'; +import { readFile } from "node:fs/promises"; import { BaseWebSocketPage } from "./BaseWebSocketPage"; -import { Transit } from '../../helpers/Transit'; +import { Transit } from "../../helpers/Transit"; export class WorkspacePage extends BaseWebSocketPage { static TextEditor = class TextEditor { diff --git a/frontend/playwright/ui/specs/render-wasm.spec.js b/frontend/playwright/ui/specs/render-wasm.spec.js index 51b6be593b..d8b72d13be 100644 --- a/frontend/playwright/ui/specs/render-wasm.spec.js +++ b/frontend/playwright/ui/specs/render-wasm.spec.js @@ -51,7 +51,7 @@ test.skip("BUG 12164 - Crash when trying to fetch a missing font", async ({ pageId: "2b7f0188-51a1-8193-8006-e05bad87b74d", }); - await workspacePage.page.waitForTimeout(1000) + await workspacePage.page.waitForTimeout(1000); await workspacePage.waitForFirstRender(); await expect( diff --git a/frontend/playwright/ui/specs/text-editor-v2.spec.js b/frontend/playwright/ui/specs/text-editor-v2.spec.js index dfef62049f..cc6061f192 100644 --- a/frontend/playwright/ui/specs/text-editor-v2.spec.js +++ b/frontend/playwright/ui/specs/text-editor-v2.spec.js @@ -1,5 +1,5 @@ import { test, expect } from "@playwright/test"; -import { Clipboard } from '../../helpers/Clipboard'; +import { Clipboard } from "../../helpers/Clipboard"; import { WorkspacePage } from "../pages/WorkspacePage"; const timeToWait = 100; @@ -11,14 +11,14 @@ test.beforeEach(async ({ page, context }) => { await WorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]); }); -test.afterEach(async ({ context}) => { +test.afterEach(async ({ context }) => { context.clearPermissions(); -}) +}); test("Create a new text shape", async ({ page }) => { const initialText = "Lorem ipsum"; const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.goToWorkspace(); @@ -36,10 +36,7 @@ test("Create a new text shape from pasting text", async ({ page, context }) => { textEditor: true, }); await workspace.setupEmptyFile(); - await workspace.mockRPC( - "update-file?id=*", - "text-editor/update-file.json", - ); + await workspace.mockRPC("update-file?id=*", "text-editor/update-file.json"); await workspace.goToWorkspace(); await Clipboard.writeText(page, textToPaste); @@ -55,10 +52,13 @@ test("Create a new text shape from pasting text", async ({ page, context }) => { await workspace.textEditor.stopEditing(); }); -test("Create a new text shape from pasting text using context menu", async ({ page, context }) => { +test("Create a new text shape from pasting text using context menu", async ({ + page, + context, +}) => { const textToPaste = "Lorem ipsum"; const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.goToWorkspace(); @@ -72,11 +72,13 @@ test("Create a new text shape from pasting text using context menu", async ({ pa expect(textContent).toBe(textToPaste); await workspace.textEditor.stopEditing(); -}) +}); -test("Update an already created text shape by appending text", async ({ page }) => { +test("Update an already created text shape by appending text", async ({ + page, +}) => { const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -94,7 +96,7 @@ test("Update an already created text shape by prepending text", async ({ page, }) => { const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -112,7 +114,7 @@ test("Update an already created text shape by inserting text in between", async page, }) => { const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -126,10 +128,13 @@ test("Update an already created text shape by inserting text in between", async await workspace.textEditor.stopEditing(); }); -test("Update a new text shape appending text by pasting text", async ({ page, context }) => { +test("Update a new text shape appending text by pasting text", async ({ + page, + context, +}) => { const textToPaste = " dolor sit amet"; const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -147,11 +152,12 @@ test("Update a new text shape appending text by pasting text", async ({ page, co }); test("Update a new text shape prepending text by pasting text", async ({ - page, context + page, + context, }) => { const textToPaste = "Dolor sit amet "; const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -173,7 +179,7 @@ test("Update a new text shape replacing (starting) text with pasted text", async }) => { const textToPaste = "Dolor sit amet"; const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -197,7 +203,7 @@ test("Update a new text shape replacing (ending) text with pasted text", async ( }) => { const textToPaste = "dolor sit amet"; const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -221,7 +227,7 @@ test("Update a new text shape replacing (in between) text with pasted text", asy }) => { const textToPaste = "dolor sit amet"; const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); @@ -244,14 +250,11 @@ test("Update text font size selecting a part of it (starting)", async ({ page, }) => { const workspace = new WorkspacePage(page, { - textEditor: true + textEditor: true, }); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-lorem-ipsum.json"); - await workspace.mockRPC( - "update-file?id=*", - "text-editor/update-file.json", - ); + await workspace.mockRPC("update-file?id=*", "text-editor/update-file.json"); await workspace.goToWorkspace(); await workspace.clickLeafLayer("Lorem ipsum"); await workspace.textEditor.startEditing(); @@ -280,7 +283,10 @@ test.skip("Update text line height selecting a part of it (starting)", async ({ await workspace.textEditor.selectFromStart(5); await workspace.textEditor.changeLineHeight(1.4); - const lineHeight = await workspace.textEditor.waitForParagraphStyle(1, 'line-height'); + const lineHeight = await workspace.textEditor.waitForParagraphStyle( + 1, + "line-height", + ); expect(lineHeight).toBe("1.4"); const textContent = await workspace.textEditor.waitForTextSpanContent(); diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index e1bbebbacb..0839e92084 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -303,7 +303,7 @@ test.describe("Tokens: Tokens Tab", () => { const nameField = tokensUpdateCreateModal.getByLabel("Name"); await nameField.pressSequentially(".changed"); - await tokensUpdateCreateModal.getByRole("button", {name: "Save"}).click(); + await tokensUpdateCreateModal.getByRole("button", { name: "Save" }).click(); await expect(tokensUpdateCreateModal).not.toBeVisible(); diff --git a/frontend/text-editor/src/editor/Event.js b/frontend/text-editor/src/editor/Event.js index 9751bad734..23fa4b1338 100644 --- a/frontend/text-editor/src/editor/Event.js +++ b/frontend/text-editor/src/editor/Event.js @@ -15,7 +15,7 @@ */ export function addEventListeners(target, object, options) { Object.entries(object).forEach(([type, listener]) => - target.addEventListener(type, listener, options) + target.addEventListener(type, listener, options), ); } @@ -27,6 +27,6 @@ export function addEventListeners(target, object, options) { */ export function removeEventListeners(target, object) { Object.entries(object).forEach(([type, listener]) => - target.removeEventListener(type, listener) + target.removeEventListener(type, listener), ); } diff --git a/frontend/text-editor/src/editor/TextEditor.js b/frontend/text-editor/src/editor/TextEditor.js index 92e9111eff..e8e8ff1ea2 100644 --- a/frontend/text-editor/src/editor/TextEditor.js +++ b/frontend/text-editor/src/editor/TextEditor.js @@ -664,8 +664,16 @@ export class TextEditor extends EventTarget { * @param {boolean} allowHTMLPaste * @returns {Root} */ -export function createRootFromHTML(html, style = undefined, allowHTMLPaste = undefined) { - const fragment = mapContentFragmentFromHTML(html, style || undefined, allowHTMLPaste || undefined); +export function createRootFromHTML( + html, + style = undefined, + allowHTMLPaste = undefined, +) { + const fragment = mapContentFragmentFromHTML( + html, + style || undefined, + allowHTMLPaste || undefined, + ); const root = createRoot([], style); root.replaceChildren(fragment); resetInertElement(); diff --git a/frontend/text-editor/src/editor/clipboard/paste.js b/frontend/text-editor/src/editor/clipboard/paste.js index cc63579062..1eb85e1db8 100644 --- a/frontend/text-editor/src/editor/clipboard/paste.js +++ b/frontend/text-editor/src/editor/clipboard/paste.js @@ -18,7 +18,10 @@ import { TextEditor } from "../TextEditor.js"; * @param {DataTransfer} clipboardData * @returns {DocumentFragment} */ -function getFormattedFragmentFromClipboardData(selectionController, clipboardData) { +function getFormattedFragmentFromClipboardData( + selectionController, + clipboardData, +) { return mapContentFragmentFromHTML( clipboardData.getData("text/html"), selectionController.currentStyle, @@ -79,9 +82,14 @@ export function paste(event, editor, selectionController) { let fragment = null; if (editor?.options?.allowHTMLPaste) { - fragment = getFormattedOrPlainFragmentFromClipboardData(event.clipboardData); + fragment = getFormattedOrPlainFragmentFromClipboardData( + event.clipboardData, + ); } else { - fragment = getPlainFragmentFromClipboardData(selectionController, event.clipboardData); + fragment = getPlainFragmentFromClipboardData( + selectionController, + event.clipboardData, + ); } if (!fragment) { @@ -92,10 +100,9 @@ export function paste(event, editor, selectionController) { if (selectionController.isCollapsed) { const hasOnlyOneParagraph = fragment.children.length === 1; const hasOnlyOneTextSpan = fragment.firstElementChild.children.length === 1; - const forceTextSpan = fragment.firstElementChild.dataset.textSpan === "force"; - if (hasOnlyOneParagraph - && hasOnlyOneTextSpan - && forceTextSpan) { + const forceTextSpan = + fragment.firstElementChild.dataset.textSpan === "force"; + if (hasOnlyOneParagraph && hasOnlyOneTextSpan && forceTextSpan) { selectionController.insertIntoFocus(fragment.textContent); } else { selectionController.insertPaste(fragment); @@ -103,10 +110,9 @@ export function paste(event, editor, selectionController) { } else { const hasOnlyOneParagraph = fragment.children.length === 1; const hasOnlyOneTextSpan = fragment.firstElementChild.children.length === 1; - const forceTextSpan = fragment.firstElementChild.dataset.textSpan === "force"; - if (hasOnlyOneParagraph - && hasOnlyOneTextSpan - && forceTextSpan) { + const forceTextSpan = + fragment.firstElementChild.dataset.textSpan === "force"; + if (hasOnlyOneParagraph && hasOnlyOneTextSpan && forceTextSpan) { selectionController.replaceText(fragment.textContent); } else { selectionController.replaceWithPaste(fragment); diff --git a/frontend/text-editor/src/editor/commands/deleteContentBackward.js b/frontend/text-editor/src/editor/commands/deleteContentBackward.js index cbc112aa68..dfefe5a4aa 100644 --- a/frontend/text-editor/src/editor/commands/deleteContentBackward.js +++ b/frontend/text-editor/src/editor/commands/deleteContentBackward.js @@ -23,7 +23,7 @@ export function deleteContentBackward(event, editor, selectionController) { // If not is collapsed AKA is a selection, then // we removeSelected. if (!selectionController.isCollapsed) { - return selectionController.removeSelected({ direction: 'backward' }); + return selectionController.removeSelected({ direction: "backward" }); } // If we're in a text node and the offset is @@ -32,18 +32,18 @@ export function deleteContentBackward(event, editor, selectionController) { if (selectionController.isTextFocus && selectionController.focusOffset > 0) { return selectionController.removeBackwardText(); - // If we're in a text node but we're at the end of the - // paragraph, we should merge the current paragraph - // with the following paragraph. + // If we're in a text node but we're at the end of the + // paragraph, we should merge the current paragraph + // with the following paragraph. } else if ( selectionController.isTextFocus && selectionController.focusAtStart ) { return selectionController.mergeBackwardParagraph(); - // If we're at an text span or a line break paragraph - // and there's more than one paragraph, then we should - // remove the next paragraph. + // If we're at an text span or a line break paragraph + // and there's more than one paragraph, then we should + // remove the next paragraph. } else if ( selectionController.isTextSpanFocus || selectionController.isLineBreakFocus diff --git a/frontend/text-editor/src/editor/commands/deleteContentForward.js b/frontend/text-editor/src/editor/commands/deleteContentForward.js index 53951bd7f1..0f51188699 100644 --- a/frontend/text-editor/src/editor/commands/deleteContentForward.js +++ b/frontend/text-editor/src/editor/commands/deleteContentForward.js @@ -28,22 +28,21 @@ export function deleteContentForward(event, editor, selectionController) { // If we're in a text node and the offset is // greater than 0 (not at the start of the text span) // we simple remove a character from the text. - if (selectionController.isTextFocus - && selectionController.focusAtEnd) { + if (selectionController.isTextFocus && selectionController.focusAtEnd) { return selectionController.mergeForwardParagraph(); - // If we're in a text node but we're at the end of the - // paragraph, we should merge the current paragraph - // with the following paragraph. + // If we're in a text node but we're at the end of the + // paragraph, we should merge the current paragraph + // with the following paragraph. } else if ( selectionController.isTextFocus && selectionController.focusOffset >= 0 ) { return selectionController.removeForwardText(); - // If we're at a text span or a line break paragraph - // and there's more than one paragraph, then we should - // remove the next paragraph. + // If we're at a text span or a line break paragraph + // and there's more than one paragraph, then we should + // remove the next paragraph. } else if ( (selectionController.isTextSpanFocus || selectionController.isLineBreakFocus) && diff --git a/frontend/text-editor/src/editor/content/Text.test.js b/frontend/text-editor/src/editor/content/Text.test.js index e8c43a1301..45924d655d 100644 --- a/frontend/text-editor/src/editor/content/Text.test.js +++ b/frontend/text-editor/src/editor/content/Text.test.js @@ -1,11 +1,17 @@ -import { describe, test, expect } from 'vitest' -import { insertInto, removeBackward, removeForward, replaceWith } from './Text'; +import { describe, test, expect } from "vitest"; +import { insertInto, removeBackward, removeForward, replaceWith } from "./Text"; describe("Text", () => { test("* should throw when passed wrong parameters", () => { - expect(() => insertInto(Infinity, Infinity, Infinity)).toThrowError('Invalid string'); - expect(() => insertInto('Hello', Infinity, Infinity)).toThrowError('Invalid offset'); - expect(() => insertInto('Hello', 0, Infinity)).toThrowError('Invalid string'); + expect(() => insertInto(Infinity, Infinity, Infinity)).toThrowError( + "Invalid string", + ); + expect(() => insertInto("Hello", Infinity, Infinity)).toThrowError( + "Invalid offset", + ); + expect(() => insertInto("Hello", 0, Infinity)).toThrowError( + "Invalid string", + ); }); test("`insertInto` should insert a string into an offset", () => { @@ -13,7 +19,9 @@ describe("Text", () => { }); test("`replaceWith` should replace a string into a string", () => { - expect(replaceWith("Hello, Something!", 7, 16, "World")).toBe("Hello, World!"); + expect(replaceWith("Hello, Something!", 7, 16, "World")).toBe( + "Hello, World!", + ); }); test("`removeBackward` should remove string backward from start (offset 0)", () => { @@ -26,13 +34,13 @@ describe("Text", () => { test("`removeBackward` should remove string backward from end", () => { expect(removeBackward("Hello, World!", "Hello, World!".length)).toBe( - "Hello, World" + "Hello, World", ); }); test("`removeForward` should remove string forward from end", () => { expect(removeForward("Hello, World!", "Hello, World!".length)).toBe( - "Hello, World!" + "Hello, World!", ); }); diff --git a/frontend/text-editor/src/editor/content/dom/Color.js b/frontend/text-editor/src/editor/content/dom/Color.js index 01a9e23bb7..ba798dd67a 100644 --- a/frontend/text-editor/src/editor/content/dom/Color.js +++ b/frontend/text-editor/src/editor/content/dom/Color.js @@ -24,7 +24,7 @@ function getContext() { if (!context) { context = canvas.getContext("2d"); } - return context + return context; } /** diff --git a/frontend/text-editor/src/editor/content/dom/Content.js b/frontend/text-editor/src/editor/content/dom/Content.js index 357c7fbe42..62181d11c4 100644 --- a/frontend/text-editor/src/editor/content/dom/Content.js +++ b/frontend/text-editor/src/editor/content/dom/Content.js @@ -230,15 +230,10 @@ export function mapContentFragmentFromString(string, styleDefaults) { const fragment = document.createDocumentFragment(); for (const line of lines) { if (line === "") { - fragment.appendChild( - createEmptyParagraph(styleDefaults) - ); + fragment.appendChild(createEmptyParagraph(styleDefaults)); } else { const textSpan = createTextSpan(new Text(line), styleDefaults); - const paragraph = createParagraph( - [textSpan], - styleDefaults, - ); + const paragraph = createParagraph([textSpan], styleDefaults); if (lines.length === 1) { paragraph.dataset.textSpan = "force"; } diff --git a/frontend/text-editor/src/editor/content/dom/Paragraph.test.js b/frontend/text-editor/src/editor/content/dom/Paragraph.test.js index 28c79612af..57e5fb7f54 100644 --- a/frontend/text-editor/src/editor/content/dom/Paragraph.test.js +++ b/frontend/text-editor/src/editor/content/dom/Paragraph.test.js @@ -112,7 +112,11 @@ describe("Paragraph", () => { const helloTextSpan = createTextSpan(new Text("Hello, ")); const worldTextSpan = createTextSpan(new Text("World")); const exclTextSpan = createTextSpan(new Text("!")); - const paragraph = createParagraph([helloTextSpan, worldTextSpan, exclTextSpan]); + const paragraph = createParagraph([ + helloTextSpan, + worldTextSpan, + exclTextSpan, + ]); const newParagraph = splitParagraphAtNode(paragraph, 1); expect(newParagraph).toBeInstanceOf(HTMLDivElement); expect(newParagraph.nodeName).toBe(TAG); diff --git a/frontend/text-editor/src/editor/content/dom/Root.test.js b/frontend/text-editor/src/editor/content/dom/Root.test.js index 49b5195d59..31f3d100c8 100644 --- a/frontend/text-editor/src/editor/content/dom/Root.test.js +++ b/frontend/text-editor/src/editor/content/dom/Root.test.js @@ -1,5 +1,11 @@ import { describe, test, expect } from "vitest"; -import { createEmptyRoot, createRoot, setRootStyles, TAG, TYPE } from "./Root.js"; +import { + createEmptyRoot, + createRoot, + setRootStyles, + TAG, + TYPE, +} from "./Root.js"; /* @vitest-environment jsdom */ describe("Root", () => { diff --git a/frontend/text-editor/src/editor/content/dom/Style.js b/frontend/text-editor/src/editor/content/dom/Style.js index cc11320495..9868572d09 100644 --- a/frontend/text-editor/src/editor/content/dom/Style.js +++ b/frontend/text-editor/src/editor/content/dom/Style.js @@ -6,7 +6,7 @@ * Copyright (c) KALEIDOS INC */ -import StyleDeclaration from '../../controllers/StyleDeclaration.js'; +import StyleDeclaration from "../../controllers/StyleDeclaration.js"; import { getFills } from "./Color.js"; const DEFAULT_FONT_SIZE = "16px"; @@ -339,8 +339,7 @@ export function setStylesFromObject(element, allowedStyles, styleObject) { continue; } let styleValue = styleObject[styleName]; - if (!styleValue) - continue; + if (!styleValue) continue; if (styleName === "font-family") { styleValue = sanitizeFontFamily(styleValue); @@ -388,8 +387,10 @@ export function setStylesFromDeclaration( * @returns {HTMLElement} */ export function setStyles(element, allowedStyles, styleObjectOrDeclaration) { - if (styleObjectOrDeclaration instanceof CSSStyleDeclaration - || styleObjectOrDeclaration instanceof StyleDeclaration) { + if ( + styleObjectOrDeclaration instanceof CSSStyleDeclaration || + styleObjectOrDeclaration instanceof StyleDeclaration + ) { return setStylesFromDeclaration( element, allowedStyles, diff --git a/frontend/text-editor/src/editor/content/dom/TextNode.js b/frontend/text-editor/src/editor/content/dom/TextNode.js index 5aaa8afd6c..051bf054c2 100644 --- a/frontend/text-editor/src/editor/content/dom/TextNode.js +++ b/frontend/text-editor/src/editor/content/dom/TextNode.js @@ -22,8 +22,7 @@ import { isRoot } from "./Root.js"; */ export function isTextNode(node) { if (!node) throw new TypeError("Invalid text node"); - return node.nodeType === Node.TEXT_NODE - || isLineBreak(node); + return node.nodeType === Node.TEXT_NODE || isLineBreak(node); } /** @@ -33,8 +32,7 @@ export function isTextNode(node) { * @returns {boolean} */ export function isEmptyTextNode(node) { - return node.nodeType === Node.TEXT_NODE - && node.nodeValue === ""; + return node.nodeType === Node.TEXT_NODE && node.nodeValue === ""; } /** diff --git a/frontend/text-editor/src/editor/content/dom/TextNodeIterator.js b/frontend/text-editor/src/editor/content/dom/TextNodeIterator.js index 08357d0dd0..ef347efee9 100644 --- a/frontend/text-editor/src/editor/content/dom/TextNodeIterator.js +++ b/frontend/text-editor/src/editor/content/dom/TextNodeIterator.js @@ -6,7 +6,7 @@ * Copyright (c) KALEIDOS INC */ -import SafeGuard from '../../controllers/SafeGuard.js'; +import SafeGuard from "../../controllers/SafeGuard.js"; /** * Iterator direction. @@ -58,7 +58,7 @@ export class TextNodeIterator { startNode, rootNode, skipNodes = new Set(), - direction = TextNodeIteratorDirection.FORWARD + direction = TextNodeIteratorDirection.FORWARD, ) { if (startNode === rootNode) { return TextNodeIterator.findDown( @@ -67,7 +67,7 @@ export class TextNodeIterator { : startNode.lastChild, rootNode, skipNodes, - direction + direction, ); } @@ -95,7 +95,7 @@ export class TextNodeIterator { : currentNode.lastChild, rootNode, skipNodes, - direction + direction, ); } currentNode = @@ -119,7 +119,7 @@ export class TextNodeIterator { startNode, rootNode, backTrack = new Set(), - direction = TextNodeIteratorDirection.FORWARD + direction = TextNodeIteratorDirection.FORWARD, ) { backTrack.add(startNode); if (TextNodeIterator.isTextNode(startNode)) { @@ -127,14 +127,14 @@ export class TextNodeIterator { startNode.parentNode, rootNode, backTrack, - direction + direction, ); } else if (TextNodeIterator.isContainerNode(startNode)) { const found = TextNodeIterator.findDown( startNode, rootNode, backTrack, - direction + direction, ); if (found) { return found; @@ -144,7 +144,7 @@ export class TextNodeIterator { startNode.parentNode, rootNode, backTrack, - direction + direction, ); } } @@ -214,7 +214,7 @@ export class TextNodeIterator { this.#currentNode, this.#rootNode, new Set(), - TextNodeIteratorDirection.FORWARD + TextNodeIteratorDirection.FORWARD, ); if (!nextNode) { @@ -237,7 +237,7 @@ export class TextNodeIterator { this.#currentNode, this.#rootNode, new Set(), - TextNodeIteratorDirection.BACKWARD + TextNodeIteratorDirection.BACKWARD, ); if (!previousNode) { @@ -270,10 +270,8 @@ export class TextNodeIterator { * @param {TextNode} endNode * @yields {TextNode} */ - * iterateFrom(startNode, endNode) { - const comparedPosition = startNode.compareDocumentPosition( - endNode - ); + *iterateFrom(startNode, endNode) { + const comparedPosition = startNode.compareDocumentPosition(endNode); this.#currentNode = startNode; SafeGuard.start(); while (this.#currentNode !== endNode) { diff --git a/frontend/text-editor/src/editor/controllers/ChangeController.js b/frontend/text-editor/src/editor/controllers/ChangeController.js index 8ca9ef571a..166f89a598 100644 --- a/frontend/text-editor/src/editor/controllers/ChangeController.js +++ b/frontend/text-editor/src/editor/controllers/ChangeController.js @@ -38,7 +38,7 @@ export class ChangeController extends EventTarget { * @param {number} [time=500] */ constructor(time = 500) { - super() + super(); if (typeof time === "number" && (!Number.isInteger(time) || time <= 0)) { throw new TypeError("Invalid time"); } diff --git a/frontend/text-editor/src/editor/controllers/SafeGuard.js b/frontend/text-editor/src/editor/controllers/SafeGuard.js index c19f05eb41..c288b8aab7 100644 --- a/frontend/text-editor/src/editor/controllers/SafeGuard.js +++ b/frontend/text-editor/src/editor/controllers/SafeGuard.js @@ -24,19 +24,19 @@ export function start() { */ export function update() { if (Date.now - startTime >= SAFE_GUARD_TIME) { - throw new Error('Safe guard timeout'); + throw new Error("Safe guard timeout"); } } -let timeoutId = 0 +let timeoutId = 0; export function throwAfter(error, timeout = SAFE_GUARD_TIME) { timeoutId = setTimeout(() => { - throw error - }, timeout) + throw error; + }, timeout); } export function throwCancel() { - clearTimeout(timeoutId) + clearTimeout(timeoutId); } export default { diff --git a/frontend/text-editor/src/editor/controllers/SelectionController.js b/frontend/text-editor/src/editor/controllers/SelectionController.js index edfe69c03e..4c3af94dbc 100644 --- a/frontend/text-editor/src/editor/controllers/SelectionController.js +++ b/frontend/text-editor/src/editor/controllers/SelectionController.js @@ -54,7 +54,7 @@ import { isRoot, setRootStyles } from "../content/dom/Root.js"; import { SelectionDirection } from "./SelectionDirection.js"; import SafeGuard from "./SafeGuard.js"; import { sanitizeFontFamily } from "../content/dom/Style.js"; -import StyleDeclaration from './StyleDeclaration.js'; +import StyleDeclaration from "./StyleDeclaration.js"; /** * Supported options for the SelectionController. @@ -280,11 +280,17 @@ export class SelectionController extends EventTarget { // FIXME: I don't like this approximation. Having to iterate nodes twice // is bad for performance. I think we need another way of "computing" // the cascade. - for (const textNode of this.#textNodeIterator.iterateFrom(startNode, endNode)) { + for (const textNode of this.#textNodeIterator.iterateFrom( + startNode, + endNode, + )) { const paragraph = textNode.parentElement.parentElement; this.#applyStylesFromElementToCurrentStyle(paragraph); } - for (const textNode of this.#textNodeIterator.iterateFrom(startNode, endNode)) { + for (const textNode of this.#textNodeIterator.iterateFrom( + startNode, + endNode, + )) { const textSpan = textNode.parentElement; this.#mergeStylesFromElementToCurrentStyle(textSpan); } @@ -1132,10 +1138,7 @@ export class SelectionController extends EventTarget { const hasOnlyOneParagraph = fragment.children.length === 1; const forceTextSpan = fragment.firstElementChild?.dataset?.textSpan === "force"; - if ( - hasOnlyOneParagraph && - forceTextSpan - ) { + if (hasOnlyOneParagraph && forceTextSpan) { // first text span const collapseNode = fragment.firstElementChild.firstElementChild; if (this.isTextSpanStart) { @@ -1403,7 +1406,7 @@ export class SelectionController extends EventTarget { // the focus node is a . if (isTextSpan(this.focusNode)) { this.focusNode.firstElementChild.replaceWith(textNode); - // the focus node is a
. + // the focus node is a
. } else { this.focusNode.replaceWith(textNode); } @@ -1981,8 +1984,7 @@ export class SelectionController extends EventTarget { this.setSelection(newTextSpan.firstChild, 0, newTextSpan.firstChild, 0); } // The styles are applied to the paragraph - else - { + else { const paragraph = this.startParagraph; setParagraphStyles(paragraph, newStyles); // Apply styles to child text spans. diff --git a/frontend/text-editor/src/editor/controllers/SelectionController.test.js b/frontend/text-editor/src/editor/controllers/SelectionController.test.js index 726acf2e4c..0885223ad5 100644 --- a/frontend/text-editor/src/editor/controllers/SelectionController.test.js +++ b/frontend/text-editor/src/editor/controllers/SelectionController.test.js @@ -278,9 +278,9 @@ describe("SelectionController", () => { expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe( "Hello", ); - expect( - textEditorMock.root.lastChild.firstChild.firstChild.nodeValue, - ).toBe(", World!"); + expect(textEditorMock.root.lastChild.firstChild.firstChild.nodeValue).toBe( + ", World!", + ); }); test("`insertPaste` should insert a paragraph from a pasted fragment (at middle)", () => { @@ -292,7 +292,12 @@ describe("SelectionController", () => { textEditorMock, selection, ); - focus(selection, textEditorMock, root.firstChild.firstChild.firstChild, "Lorem ".length); + focus( + selection, + textEditorMock, + root.firstChild.firstChild.firstChild, + "Lorem ".length, + ); const paragraph = createParagraph([createTextSpan(new Text("ipsum "))]); const fragment = document.createDocumentFragment(); fragment.append(paragraph); @@ -315,9 +320,9 @@ describe("SelectionController", () => { expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe( "Lorem ", ); - expect(textEditorMock.root.children.item(1).firstChild.firstChild.nodeValue).toBe( - "ipsum ", - ); + expect( + textEditorMock.root.children.item(1).firstChild.firstChild.nodeValue, + ).toBe("ipsum "); expect(textEditorMock.root.lastChild.firstChild.firstChild.nodeValue).toBe( "dolor", ); @@ -359,25 +364,21 @@ describe("SelectionController", () => { expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe( "Hello", ); - expect( - textEditorMock.root.lastChild.firstChild.firstChild.nodeValue, - ).toBe(", World!"); + expect(textEditorMock.root.lastChild.firstChild.firstChild.nodeValue).toBe( + ", World!", + ); }); test("`insertPaste` should insert a text span from a pasted fragment (at start)", () => { - const textEditorMock = TextEditorMock.createTextEditorMockWithText(", World!"); + const textEditorMock = + TextEditorMock.createTextEditorMockWithText(", World!"); const root = textEditorMock.root; const selection = document.getSelection(); const selectionController = new SelectionController( textEditorMock, selection, ); - focus( - selection, - textEditorMock, - root.firstChild.firstChild.firstChild, - 0, - ); + focus(selection, textEditorMock, root.firstChild.firstChild.firstChild, 0); const paragraph = createParagraph([createTextSpan(new Text("Hello"))]); paragraph.dataset.textSpan = "force"; const fragment = document.createDocumentFragment(); @@ -415,7 +416,12 @@ describe("SelectionController", () => { textEditorMock, selection, ); - focus(selection, textEditorMock, root.firstChild.firstChild.firstChild, "Lorem ".length); + focus( + selection, + textEditorMock, + root.firstChild.firstChild.firstChild, + "Lorem ".length, + ); const paragraph = createParagraph([createTextSpan(new Text("ipsum "))]); paragraph.dataset.textSpan = "force"; const fragment = document.createDocumentFragment(); @@ -439,9 +445,9 @@ describe("SelectionController", () => { expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe( "Lorem ", ); - expect(textEditorMock.root.firstChild.children.item(1).firstChild.nodeValue).toBe( - "ipsum ", - ); + expect( + textEditorMock.root.firstChild.children.item(1).firstChild.nodeValue, + ).toBe("ipsum "); expect( textEditorMock.root.firstChild.children.item(2).firstChild.nodeValue, ).toBe("dolor"); @@ -461,9 +467,7 @@ describe("SelectionController", () => { root.firstChild.firstChild.firstChild, "Hello".length, ); - const paragraph = createParagraph([ - createTextSpan(new Text(", World!")) - ]); + const paragraph = createParagraph([createTextSpan(new Text(", World!"))]); paragraph.dataset.textSpan = "force"; const fragment = document.createDocumentFragment(); fragment.append(paragraph); @@ -486,9 +490,9 @@ describe("SelectionController", () => { expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe( "Hello", ); - expect(textEditorMock.root.firstChild.children.item(1).firstChild.nodeValue).toBe( - ", World!", - ); + expect( + textEditorMock.root.firstChild.children.item(1).firstChild.nodeValue, + ).toBe(", World!"); }); test("`removeBackwardText` should remove text in backward direction (backspace)", () => { diff --git a/frontend/text-editor/src/editor/controllers/StyleDeclaration.js b/frontend/text-editor/src/editor/controllers/StyleDeclaration.js index fcca9e39b4..09a4ce9699 100644 --- a/frontend/text-editor/src/editor/controllers/StyleDeclaration.js +++ b/frontend/text-editor/src/editor/controllers/StyleDeclaration.js @@ -77,7 +77,10 @@ export class StyleDeclaration { const currentValue = this.getPropertyValue(name); if (this.#isQuotedValue(currentValue, value)) { return this.setProperty(name, value); - } else if (currentValue === "" && value === StyleDeclaration.Property.NULL) { + } else if ( + currentValue === "" && + value === StyleDeclaration.Property.NULL + ) { return this.setProperty(name, value); } else if (currentValue === "" && ["initial", "none"].includes(value)) { return this.setProperty(name, value); @@ -107,4 +110,4 @@ export class StyleDeclaration { } } -export default StyleDeclaration +export default StyleDeclaration; diff --git a/frontend/text-editor/src/editor/debug/SelectionControllerDebug.js b/frontend/text-editor/src/editor/debug/SelectionControllerDebug.js index e588497a4d..cd8eef9665 100644 --- a/frontend/text-editor/src/editor/debug/SelectionControllerDebug.js +++ b/frontend/text-editor/src/editor/debug/SelectionControllerDebug.js @@ -43,33 +43,38 @@ export class SelectionControllerDebug { this.#elements.isParagraphStart.checked = selectionController.isParagraphStart; this.#elements.isParagraphEnd.checked = selectionController.isParagraphEnd; - this.#elements.isTextSpanStart.checked = selectionController.isTextSpanStart; + this.#elements.isTextSpanStart.checked = + selectionController.isTextSpanStart; this.#elements.isTextSpanEnd.checked = selectionController.isTextSpanEnd; this.#elements.isTextAnchor.checked = selectionController.isTextAnchor; this.#elements.isTextFocus.checked = selectionController.isTextFocus; this.#elements.focusNode.value = this.getNodeDescription( selectionController.focusNode, - selectionController.focusOffset + selectionController.focusOffset, ); this.#elements.focusOffset.value = selectionController.focusOffset; this.#elements.anchorNode.value = this.getNodeDescription( selectionController.anchorNode, - selectionController.anchorOffset + selectionController.anchorOffset, ); this.#elements.anchorOffset.value = selectionController.anchorOffset; this.#elements.focusTextSpan.value = this.getNodeDescription( - selectionController.focusTextSpan + selectionController.focusTextSpan, ); this.#elements.anchorTextSpan.value = this.getNodeDescription( - selectionController.anchorTextSpan + selectionController.anchorTextSpan, ); this.#elements.focusParagraph.value = this.getNodeDescription( - selectionController.focusParagraph + selectionController.focusParagraph, ); this.#elements.anchorParagraph.value = this.getNodeDescription( - selectionController.anchorParagraph + selectionController.anchorParagraph, + ); + this.#elements.startContainer.value = this.getNodeDescription( + selectionController.startContainer, + ); + this.#elements.endContainer.value = this.getNodeDescription( + selectionController.endContainer, ); - this.#elements.startContainer.value = this.getNodeDescription(selectionController.startContainer); - this.#elements.endContainer.value = this.getNodeDescription(selectionController.endContainer); } } diff --git a/frontend/text-editor/src/playground/geom.js b/frontend/text-editor/src/playground/geom.js index 4f9962d2ab..2d065eee62 100644 --- a/frontend/text-editor/src/playground/geom.js +++ b/frontend/text-editor/src/playground/geom.js @@ -39,10 +39,7 @@ export class Point { } polar(angle, length = 1.0) { - return this.set( - Math.cos(angle) * length, - Math.sin(angle) * length - ); + return this.set(Math.cos(angle) * length, Math.sin(angle) * length); } add({ x, y }) { @@ -119,10 +116,7 @@ export class Point { export class Rect { static create(x, y, width, height) { - return new Rect( - new Point(width, height), - new Point(x, y), - ); + return new Rect(new Point(width, height), new Point(x, y)); } #size; @@ -228,10 +222,7 @@ export class Rect { } clone() { - return new Rect( - this.#size.clone(), - this.#position.clone(), - ); + return new Rect(this.#size.clone(), this.#position.clone()); } toFixed(fractionDigits = 0) { diff --git a/frontend/text-editor/src/playground/shape.js b/frontend/text-editor/src/playground/shape.js index 2f462202dc..e54579a974 100644 --- a/frontend/text-editor/src/playground/shape.js +++ b/frontend/text-editor/src/playground/shape.js @@ -82,13 +82,13 @@ export class Shape { } get rotation() { - return this.#rotation + return this.#rotation; } set rotation(newRotation) { if (!Number.isFinite(newRotation)) { - throw new TypeError('Invalid rotation') + throw new TypeError("Invalid rotation"); } - this.#rotation = newRotation + this.#rotation = newRotation; } } diff --git a/frontend/text-editor/src/playground/style.js b/frontend/text-editor/src/playground/style.js index aeef4a236c..bb52b93d9d 100644 --- a/frontend/text-editor/src/playground/style.js +++ b/frontend/text-editor/src/playground/style.js @@ -6,8 +6,7 @@ export function fromStyle(style) { const entry = Object.entries(this).find(([name, value]) => name === fromStyleValue(style) ? value : 0, ); - if (!entry) - return; + if (!entry) return; const [name] = entry; return name; diff --git a/frontend/text-editor/src/playground/viewport.js b/frontend/text-editor/src/playground/viewport.js index 87d0d34462..a6e3980573 100644 --- a/frontend/text-editor/src/playground/viewport.js +++ b/frontend/text-editor/src/playground/viewport.js @@ -1,4 +1,4 @@ -import { Point } from './geom'; +import { Point } from "./geom"; export class Viewport { #zoom; @@ -38,7 +38,7 @@ export class Viewport { } pan(dx, dy) { - this.#position.x += dx / this.#zoom - this.#position.y += dy / this.#zoom + this.#position.x += dx / this.#zoom; + this.#position.y += dy / this.#zoom; } } diff --git a/frontend/text-editor/src/test/TextEditorMock.js b/frontend/text-editor/src/test/TextEditorMock.js index 2fe620d3a3..2ce0ae4c06 100644 --- a/frontend/text-editor/src/test/TextEditorMock.js +++ b/frontend/text-editor/src/test/TextEditorMock.js @@ -1,6 +1,9 @@ import { createRoot } from "../editor/content/dom/Root.js"; import { createParagraph } from "../editor/content/dom/Paragraph.js"; -import { createEmptyTextSpan, createTextSpan } from "../editor/content/dom/TextSpan.js"; +import { + createEmptyTextSpan, + createTextSpan, +} from "../editor/content/dom/TextSpan.js"; import { createLineBreak } from "../editor/content/dom/LineBreak.js"; export class TextEditorMock extends EventTarget { @@ -38,14 +41,14 @@ export class TextEditorMock extends EventTarget { static createTextEditorMockWithRoot(root) { const container = TextEditorMock.getTemplate(); const selectionImposterElement = container.querySelector( - ".text-editor-selection-imposter" + ".text-editor-selection-imposter", ); const textEditorMock = new TextEditorMock( container.querySelector(".text-editor-content"), { root, selectionImposterElement, - } + }, ); return textEditorMock; } @@ -86,8 +89,8 @@ export class TextEditorMock extends EventTarget { return this.createTextEditorMockWithParagraphs([ createParagraph([ text.length === 0 - ? createEmptyTextSpan() - : createTextSpan(new Text(text)) + ? createEmptyTextSpan() + : createTextSpan(new Text(text)), ]), ]); } @@ -100,7 +103,9 @@ export class TextEditorMock extends EventTarget { * @returns */ static createTextEditorMockWithParagraph(textSpans) { - return this.createTextEditorMockWithParagraphs([createParagraph(textSpans)]); + return this.createTextEditorMockWithParagraphs([ + createParagraph(textSpans), + ]); } #element = null; diff --git a/frontend/text-editor/vite.config.js b/frontend/text-editor/vite.config.js index 34d8e7cdfc..bce37c7715 100644 --- a/frontend/text-editor/vite.config.js +++ b/frontend/text-editor/vite.config.js @@ -1,30 +1,28 @@ import path from "node:path"; -import fs from 'node:fs/promises'; +import fs from "node:fs/promises"; import { defineConfig } from "vite"; import { coverageConfigDefaults } from "vitest/config"; async function waitFor(timeInMillis) { - return new Promise(resolve => - setTimeout(_ => resolve(), timeInMillis) - ); + return new Promise((resolve) => setTimeout((_) => resolve(), timeInMillis)); } const wasmWatcherPlugin = (options = {}) => { return { name: "vite-wasm-watcher-plugin", configureServer(server) { - server.watcher.add("../resources/public/js/render_wasm.wasm") - server.watcher.add("../resources/public/js/render_wasm.js") + server.watcher.add("../resources/public/js/render_wasm.wasm"); + server.watcher.add("../resources/public/js/render_wasm.js"); server.watcher.on("change", async (file) => { if (file.includes("../resources/")) { // If we copy the files immediately, we end // up with an empty .js file (I don't know why). - await waitFor(100) + await waitFor(100); // copy files. await fs.copyFile( path.resolve(file), - path.resolve('./src/wasm/', path.basename(file)) - ) + path.resolve("./src/wasm/", path.basename(file)), + ); console.log(`${file} changed`); } }); @@ -49,9 +47,7 @@ const wasmWatcherPlugin = (options = {}) => { }; export default defineConfig({ - plugins: [ - wasmWatcherPlugin() - ], + plugins: [wasmWatcherPlugin()], root: "./src", resolve: { alias: {