Skip to content

[hosting/scaffold] 0.0.27 gen-pages emits #module/<pkg> CSS imports that pre-0.0.27 hosts cannot resolve — build breaks on upgrade #253

Description

@antosubash

Summary

In 0.0.27 gen-pages started emitting @import "#module/<pkg>/styles.css" into modules.generated.css. That specifier can only resolve if the host's committed, hand-editable host/client_app/vite.config.ts builds a matching Vite alias — and existing hosts never receive that change, because vite.config.ts is scaffold output, not a package file.

Upgrading simple_module_* 0.0.26 → 0.0.27 therefore breaks vite build in any app scaffolded earlier, with an error naming a specifier that appears nowhere in the app's own sources.

Reproduction

An app scaffolded at 0.0.26 (host + a wheel-installed dashboard, which ships styles.css):

# bump simple_module_* to 0.0.27 in host/pyproject.toml
uv sync --all-packages
make gen-pages
make build

modules.generated.css now contains:

/* ── styles: layer(components), always loses to utilities ── */
@import "#module/dashboard/styles.css" layer(components);

and the build fails:

✗ Build failed in 21ms
error during build:
[@tailwindcss/vite:generate:build] Can't resolve '#module/dashboard/styles.css'
  in '<app>/host/client_app'
file: <app>/host/client_app/styles.css

Versions: simple_module_hosting==0.0.27, simple_module_cli==0.0.27, vite 6.4.3, @tailwindcss/vite 4.x, Python 3.12.

Why this is awkward rather than just a migration step

modules.generated.css is generated on every build (and gitignored in our app). vite.config.ts is scaffolded once and then owned and edited by the app — ours carries a base override for serving the bundle under /static/dist/. So the generated artifact and the config that has to resolve it are versioned independently, and a Python-only dependency bump silently invalidates the config.

There's also an internal inconsistency in the emitter that makes this look avoidable. render_modules_css in simple_module_hosting/assets.py emits, into the same file:

  • @sourceabsolute paths (e.pages_dir.as_posix())
  • @importalias specifiers (#module/<pkg>/...)

The docstring explains the alias as avoiding ../../../.venv/lib/python3.12/site-packages/<pkg>/styles.css, which is fair for a relative path. But the @source lines right above are already absolute and interpreter-path-bearing, so absolute @import paths would be no worse and would need no host config at all.

Second, related problem: the template drops aliases the modules still need

The 0.0.27 vite.config.ts template also removes the npm-package-name alias block that maps @simple-module-py/<pkg> onto a module's directory. But module .ts sources still import that way:

  • site-packages/news/puck-blocks.ts:10@simple-module-py/pagebuilder/pagebuilder/components/blockRegistry
  • site-packages/news/components/NewsFeed.tsx:5@simple-module-py/pagebuilder/.../article-cards-render

Following the new template verbatim, our workspace-member module fails to build:

[vite]: Rollup failed to resolve import
  "@simple-module-py/pagebuilder/pagebuilder/components/blockRegistry"
  from "modules/canopy_atlas/canopy_atlas/puck-blocks.ts".

This is the case in #156 — which is closed, but whose fix (moduleBareImportResolver no longer short-circuiting on fsRoot/projectRoot containment) is still under ## [Unreleased] in CHANGELOG.md and is not in 0.0.27. So the template change that depends on that fix shipped a release ahead of it. A fresh smpy new app with a workspace module importing pagebuilder hits this on day one.

We worked around both by keeping the package-name aliases alongside the new #module/* ones — the prefixes are disjoint, so they compose:

alias: [...moduleAliases, ...packageNameAliases],

Suggested fixes

Roughly in order of how much they'd help:

  1. Emit absolute paths for @import, as @source already does. Removes the host-config dependency entirely and makes the generated CSS self-contained. The stated objection (ugly relative paths) doesn't apply to absolute ones.
  2. If the alias is worth keeping, make gen-pages verify it resolves — it already writes modules.assets.json, so it can check vite.config.ts reads that file and fail with the required diff rather than letting Vite fail later on a specifier the app never wrote.
  3. Add a doctor check for the same thing, so make doctor catches it before a build does.
  4. Re-land the package-name aliases in the template until the v0.0.13 cross-package fix doesn't cover workspace-member modules + dep-scan still errors #156 fix is actually released, or hold the template change until it is.
  5. Document it — a CHANGELOG entry for 0.0.27 saying "hosts must update vite.config.ts; here is the diff" would have turned a confusing build failure into a two-minute edit. As of 0.0.27 the CHANGELOG has no per-version entries between 0.0.1 and Unreleased.

Happy to send a PR for (1) or (4) if you have a preference.

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