Summary
Module pages live in modules/<m>/lacowiki_<m>/pages/<Name>.tsx (workspace member) or .venv/lib/python3.12/site-packages/<m>/pages/<Name>.tsx (wheel-installed). Vite's normal Node module resolution walks UP from the .tsx file looking for node_modules and fails because module dirs don't have one.
The framework currently requires three workarounds:
- Symlink
node_modules into every module dir. Each module's root needs node_modules → ../../host/client_app/node_modules. Without this, Vite can't resolve import { Button } from '@simple-module-py/ui/components/ui/button' from a module page. - Aliases in
vite.config.ts. The host's vite config has to alias react/jsx-runtime, react, react-dom, etc. to absolute paths in host/client_app/node_modules/, otherwise module .tsx files can't resolve them. - Module-side
tsconfig.json can't extend @simple-module-py/tsconfig. TypeScript's extends resolves relative to the tsconfig's location, but @simple-module-py/tsconfig lives in host/client_app/node_modules — TS can't find it from modules/<m>/tsconfig.json. Every module tsconfig has to inline the base config verbatim.
Reproduction
- Generate a host, add a workspace module under
modules/lacowiki_legends/. - Module ships
lacowiki_legends/pages/Browse.tsx importing @simple-module-py/ui. npm run build fails with "failed to resolve import @simple-module-py/ui" until you symlink node_modules into the module dir.- After symlinking,
import { foo } from 'react/jsx-runtime' fails until you alias it in vite.config.ts. - Module's
tsconfig.json with "extends": "@simple-module-py/tsconfig" fails until you inline the base config.
Workarounds we ship
Every module:
ln -s ../../host/client_app/node_modules modules/lacowiki_<m>/node_modules
Every module's tsconfig.json is a 19-line inline copy of the base.
Host's vite.config.ts has 8 alias entries.
Suggested fix
This is core to how module-shipped pages work — not a paper-cut. Options:
- Have the framework's
gen-pages step also create the symlinks when it walks the manifest. One-liner per module on regeneration; eliminates manual symlinking. - Publish
@simple-module-py/tsconfig as a relative alias (e.g. via a per-module tsconfig template the framework writes alongside the symlink). - Ship a Vite plugin (
@simple-module-py/vite-plugin) that handles the resolution differences between in-tree and wheel-installed module pages, including the React aliases. Then vite.config.ts becomes one plugins: [smPlugin()] line.
Impact
The "five-step boilerplate every new module needs" is documented in every plan in our project. New users hit at least three of these traps before the build compiles.
Environment
vite@6.4.2, @simple-module-py/ui@0.0.3, @simple-module-py/tsconfig@0.0.3
Summary
Module pages live in
modules/<m>/lacowiki_<m>/pages/<Name>.tsx(workspace member) or.venv/lib/python3.12/site-packages/<m>/pages/<Name>.tsx(wheel-installed). Vite's normal Node module resolution walks UP from the .tsx file looking fornode_modulesand fails because module dirs don't have one.The framework currently requires three workarounds:
node_modulesinto every module dir. Each module's root needsnode_modules → ../../host/client_app/node_modules. Without this, Vite can't resolveimport { Button } from '@simple-module-py/ui/components/ui/button'from a module page.vite.config.ts. The host's vite config has to aliasreact/jsx-runtime,react,react-dom, etc. to absolute paths inhost/client_app/node_modules/, otherwise module .tsx files can't resolve them.tsconfig.jsoncan't extend@simple-module-py/tsconfig. TypeScript'sextendsresolves relative to the tsconfig's location, but@simple-module-py/tsconfiglives inhost/client_app/node_modules— TS can't find it frommodules/<m>/tsconfig.json. Every module tsconfig has to inline the base config verbatim.Reproduction
modules/lacowiki_legends/.lacowiki_legends/pages/Browse.tsximporting@simple-module-py/ui.npm run buildfails with "failed to resolve import @simple-module-py/ui" until you symlink node_modules into the module dir.import { foo } from 'react/jsx-runtime'fails until you alias it invite.config.ts.tsconfig.jsonwith"extends": "@simple-module-py/tsconfig"fails until you inline the base config.Workarounds we ship
Every module:
Every module's
tsconfig.jsonis a 19-line inline copy of the base.Host's
vite.config.tshas 8 alias entries.Suggested fix
This is core to how module-shipped pages work — not a paper-cut. Options:
gen-pagesstep also create the symlinks when it walks the manifest. One-liner per module on regeneration; eliminates manual symlinking.@simple-module-py/tsconfigas a relative alias (e.g. via a per-module tsconfig template the framework writes alongside the symlink).@simple-module-py/vite-plugin) that handles the resolution differences between in-tree and wheel-installed module pages, including the React aliases. Thenvite.config.tsbecomes oneplugins: [smPlugin()]line.Impact
The "five-step boilerplate every new module needs" is documented in every plan in our project. New users hit at least three of these traps before the build compiles.
Environment
vite@6.4.2,@simple-module-py/ui@0.0.3,@simple-module-py/tsconfig@0.0.3