From 670693bde8fea084012b8d89611da5e6c57ba4e3 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Mon, 3 Aug 2026 03:07:51 +0200 Subject: [PATCH] Support repository-root WebUI packages --- docs/WEBUI_MODULE_PACKAGE_LAYOUT.md | 12 ++++++++++++ webui/vite.config.ts | 19 ++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 docs/WEBUI_MODULE_PACKAGE_LAYOUT.md diff --git a/docs/WEBUI_MODULE_PACKAGE_LAYOUT.md b/docs/WEBUI_MODULE_PACKAGE_LAYOUT.md new file mode 100644 index 0000000..50ed260 --- /dev/null +++ b/docs/WEBUI_MODULE_PACKAGE_LAYOUT.md @@ -0,0 +1,12 @@ +# WebUI Module Package Layout + +Core discovers a module contribution from `src/module.ts` when `node_modules` +links directly to a module's `webui` package. Tagged release dependencies are +installed from repository-root packages and expose the same contribution at +`webui/src/module.ts`. The Vite registry accepts both layouts and imports the +contribution descriptor directly so route-level lazy loading is preserved. + +A release package is invalid if neither entry exists. The module-permutation CI +matrix builds source-linked and installed release compositions; it must not fall +back to a package root barrel because that would eagerly pull module pages into +the shell bundle. diff --git a/webui/vite.config.ts b/webui/vite.config.ts index 5bacf9f..6289f60 100644 --- a/webui/vite.config.ts +++ b/webui/vite.config.ts @@ -75,6 +75,17 @@ function availableWebModuleSpecifiers(): string[] { return configuredWebModulePackages().filter(packageInstalled); } +function webModuleEntry(specifier: string): string | null { + const root = packageDirectory(specifier); + for (const candidate of [ + join(root, "src", "module.ts"), + join(root, "webui", "src", "module.ts") + ]) { + if (existsSync(candidate)) return candidate; + } + return null; +} + function deferredVendorChunk(id: string): string | undefined { const normalized = id.replaceAll("\\", "/"); if (!normalized.includes("/node_modules/")) return undefined; @@ -167,9 +178,11 @@ function govoplanInstalledModulesPlugin(): Plugin { const publicVirtualId = id.slice(1); const specifier = specifierByModuleVirtualId.get(publicVirtualId); if (!specifier) return null; - const moduleEntry = join(packageDirectory(specifier), "src", "module.ts"); - if (!existsSync(moduleEntry)) { - throw new Error(`${specifier} does not expose src/module.ts`); + const moduleEntry = webModuleEntry(specifier); + if (!moduleEntry) { + throw new Error( + `${specifier} does not expose src/module.ts or webui/src/module.ts` + ); } // Import the contribution descriptor directly. Package root barrels may // intentionally re-export pages and would otherwise defeat route splitting.