fix(vite): seed esbuild nodePaths so scan-imports resolves bare imports from module pages - #155
Merged
Conversation
…ross-package bare imports from module pages (#152)
Deploying simple-module-python with
|
| Latest commit: |
769510c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://6129667f.simple-module-python.pages.dev |
| Branch Preview URL: | https://githubissues.simple-module-python.pages.dev |
antosubash
added a commit
that referenced
this pull request
Aug 9, 2026
…me imports (#253) (#254) * fix(hosting): emit absolute paths for module CSS imports (#253) `gen-pages` emitted `@import "#module/<pkg>/styles.css"` into `modules.generated.css`. That specifier only resolves if the host's `vite.config.ts` defines a matching `resolve.alias` — but that file is scaffold output: written into an app once, then owned and edited there. It is versioned independently of these Python packages, so a Python-only bump 0.0.26 -> 0.0.27 broke `vite build` in every app scaffolded earlier, with `Can't resolve '#module/<pkg>/styles.css'` naming a specifier that appears nowhere in the app's own sources. Emit absolute paths instead, exactly as the `@source` lines in the same file already did. The stated objection to filesystem paths (an ugly `../../../.venv/lib/python3.12/site-packages/...`) only ever applied to *relative* ones. `modules.generated.css` is now self-contained and needs no host cooperation at all. Verified on the scaffold's own stack (vite 6.4.3 + @tailwindcss/vite 4.3.3) that an absolute `@import` resolves with no alias configured, and that the alias form fails there with the reporter's exact error; and on the repo's stack (vite 8) for both `vite build` and the dev server, including a module directory entirely outside the project root. The `#module/<pkg>` alias stays in the scaffold template — it costs nothing and removing it would be a second breaking change to a file apps own — but nothing generated depends on it any more. Also: - biome.json: exclude `modules.generated.css` / `modules.assets.json`, matching the existing exclusions for the other two generated files. Absolute paths are long, so whether a line exceeds biome's 100-char lineWidth depended on where the repo happened to live — `make lint` passed or failed by checkout path. - CHANGELOG: move the #152/#155/#156/#173 entries out of [Unreleased] into the releases that actually shipped them (verified with `git tag --contains`). Leaving them there is what led #253 to conclude the #156 fix was unreleased when it has shipped since v0.0.15. * feat(hosting): let a module import a sibling's TS by npm package name (#253) Nothing in Node's own resolution made this work. A wheel-installed module is not an npm workspace member, so it never lands in node_modules at all; a workspace member *is* symlinked, but onto the source-tree module root — one level above the Python package — so subpaths landed somewhere that does not exist. Either way `@simple-module-py/foo/components/x` failed to resolve. `gen-pages` now records each module's `npm_name` in modules.assets.json and the host aliases it onto the module's **Python package directory**. That anchor is forced, not chosen. A wheel ships `site-packages/<pkg>/**` and nothing above it — Hatch force-includes the module-root package.json *into* the package — so the source-tree module root does not survive installation, and the package dir is the only anchor both layouts share. Verified by inspecting a real built wheel. The practical consequence is that the subpath is relative to the package: @simple-module-py/foo/components/Widget ok in both layouts @simple-module-py/foo/foo/components/Widget workspace-only The second shape is what npm's own symlink gives you for a workspace member, which is why hand-rolled aliases against the module root appear to work in a checkout and break once the module is wheel-installed. npm_name discovery skips a parent package.json unless that directory also holds a pyproject.toml, so a wheel-installed module cannot pick up a stray site-packages/package.json and alias itself onto a stranger. Sibling module names are also excluded from optimizeDeps: they resolve to source directories, not to pre-bundlable packages. Verified end-to-end in a real `smpy new` app on the template's vite 6 stack: a workspace module importing a wheel-installed module by package name — the exact shape from the issue — builds and pulls the sibling's component into the chunk. Also verified the new template degrades gracefully against 0.0.27 packages (no npm_name -> no alias, no crash). Structural changes to stay under the 300-line cap, per CLAUDE.md's "split by responsibility": - host/client_app/module-assets.ts — module discovery lifted out of vite.config.ts (which was already at 291 lines), mirroring the existing compress-assets.ts split. - framework/cli/tests/test_module_npm_aliases.py — npm identity tests, with the importable-module factory moved to a tests/conftest.py fixture so both files share it without a cross-file import. Tests: a real-build guard proving a cross-module import resolves (checked that it fails when the alias is removed, so it is not vacuous), plus unit coverage for both install layouts, the pyproject guard, malformed package.json, and npm-name uniqueness. * ci: run the module-asset build guards in a job that can actually run them test_module_css_build.py self-skips without node_modules, and the `Python tests` job runs `make install-py` only — so both real-build guards silently skipped in CI (`test_module_css_build.py ss`). They are the only tests that prove module CSS and cross-module npm-name imports resolve through a real Tailwind/Vite build; every unit test around them stays green while the wiring is broken. `JS build (Vite)` already installs Python deps, JS deps, and runs gen-pages + build, so it is the one job with everything they need.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
maplibre-glandpmtilesfrom TSX pages that live outside the host'sclient_app/— wheel-installed modules and workspace modules shipping their own JS deps.optimizeDeps.esbuildOptions.nodePaths— esbuild's documented NODE_PATH-style fallback for its dependency scanner.host/client_app/vite.config.ts(Vite 8) uses the Rolldown equivalent:optimizeDeps.rolldownOptions.resolve.moduleswith'node_modules'plus an absolute fallback to the workspacenode_modules/. (Vite 8 deprecatedesbuildOptionsand only forwards a fixed subset of fields —nodePathsis not among them.)optimizeDepsdoes not run duringvite build. A build-time fix is a separate concern and is not in this PR.Test plan
framework/cli/tests/test_scaffolding_host.pyasserts the scaffold wiresesbuildOptions.nodePathstofsRoot/node_modules.make test-pypasses locally (1011 passed).make lintpasses locally.npx tsc --noEmit -p host/client_app/tsconfig.jsonhas no new errors invite.config.ts.make devboot smoke (manual — human verifier).smpy_gisconfirms the original repro is fixed.Closes #152.