microsoft / microsoft/fluentui
[Bug]: `export-maps-sync` silently deletes any export-map key it cannot derive from a source entry point
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 20.3k
- Forks
- 2.9k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 46
Description
Area
workspace-plugin (package)
Environment
node v22.12.0
tools/workspace-plugin — generators/export-maps-sync
nx sync / nx sync:check
Current Behavior
export-maps-sync rebuilds a project's entire exports map from its declared entry points and
keeps nothing else. Any subpath the generator cannot derive from a source file is therefore
silently deleted on the next nx sync.
That is fine for an all-TypeScript map, where every key has a source entry point behind it. It is
destructive for a package that ships anything a source glob cannot produce — a compiled stylesheet,
a raw .css source shipped for @source scanning, a wildcard asset subpath.
Measured on a package with 151 export keys, of which 4 are asset subpaths:
metadata.exportMap declaration |
keys surviving nx sync |
|---|---|
| none | 2 of 151 |
subpathEntryPoints only |
149 — losing exactly ./styles.css and ./variants.css |
There is no diagnostic in either case. The keys are simply gone from package.json, and the first
sign is a consumer's failed import.
Expected Behavior
Either the generator preserves keys it does not own, or it fails loudly when it is about to drop
one. Silently rewriting a published package's export map is the failure mode that most needs a
guard.
Reproduction
- Take any package whose
exportsmap contains an asset subpath (a.cssfile, say) alongside
TypeScript entry points. - Run
nx sync. - Diff
package.json— the asset subpath is gone, with no warning.
Steps to reproduce
See above; the branch demonstrating both the loss and the fix is available on request.
Discovery context
Found by measuring what un-privateing a new package would actually do, during work on a styling
layer over @fluentui/react-headless-components-preview. That package ships a compiled stylesheet
and a raw .css authoring source alongside 151 TypeScript subpaths, which is what made the loss
measurable rather than theoretical — but the failure mode is general and applies to any package in
the workspace that ships a non-derivable subpath.
Documenting the hazard in a guard note was the earlier answer. It was not good enough: a note does
not survive the next contributor running nx sync.
Proposed fix
Add metadata.exportMap.staticSubpaths — a list of keys the generator explicitly does not own:
// project.json
"metadata": {
"exportMap": {
"subpathEntryPoints": ["…"],
"staticSubpaths": ["./styles.css", "./variants.css", "./base.css", "./css/*"]
}
}
Only the keys are declared there; the entries themselves stay hand-authored in package.json,
next to the files array that ships them, and are read back verbatim on every sync — so there is no
second copy of the paths to drift.
Two further changes come with it:
- A declared key with nothing to preserve now throws, naming the key, instead of dropping it.
The silent-deletion failure mode becomes a loud one. - Key emission runs through one ordering pass (
.first,./package.jsonlast, the rest
alphabetical), so the generator's output is canonical rather than insertion-ordered.
Verification, measured on the real workspace:
- Eleven unit tests in the plugin's existing style.
nx syncreproduces all 151 keys with byte-identical values; the only movement is
./package.jsonrelocating to its canonical last position.- The second
nx syncis a no-op andnx sync:checkreports the workspace up to date — before
and after the generator change, so no pre-existing package in the workspace moves. - Deleting a declared key's target while the key stays declared fails the sync with the key
named, verified against the real workspace. - A later change exercised it further: two more static keys (including a wildcard
./css/*) were
added and needed no generator change, because declared keys are copied verbatim.
A working implementation is included in PR #[WINDMOD-PR-NUMBER] (commit b7c455ee52), including
the README update; happy to split it into a standalone PR against this issue — it is independent of
everything else in that PR and is useful to the workspace on its own.
Suggested severity
High - No workaround (short of not running the repo's own sync generator).
Products/sites affected
Any workspace package shipping an export-map key without a source entry point behind it.
Are you willing to submit a PR to fix?
yes
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in tools/workspace-plugin, at the generators/export-maps-sync entry point, and inspect the existing unit tests and export-map handling in project.json and package.json. Run nx sync on a package with asset subpaths, then verify that declared static keys are preserved, missing targets fail with the key named, and output ordering is canonical. Finish by running the plugin tests, nx sync, and nx sync:check.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- build-system, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100