From 0621b7d482e233a6466b8c9262a49be9b3415291 Mon Sep 17 00:00:00 2001 From: Tomas Maritano Date: Sun, 16 Aug 2026 19:42:34 -0300 Subject: [PATCH] fix(desktop): stop packaged editor crash electron-vite never loaded electron-vite.config.ts (it looks for electron.vite.config.ts). Production also bundled two @lezer/common copies, so HighlightStyle.style saw undefined tags. Pin common@1.5.2, emit one CodeMirror chunk, and open a note in smoke so the lazy editor actually loads. --- apps/desktop/e2e/smoke.spec.ts | 28 ++++++++++ apps/desktop/electron-vite.config.ts | 79 ---------------------------- apps/desktop/electron.vite.config.ts | 63 ++++++++++++++++++++++ apps/desktop/package.json | 1 + knip.json | 2 +- package.json | 6 ++- pnpm-lock.yaml | 15 +++--- 7 files changed, 106 insertions(+), 88 deletions(-) delete mode 100644 apps/desktop/electron-vite.config.ts create mode 100644 apps/desktop/electron.vite.config.ts diff --git a/apps/desktop/e2e/smoke.spec.ts b/apps/desktop/e2e/smoke.spec.ts index 2ca924ce..f56b0429 100644 --- a/apps/desktop/e2e/smoke.spec.ts +++ b/apps/desktop/e2e/smoke.spec.ts @@ -55,4 +55,32 @@ test.describe('app launch (smoke)', () => { await cleanup(); } }); + + // Regression for #311: optimizeDeps only covers the dev server. The + // production rollup/rolldown build used to split @codemirror/@lezer + // across lazy chunks, so HighlightStyle.define() saw `tags` as + // undefined and the packaged editor logged "[CodeMirror] plugin error: + // TypeError: tags is not iterable". This test must load MarkdownEditor + // (lazy from NoteEditor) against the built bundle. + test('opening a note loads CodeMirror without plugin errors', async () => { + const { window, cleanup } = await launchApp(); + const consoleErrors: string[] = []; + window.on('console', msg => { + if (msg.type() === 'error') consoleErrors.push(msg.text()); + }); + window.on('pageerror', err => consoleErrors.push(`pageerror: ${err.message}`)); + + try { + await window.getByRole('button', { name: 'Create Your First Note' }).click(); + + const content = window.locator('.cm-content'); + await expect(content).toBeVisible({ timeout: 15_000 }); + await expect(content).toContainText('Untitled'); + + const codeMirrorErrors = consoleErrors.filter(line => /\[CodeMirror\]/.test(line)); + expect(codeMirrorErrors, codeMirrorErrors.join('\n')).toEqual([]); + } finally { + await cleanup(); + } + }); }); diff --git a/apps/desktop/electron-vite.config.ts b/apps/desktop/electron-vite.config.ts deleted file mode 100644 index 06121cd5..00000000 --- a/apps/desktop/electron-vite.config.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { resolve } from 'path'; -import { defineConfig, externalizeDepsPlugin } from 'electron-vite'; -import react from '@vitejs/plugin-react'; - -export default defineConfig({ - main: { - plugins: [ - externalizeDepsPlugin({ - exclude: [ - '@dripnex/core', - '@dripnex/storage-core', - '@dripnex/storage-sqlite', - '@dripnex/sync-core', - '@dripnex/licensing', - '@dripnex/ai-core', - ], - }), - ], - build: { - outDir: 'out/main', - rollupOptions: { - input: { - index: resolve(__dirname, 'src/main/index.ts'), - }, - output: { - format: 'cjs', - }, - }, - }, - }, - preload: { - plugins: [ - externalizeDepsPlugin({ - exclude: ['@dripnex/core', '@dripnex/storage-core', '@dripnex/licensing'], - }), - ], - build: { - outDir: 'out/preload', - rollupOptions: { - input: { - index: resolve(__dirname, 'src/preload/index.ts'), - }, - output: { - format: 'cjs', - }, - }, - }, - }, - renderer: { - plugins: [react()], - // Pre-bundle all CodeMirror packages together to avoid multiple instances of @codemirror/state - // See: https://codemirror.net/docs/guide/#bundling - optimizeDeps: { - include: [ - '@codemirror/state', - '@codemirror/view', - '@codemirror/autocomplete', - '@codemirror/commands', - '@codemirror/language', - '@codemirror/lang-markdown', - '@codemirror/language-data', - '@lezer/highlight', - ], - }, - build: { - outDir: 'out/renderer', - rollupOptions: { - input: { - index: resolve(__dirname, 'src/renderer/index.html'), - }, - }, - }, - resolve: { - alias: { - '@': resolve(__dirname, 'src/renderer'), - }, - }, - }, -}); diff --git a/apps/desktop/electron.vite.config.ts b/apps/desktop/electron.vite.config.ts new file mode 100644 index 00000000..ce1decd6 --- /dev/null +++ b/apps/desktop/electron.vite.config.ts @@ -0,0 +1,63 @@ +import { resolve } from 'path'; +import { defineConfig } from 'electron-vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + main: {}, + preload: {}, + renderer: { + plugins: [react()], + // Pre-bundle all CodeMirror packages together to avoid multiple instances of @codemirror/state + // See: https://codemirror.net/docs/guide/#bundling + // optimizeDeps only covers the dev server. Production also has to keep + // @codemirror/* and @lezer/* in one chunk — HighlightStyle.define() in + // editorTheme.ts reads tags at module init, and a split build leaves + // them undefined ("tags is not iterable"). + optimizeDeps: { + include: [ + '@codemirror/state', + '@codemirror/view', + '@codemirror/autocomplete', + '@codemirror/commands', + '@codemirror/language', + '@codemirror/lang-markdown', + '@codemirror/language-data', + '@lezer/highlight', + ], + }, + build: { + outDir: 'out/renderer', + rollupOptions: { + input: { + index: resolve(__dirname, 'src/renderer/index.html'), + }, + output: { + // Function form is the #311 acceptance criterion. Keep + // @codemirror/* and @lezer/* in one chunk so HighlightStyle + // and tags initialize together. + manualChunks(id: string) { + if (id.includes('@codemirror/') || id.includes('@lezer/')) { + return 'codemirror'; + } + }, + }, + }, + }, + resolve: { + alias: { + '@': resolve(__dirname, 'src/renderer'), + // highlight@1.2.3 nests common@1.5.0; language/markdown nest 1.5.2. + // Two NodeProp identities → HighlightStyle.style(undefined) → + // "tags is not iterable". Pin every import to the desktop copy. + '@lezer/common': resolve(__dirname, 'node_modules/@lezer/common'), + }, + dedupe: [ + '@codemirror/state', + '@codemirror/view', + '@codemirror/language', + '@lezer/highlight', + '@lezer/common', + ], + }, + }, +}); diff --git a/apps/desktop/package.json b/apps/desktop/package.json index be2850bb..dc14cf54 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -38,6 +38,7 @@ "@codemirror/language-data": "^6.5.2", "@codemirror/state": "^6.6.0", "@codemirror/view": "^6.43.0", + "@lezer/common": "1.5.2", "@lezer/highlight": "^1.2.3", "@sentry/electron": "^7.13.0", "@tanstack/react-query": "^5.101.0", diff --git a/knip.json b/knip.json index 24b39654..5def887f 100644 --- a/knip.json +++ b/knip.json @@ -19,7 +19,7 @@ "src/main/index.ts", "src/preload/index.ts", "src/renderer/main.tsx", - "electron-vite.config.ts", + "electron.vite.config.ts", "vitest.config.ts", "playwright.config.ts", "e2e/**/*.{ts,spec.ts}" diff --git a/package.json b/package.json index 53e52652..f416852b 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,11 @@ "*.{json,md}": "prettier --write" }, "packageManager": "pnpm@9.15.1", - "pnpm": {}, + "pnpm": { + "overrides": { + "@lezer/common": "1.5.2" + } + }, "engines": { "node": ">=20.0.0", "pnpm": ">=9.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6147157..7363d7f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + '@lezer/common': 1.5.2 + importers: .: @@ -98,6 +101,9 @@ importers: '@codemirror/view': specifier: ^6.43.0 version: 6.43.0 + '@lezer/common': + specifier: 1.5.2 + version: 1.5.2 '@lezer/highlight': specifier: ^1.2.3 version: 1.2.3 @@ -1991,9 +1997,6 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@lezer/common@1.5.0': - resolution: {integrity: sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==} - '@lezer/common@1.5.2': resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} @@ -8824,7 +8827,7 @@ snapshots: '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 '@codemirror/view': 6.43.0 - '@lezer/common': 1.5.0 + '@lezer/common': 1.5.2 '@lezer/markdown': 1.6.2 '@codemirror/lang-php@6.0.2': @@ -9817,8 +9820,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@lezer/common@1.5.0': {} - '@lezer/common@1.5.2': {} '@lezer/cpp@1.1.4': @@ -9841,7 +9842,7 @@ snapshots: '@lezer/highlight@1.2.3': dependencies: - '@lezer/common': 1.5.0 + '@lezer/common': 1.5.2 '@lezer/html@1.3.13': dependencies: