Skip to content

Module-shipped .tsx pages need symlink + vite aliases + inline tsconfig boilerplate #73

Description

@antosubash

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:

  1. 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.
  2. 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.
  3. 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

  1. Generate a host, add a workspace module under modules/lacowiki_legends/.
  2. Module ships lacowiki_legends/pages/Browse.tsx importing @simple-module-py/ui.
  3. npm run build fails with "failed to resolve import @simple-module-py/ui" until you symlink node_modules into the module dir.
  4. After symlinking, import { foo } from 'react/jsx-runtime' fails until you alias it in vite.config.ts.
  5. 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:

  1. 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.
  2. Publish @simple-module-py/tsconfig as a relative alias (e.g. via a per-module tsconfig template the framework writes alongside the symlink).
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions