fix(cli,themes): verify the icon import theme build emits - #4625
Open
AKnassa wants to merge 7 commits into
Open
Conversation
The icon-registry specifier is scraped from the theme source (where jiti resolves extensionless './icons') and was emitted verbatim into a module Node ESM loads — where it resolves nothing. Verify it against the out dir before any write: rewrite onto the compiled companion (preferring .mjs — the .js twin of a dual tsup build is a second registry instance), keep a source-only sibling with a warning (in-place builds consumed through bundlers), and fail loudly with ERR_THEME_ICON_UNRESOLVED when nothing satisfies the import. Fixes facebook#4620 Fixes facebook#4621
Run tsup before astryx theme build so the compiled registry exists when the emitted specifier is verified — the generated module now imports './icons.mjs' and the /built entry loads under Node ESM and externalized SSR. tsup owns dist cleaning now that it runs first, and check-fully-specified.mjs gates every theme's dist the way it already gates core/lab/charts/richtext. Refs facebook#4620
The in-place-build warning went only through the shared logger, which is silent for programmatic callers — mirror it into the receipt's warnings array like the validation warnings. Refs facebook#4620
A case-insensitive filesystem (macOS default) let statSync match icons.mjs onto ICONS.mjs, so a mac build could emit a specifier that fails on the case-sensitive systems the artifact ships to. Only the exact on-disk name satisfies the import now (readdir check). Edge battery: parent-relative and nested specifiers rewrite onto their companions, .cjs-only fallback, a directory named icons is not a module, fully-specified-but-missing fails, the default out path (beside the source) takes the warn path, compiled beats TS-sibling (ladder order, mutation-verified), --json envelope carries ERR_THEME_ICON_UNRESOLVED, and --watch survives the throw and recovers on the next change. Refs facebook#4620 Refs facebook#4621
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
AKnassa
marked this pull request as ready for review
August 1, 2026 02:18
AKnassa
requested review from
cixzhang,
ejhammond,
imdreamrunner and
josephfarina
as code owners
August 1, 2026 02:18
Contributor
PR Analysis Report📚 Storybook PreviewView Storybook for this PR 🧪 Sandbox PreviewView Sandbox for this PR No new or modified components detected. Bundle Size SummaryNo component packages changed. Accessibility AuditStatus: No accessibility violations detected. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
Upstream restructured packages/cli (lib/ -> foundation/response/, cli/ -> clients/cli/). Resolved by moving build-theme.icons.test.mjs to clients/cli/commands/ and updating its two relative paths (run-cli helper, CLI bin). ERR_THEME_ICON_UNRESOLVED merged into foundation/response/ error-codes.mjs via rename detection.
cixzhang
approved these changes
Aug 3, 2026
cixzhang
left a comment
Contributor
There was a problem hiding this comment.
Looks good from the implementation side — the resolver/checking approach makes sense and the focused theme smoke passes.
Before merge, could you please clean up two things:
- Rebase/resolve the current merge conflicts against
main. - Trim the changesets down to changelog-sized summaries. They currently read more like PR-body/root-cause notes; the changelog only needs the consumer-visible effect.
Thanks for the thorough test coverage here.
Resolves the build.mjs file-header conflict: upstream's `--check` mode paragraph (facebook#4565) and this branch's icon-resolve paragraph both land.
Review feedback: the changelog only needs what changed for consumers, not the root cause. The resolve-order reasoning, the TypeScript-source warning case, and the theme build-order/guard wiring stay in the PR body.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
astryx theme buildcopies the icon-registry import from the theme source straight into the module it generates, and nothing checked that the copied specifier means anything from where the generated file lives. The build now verifies it before writing: the specifier is rewritten onto the compiled companion when one exists, kept with a warning when only the TypeScript source sits beside the output, and the build fails with a clear error when nothing can satisfy it.One PR for two issues on purpose: both defects are the same emit step. #4620 is the symptom in the published themes (extensionless
./icons, so/builtcannot load in Node or externalized SSR). #4621 is the symptom for self-built themes (the import points at a module the build never produces). A resolve check is the union of the two fixes, and either half alone would still have to decide the other's case.What changed
packages/cli/api/theme/build/build.mjsresolves the emitted specifier against the out dir before any file is written. Preference order is.mjs, then.js, then.cjs..mjswins deliberately: with a dual tsup build,.jsis the CJS twin and hands consumers a second, distinct registry instance. A registry that exists only as TypeScript source beside the output is kept as written with a warning, since in-place builds are consumed through bundlers that resolve it. When nothing satisfies the import the build throwsERR_THEME_ICON_UNRESOLVEDbefore writing anything, matching the existing stage-then-commit design. Bare package specifiers pass through untouched.astryx theme build, so the compiled registry exists when the specifier is verified, andcheck-fully-specified.mjsgates their dist the way it already gates core, lab, charts and richtext.warningsarray, since the shared logger is silent for programmatic callers..cjsfallback, a directory namedicons, fully-specified-but-missing, ladder order, exact case), plus e2e for the CLI error and exit code, the--jsonenvelope code, watch-mode surviving the failure and recovering on the next change, and a spawned-Node proof that the emitted module actually loads.How to check
The published-theme shape (was
ERR_MODULE_NOT_FOUNDbefore):The self-built shape (was a silently broken module before, now a clear failure):
Not included
The packaging defects the issues list as contributing factors (no
typefield in the theme packages,./builtmappingrequireto the ESM file) are left alone. With the specifier fixed the entry loads under both module systems on current Node, and flippingtypewould change how the existing CJSdist/source.jsparses.Fixes #4620
Fixes #4621