microsoft / microsoft/fluentui

[Bug]: `export-maps-sync` silently deletes any export-map key it cannot derive from a source entry point

Open
#36,655 0 comments 0 reactions 0 assignees View on GitHub

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
  1. Take any package whose exports map contains an asset subpath (a .css file, say) alongside
    TypeScript entry points.
  2. Run nx sync.
  3. 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:

  1. 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.
  2. Key emission runs through one ordering pass (. first, ./package.json last, 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 sync reproduces all 151 keys with byte-identical values; the only movement is
    ./package.json relocating to its canonical last position.
  • The second nx sync is a no-op and nx sync:check reports 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.