Summary
Sister issue to #116. Even after sm host sync-js-deps puts a module's required npm packages into host/client_app/node_modules, Vite still can't resolve those bare imports from .tsx files that live outside the host directory. The Node module-resolution algorithm walks up from each importer's directory looking for node_modules, and from .venv/lib/python3.12/site-packages/<module>/pages/Foo.tsx (or modules/<pkg>/<pkg>/pages/Foo.tsx) the walk never reaches host/client_app/node_modules — there is no symlink in any intermediate directory.
So #116 fixes the install; this issue is what's left after the install: the resolve still fails.
The host's vite.config.ts template already hand-aliases react, react-dom, @inertiajs/react, but every other bare specifier a module page can plausibly import (sonner, lucide-react, clsx, class-variance-authority, tailwind-merge, cmdk, maplibre-gl, pmtiles, recharts, vaul, @simple-module-py/ui/..., @simple-module-py/i18n/..., …) is left to Node resolution. The comment in the scaffolded vite.config.ts claims:
Subpath imports (e.g. @simple-module-py/ui/components/ui/badge) resolve via the symlinked node_modules in module dirs and the package's own exports map.
But no such symlinks are created by sm gen-pages, sm sync-js-deps, or anything else the framework ships.
Reproduction
- Fresh
sm new host, install the framework's modules + a wheel-installed module that imports e.g. sonner.
cd client_app && npm install sonner so the dep is present in host/client_app/node_modules/sonner.
- Open any page that triggers loading the module's
.tsx.
[plugin:vite:import-analysis] Failed to resolve import "sonner" from
"../../.venv/lib/python3.12/site-packages/<mod>/pages/UploadCard.tsx".
The package is installed; Vite just can't see it from that importer.
Expected
sm gen-pages (or a sibling command, or a Vite plugin shipped under simple_module_hosting) should make module pages able to resolve any package that lives in host/client_app/node_modules. Two viable shapes:
A. Ship a simple_module_hosting/vite_plugin.ts (npm-published or template-copied into the scaffold) that intercepts bare-specifier resolution and retries against host/client_app/node_modules whenever the importer is outside the host directory:
{
name: 'sm:module-host-resolve',
enforce: 'pre',
async resolveId(id, importer) {
if (!importer || importer.startsWith(__dirname)) return null;
if (id.startsWith('.') || id.startsWith('/') || id.startsWith('\0') || id.includes('?')) return null;
return this.resolve(id, path.join(hostNodeModules, '__entry__'), { skipSelf: true });
},
}
B. Have sm gen-pages materialize a node_modules symlink (or junction) inside each module's package root that points at host/client_app/node_modules. This is what the vite.config.ts comment already claims happens.
(A) is portable and doesn't litter the wheel install tree; (B) lets Node resolution work without a custom plugin and is closer to how npm/pnpm workspaces do it.
Acceptance
- A fresh
sm new host with one wheel-installed module that imports sonner boots make dev cleanly, with no hand-edits to client_app/vite.config.ts.
- The scaffolded
vite.config.ts either contains the resolver plugin from (A) or the comment about "symlinked node_modules in module dirs" matches reality.
Workaround
In host/client_app/vite.config.ts, add a pre-resolver before react():
const moduleHostResolve = {
name: 'host:module-host-resolve',
enforce: 'pre' as const,
async resolveId(id: string, importer?: string) {
if (!importer) return null;
if (id.startsWith('.') || id.startsWith('/') || id.startsWith('\0')) return null;
if (id.startsWith('virtual:') || id.startsWith('@vite/') || id.includes('?')) return null;
if (importer.startsWith(__dirname) || importer.startsWith(hostNodeModules)) return null;
try {
const resolved = await this.resolve(id, path.join(hostNodeModules, '__entry__'), { skipSelf: true });
if (resolved) return resolved;
} catch { /* fall through */ }
return null;
},
};
Related
Summary
Sister issue to #116. Even after
sm host sync-js-depsputs a module's required npm packages intohost/client_app/node_modules, Vite still can't resolve those bare imports from.tsxfiles that live outside the host directory. The Node module-resolution algorithm walks up from each importer's directory looking fornode_modules, and from.venv/lib/python3.12/site-packages/<module>/pages/Foo.tsx(ormodules/<pkg>/<pkg>/pages/Foo.tsx) the walk never reacheshost/client_app/node_modules— there is no symlink in any intermediate directory.So #116 fixes the install; this issue is what's left after the install: the resolve still fails.
The host's
vite.config.tstemplate already hand-aliasesreact,react-dom,@inertiajs/react, but every other bare specifier a module page can plausibly import (sonner,lucide-react,clsx,class-variance-authority,tailwind-merge,cmdk,maplibre-gl,pmtiles,recharts,vaul,@simple-module-py/ui/...,@simple-module-py/i18n/..., …) is left to Node resolution. The comment in the scaffoldedvite.config.tsclaims:But no such symlinks are created by
sm gen-pages,sm sync-js-deps, or anything else the framework ships.Reproduction
sm newhost, install the framework's modules + a wheel-installed module that imports e.g.sonner.cd client_app && npm install sonnerso the dep is present inhost/client_app/node_modules/sonner..tsx.The package is installed; Vite just can't see it from that importer.
Expected
sm gen-pages(or a sibling command, or a Vite plugin shipped undersimple_module_hosting) should make module pages able to resolve any package that lives inhost/client_app/node_modules. Two viable shapes:A. Ship a
simple_module_hosting/vite_plugin.ts(npm-published or template-copied into the scaffold) that intercepts bare-specifier resolution and retries againsthost/client_app/node_moduleswhenever the importer is outside the host directory:B. Have
sm gen-pagesmaterialize anode_modulessymlink (or junction) inside each module's package root that points athost/client_app/node_modules. This is what thevite.config.tscomment already claims happens.(A) is portable and doesn't litter the wheel install tree; (B) lets Node resolution work without a custom plugin and is closer to how npm/pnpm workspaces do it.
Acceptance
sm newhost with one wheel-installed module that importssonnerbootsmake devcleanly, with no hand-edits toclient_app/vite.config.ts.vite.config.tseither contains the resolver plugin from (A) or the comment about "symlinked node_modules in module dirs" matches reality.Workaround
In
host/client_app/vite.config.ts, add a pre-resolver beforereact():Related
npm run dev.@simple-module-py/uiexports-map fixes; those don't help when the importer can't find the package at all.