From 87f05fb66cbe8b1ae3a0692007d88baa61180ea4 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Fri, 4 Sep 2026 19:02:25 +0800 Subject: [PATCH] build(vue-widgets): keep shared runtime modules external in the widget bundle Guard against the inlined-shim bug class that broke the removed active-filters chip: importing web/comfyui/* modules from widget source inlines them into lora-manager-widgets.js, and their own relative imports then resolve against the repo filesystem at build time instead of the vanilla files' runtime URL layout. A resolveId plugin (enforce: pre) now returns explicit external markers: - scripts/app.js and scripts/api.js imported at any "../../scripts/*" depth are rewritten to the canonical "../../../scripts/*" specifier so every app/api binding in the bundle is the real ComfyUI module. The repo-root scripts/app.js is a unit-test shim (in-memory settings store) and must never be bundled; the canonical depth is the only one that resolves from the emitted bundle's served location. - web/comfyui/settings.js is externalized to "../settings.js" so the bundle binds to the SAME vanilla module instance the ComfyUI extension loader already runs - real settings store, registerExtension side effect executed exactly once, no duplicated module state. A companion plugin warns on any web/comfyui/* import from widget source, since an inlined copy still duplicates module-level side effects. Notes from validating the mechanism: rollup output.paths resolves returned paths to absolute filesystem locations (rejected), and a depth regex inside rollupOptions.external matches raw specifiers before resolveId hooks run and would emit the shim-relative depth verbatim (rejected) - hence explicit { id, external: true } returns. --- vue-widgets/vite.config.mts | 87 +++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/vue-widgets/vite.config.mts b/vue-widgets/vite.config.mts index 681fd72c..63cb3c32 100644 --- a/vue-widgets/vite.config.mts +++ b/vue-widgets/vite.config.mts @@ -1,12 +1,86 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js' -import { resolve } from 'path' +import { dirname, resolve } from 'path' + +// Specifiers that must stay external. The bundle is emitted to +// web/comfyui/vue-widgets/, and ComfyUI serves that directory's parent +// (web/comfyui) at /extensions/ComfyUI-Lora-Manager/, so one "../" from the +// bundle reaches web/comfyui modules and three "../../.." reach ComfyUI's +// own runtime scripts at runtime. +// +// scripts/app.js and scripts/api.js are intentionally NOT listed here: they +// are externalized by the keep-runtime-modules-external plugin below, which +// also rewrites the shallower "../../scripts/*" specifiers used by modules +// inlined from web/comfyui/ so every binding dedupes into a single import. +const EXTERNAL_SPECIFIERS = [ + '../loras_widget.js', + '../autocomplete.js', + '../preview_tooltip.js' +] export default defineConfig({ plugins: [ vue(), - cssInjectedByJsPlugin() // Inject CSS into JS for ComfyUI compatibility + cssInjectedByJsPlugin(), // Inject CSS into JS for ComfyUI compatibility + // Keep shared runtime modules external instead of inlining them into + // the bundle. This guards against the inlined-shim bug class (the + // removed active-filters chip ended up writing settings to a dead + // in-memory store this way): + // + // 1. Modules under web/comfyui/ import the repo-root scripts/app.js + // TEST SHIM as "../../scripts/app.js" — a depth that resolves to + // the shim on the build filesystem but 404s at the bundle's + // runtime location. Rewrite to the canonical bundle-depth + // specifier so every app/api binding in the bundle is the REAL + // ComfyUI module. + // 2. web/comfyui/settings.js must never be duplicated into the + // bundle: it registers settings via a module-level side effect + // and owns module state. Externalize it to "../settings.js" so + // the bundle binds to the SAME vanilla module instance that the + // ComfyUI extension loader already loaded. + { + name: 'lora-manager:keep-runtime-modules-external', + enforce: 'pre', + resolveId(source, importer) { + const scriptsMatch = source.match(/^(\.\.\/)+scripts\/(app|api)\.js$/) + if (scriptsMatch) { + return { id: `../../../scripts/${scriptsMatch[2]}.js`, external: true } + } + if ( + importer && + /[\\/]web[\\/]comfyui[\\/]settings\.js$/.test( + resolve(dirname(importer), source) + ) + ) { + return { id: '../settings.js', external: true } + } + return null + }, + }, + // Warning twin of the rewrite above: importing web/comfyui/* from + // widget source inlines that module into the bundle, duplicating any + // module-level side effects/state it owns. The settings.js and + // scripts/app|api.js imports are made safe by the plugin above, but + // review any further such import deliberately. + { + name: 'lora-manager:warn-web-comfyui-imports', + enforce: 'pre', + resolveId(source, importer) { + if ( + importer && + /[\\/]vue-widgets[\\/]src[\\/]/.test(importer) && + /[\\/]web[\\/]comfyui[\\/]/.test(source) + ) { + this.warn( + `[vue-widgets] Inlining web/comfyui module "${source}" into the bundle. ` + + 'settings.js and scripts/app|api.js imports are externalized by this config, ' + + 'but the inlined copy still duplicates module-level side effects — verify that is intended.' + ) + } + return null + }, + }, ], resolve: { alias: { @@ -20,13 +94,7 @@ export default defineConfig({ fileName: 'lora-manager-widgets' }, rollupOptions: { - external: [ - '../../../scripts/app.js', - '../../../scripts/api.js', - '../loras_widget.js', - '../autocomplete.js', - '../preview_tooltip.js' - ], + external: EXTERNAL_SPECIFIERS, output: { dir: '../web/comfyui/vue-widgets', entryFileNames: 'lora-manager-widgets.js', @@ -41,3 +109,4 @@ export default defineConfig({ 'process.env.NODE_ENV': JSON.stringify('production') } }) +