Skip to content

[Docs] Generated theme modules require a compiled icons module alongside — this isn't documented #4621

Description

@imdreamrunner

Description

When a theme's icons: field is fed by a relative import, astryx theme build writes that import into the module it generates:

// dist/neutral.js
import { neutralIconRegistry } from './icons';

Producing a loadable module at that path is the caller's responsibility — theme build does not compile the icon registry. That's a reasonable division of labour, and the theme packages satisfy it with their own pipeline (astryx theme build && tsup && tsc, where tsup emits dist/icons.mjs).

But nothing says so. The docs list the four emitted files (.css, .js, .d.ts, optional .variants.d.ts) without mentioning that one of them carries an external requirement, and a reader would have to infer it from the theme packages' build scripts.

What a theme author actually has to do

Two steps, starting from a stock astryx theme add scaffold:

$ astryx theme build src/neutralTheme.ts
$ sed -n 8p src/neutral.js
import { neutralIconRegistry } from './icons';
$ node -e "import('./src/neutral.js')"
ERR_MODULE_NOT_FOUND                       # icons are still TypeScript

# 1. compile the icon registry  (2.0 kB)
$ esbuild src/icons.tsx --bundle --format=esm --outfile=src/icons.mjs \
    --external:react --external:lucide-react --jsx=automatic

# 2. point the generated module at it
$ sed -i '' "s#from './icons'#from './icons.mjs'#" src/neutral.js

$ node -e "import('./src/neutral.js')"
LOADS — theme=neutral, icons=26
$ esbuild src/neutral.js --bundle --format=esm --outfile=/dev/null
BUNDLES OK

Do both and the module works in Node and in bundlers. Skip step 1 and it works only in bundlers, which resolve ./icons to the neighbouring icons.tsx. Skip step 2 and it never works in Node, because ./icons is not a valid ESM specifier — that part is #4620, and step 2 isn't currently possible without hand-editing a @generated file.

The most visible symptom of the missing documentation is that this exits 0:

$ astryx theme build src/myTheme.ts -o dist/theme.css   # no compile step in this project
$ ls dist
my.d.ts  my.js  my.variants.d.ts  theme.css             # nothing named icons

That module resolves in nothing — Node, esbuild and Vite all fail — and the build reported success.

Suggested

Document the contract: the generated module imports the icon registry from a sibling path, producing a module there is the caller's job, and here is the one-line esbuild invocation that does it. The transcript above is most of the docs already.

That's the whole ask. In particular, theme build should not check this itself — it runs first in the pipeline, so at codegen time the target legitimately doesn't exist yet. Immediately after step 1 in a theme package, dist/ holds only the four generated files and no icons module, so a resolve-check inside the generator would fail on every correct build of all seven packages. Emitting a forward reference is right; whether the finished artifact resolves is the pipeline's business.

If a check is wanted, it belongs after the build, and there's already a home for it: scripts/verify-exports.mjs runs in CI as "Verify package exports" and walks every exports map, but only existsSync()s each target and never opens it. Extending it to await import() each resolvable target is ~15 lines, can't false-positive because it runs on a finished build, and catches both this and #4569's failure mode. (scripts/check-fully-specified.mjs can't cover it — that one is purely textual and checks only that specifiers have an extension, never that the target exists.)

If the contract is considered self-evident and the --out-into-an-uncompiled-directory case is user error, this is fine to close — #4620 is then the whole bug. Raising it only because the current behaviour is silent.

Environment

  • @astryxdesign/{cli,core,theme-*} 0.2.0
  • Node 22.17.1, macOS 26.6 (Darwin 25.6.0, arm64), esbuild 0.28.1, Vite 8.2.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions