diff --git a/CHANGELOG.md b/CHANGELOG.md index c65e1414..d038279c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ All notable changes to this project are documented in this file. The format is b seeds `optimizeDeps.esbuildOptions.nodePaths` and the framework repo (Vite 8) seeds `optimizeDeps.rolldownOptions.resolve.modules` with the workspace `node_modules/` as a NODE_PATH-style fallback for the dep scanner (GH issue #152). +- The `moduleBareImportResolver` Vite plugin no longer short-circuits on + `fsRoot`/`projectRoot` containment, so workspace-member modules at + `modules///pages/` get the same workspace-root re-resolution as + wheel-installed modules. In an npm-workspaces layout the workspace root *is* + the resolver root, so the previous early-return excluded the very modules + that need it. Cross-package bare imports (`maplibre-gl`, `pmtiles`, peer + deps) now resolve in both wheel and workspace install modes (GH issue #156). ## [0.0.1] — 2026-04-21 diff --git a/framework/cli/simple_module_cli/templates/host/client_app/vite.config.ts b/framework/cli/simple_module_cli/templates/host/client_app/vite.config.ts index df0d1f8f..d32e2998 100644 --- a/framework/cli/simple_module_cli/templates/host/client_app/vite.config.ts +++ b/framework/cli/simple_module_cli/templates/host/client_app/vite.config.ts @@ -68,7 +68,6 @@ if (fs.existsSync(manifestPath)) { } } } -const fsRootPrefix = fsRoot + path.sep; const fakeWorkspaceImporter = path.join(fsRoot, 'package.json'); // CJS-only deps like `clsx`, `tailwind-merge`, `class-variance-authority` @@ -158,11 +157,13 @@ function collectOptimizeIncludes(): string[] { // Cross-package bare imports from module pages (`maplibre-gl`, `pmtiles`, // `@inertiajs/react`, …) live in fsRoot/node_modules after `npm install`. -// But when a module's pages sit outside fsRoot — under -// `.venv/.../site-packages//pages/` for wheel installs — Vite's -// resolver walks up from the importer looking for node_modules and never -// reaches fsRoot/node_modules. Resolution fails with: "Failed to resolve -// import … Does the file exist?". +// But Vite's resolver walks up from the importer looking for node_modules +// and doesn't always reach fsRoot/node_modules — true for wheel installs +// under `.venv/.../site-packages//pages/` (outside fsRoot) AND for +// workspace-member modules at `modules///pages/` whose upward +// walk hits intermediate dirs without node_modules before reaching the +// hoisted workspace root. Resolution fails with: "Failed to resolve import +// … Does the file exist?". // // This plugin recovers by retrying any unresolved bare import from a // module-pages importer as if the importer lived at fsRoot, which puts @@ -184,7 +185,6 @@ function moduleBareImportResolver(): Plugin { return null; } const importerPath = importer.split('?')[0]; - if (importerPath.startsWith(fsRootPrefix)) return null; if (!modulePagesPrefixes.some((prefix) => importerPath.startsWith(prefix))) { return null; } diff --git a/framework/cli/tests/test_scaffolding_host.py b/framework/cli/tests/test_scaffolding_host.py index af4eeabe..66a35366 100644 --- a/framework/cli/tests/test_scaffolding_host.py +++ b/framework/cli/tests/test_scaffolding_host.py @@ -206,3 +206,42 @@ async def test_scaffold_vite_config_includes_node_paths_fallback(self, tmp_path) assert "node_modules" in vite_config, ( "nodePaths entry must include 'node_modules' (GH issue #152)." ) + + async def test_scaffold_vite_resolver_does_not_skip_workspace_modules(self, tmp_path): + """The moduleBareImportResolver plugin must NOT short-circuit on + ``fsRootPrefix`` containment. + + In an npm-workspaces scaffold, ``fsRoot`` resolves to the workspace + root, which means workspace-member modules at ``modules//`` sit + *under* ``fsRoot``. An early-return gating on ``fsRootPrefix`` skips + them, leaving cross-package bare imports (`maplibre-gl`, `pmtiles`, + ...) unresolved during dev-mode resolveId. + + The plugin should guard only on the module-pages prefix set — the + condition that actually identifies module-page importers regardless of + whether they sit inside or outside ``fsRoot``. + + Regression test for GitHub issue #156. + """ + from simple_module_cli.scaffolding import create_host + + dest = tmp_path / "demo" + create_host(name="demo", dest=dest, modules=[]) + + vite_config = (dest / "client_app" / "vite.config.ts").read_text(encoding="utf-8") + + # The resolver must still exist — the fix shouldn't remove the plugin. + assert "moduleBareImportResolver" in vite_config, ( + "vite.config.ts must register the cross-package bare-import resolver." + ) + # The buggy early-return must be gone (GH issue #156). + assert "startsWith(fsRootPrefix)" not in vite_config, ( + "vite.config.ts must not early-return on fsRootPrefix containment — " + "in npm-workspaces mode workspace-member module pages live under " + "fsRoot and would be incorrectly skipped (GH issue #156)." + ) + # The workspace-root re-resolution must still run. + assert "fakeWorkspaceImporter" in vite_config, ( + "vite.config.ts must re-resolve unresolved bare imports against the " + "workspace root so hoisted node_modules wins (GH issue #156)." + ) diff --git a/host/client_app/vite.config.ts b/host/client_app/vite.config.ts index 4a2f0374..614cec0d 100644 --- a/host/client_app/vite.config.ts +++ b/host/client_app/vite.config.ts @@ -51,7 +51,6 @@ for (const pagesDir of Object.values(manifest)) { } } } -const projectRootPrefix = projectRoot + path.sep; // Gather every bare specifier a module's pages might import. We include // both `dependencies` (deps the module ships its own copy of) and @@ -80,11 +79,13 @@ function collectModuleDecls(): string[] { // Cross-package bare imports from module pages (`maplibre-gl`, `pmtiles`, // `@inertiajs/react`, …) live in the workspace-root node_modules after -// `npm install`. But when a module's pages sit outside the workspace root -// — under `.venv/.../site-packages//pages/` for wheel installs — Vite's -// resolver walks up from the importer looking for node_modules and never -// reaches /node_modules. Resolution fails with: "Failed to resolve -// import … Does the file exist?". +// `npm install`. But Vite's resolver walks up from the importer looking +// for node_modules and doesn't always reach /node_modules — true for +// wheel installs under `.venv/.../site-packages//pages/` (outside the +// project root) AND for workspace-member modules at `modules/// +// pages/` whose upward walk hits intermediate dirs without node_modules +// before reaching the hoisted workspace root. Resolution fails with: +// "Failed to resolve import … Does the file exist?". // // This plugin recovers by retrying any unresolved bare import from a // module-pages importer as if the importer lived at the workspace root, @@ -106,7 +107,6 @@ function moduleBareImportResolver(): Plugin { return null; } const importerPath = importer.split('?')[0]; - if (importerPath.startsWith(projectRootPrefix)) return null; if (!modulePagesPrefixes.some((prefix) => importerPath.startsWith(prefix))) { return null; }