feat(v9): ship web packages ESM-first (type:module), drop node export condition#36327
Draft
Hotell wants to merge 9 commits into
Draft
feat(v9): ship web packages ESM-first (type:module), drop node export condition#36327Hotell wants to merge 9 commits into
node export condition#36327Hotell wants to merge 9 commits into
Conversation
…targeted bundlers Adds a `module` condition nested in `node` to every converged v9 package's export map: - node-targeted bundlers (webpack/rollup/vite/esbuild) resolve the ESM build and tree-shake - bare Node ignores `module` and falls back to CommonJS, keeping SSR single-instance / dual-package-hazard free - enables fully-specified .js import emit via swc `baseUrl` so lib/ is valid ESM Updates the migrate-converged-pkg generator (source of truth) + swcrc template, and rolls the change out across all v9 packages with matching beachball change files.
…rt condition Migrates converged v9 web packages to ESM-first packaging: - `type: module` with valid ESM under lib/ (fully-specified .js) and CommonJS under lib-commonjs/*.cjs - drop the `node` export condition; bare-Node `import` resolves ESM, `require` resolves CJS - per-condition types: `require` points at a rolled `dist/*.d.cts` (attw-clean for node16/nodenext CJS) - rename CJS dev configs to .cjs (jest/eslint/tests) for type:module compatibility Build executor auto-emits lib-commonjs .cjs + dist .d.cts (gated on type:module). Generators (migrate-converged-pkg, react-library) emit the new web shape; optional `attw` target added. Excludes CJS-first (platform:node, storybook addons) and just-scripts (charts) packages.
|
Pull request demo site: URL |
@fluentui/tokens is now type:module (ESM, named exports, no default). The generate-tokens script relied on CJS-interop default import; switch to a namespace import.
…e tests - rit.config.js -> .cjs (react-provider) + teach rit loader (args.ts) and workspace-plugin rit-target inference to prefer .cjs - .storybook/main.js -> .cjs (recipes), scripts/server.js -> .cjs (tokens) - getDependencies.spec: react-text main is now lib-commonjs/index.cjs; refresh dep-tree order snapshot - react-library spec: drop stale jest.config.js from scaffold snapshot
Graph traversal order is non-deterministic across machines; sort deps by name for a stable snapshot.
| @@ -153,3 +153,5 @@ gulp-cache | |||
| .cursor/rules/nx-rules.mdc | |||
There was a problem hiding this comment.
🕵🏾♀️ visual changes to review in the Visual Change Report
vr-tests-react-components/Positioning 2 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png | 753 | Changed |
| vr-tests-react-components/Positioning.Positioning end.chromium.png | 733 | Changed |
vr-tests-react-components/ProgressBar converged 3 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png | 51 | Changed |
| vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png | 51 | Changed |
| vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png | 160 | Changed |
vr-tests-react-components/TagPicker 2 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/TagPicker.disabled - Dark Mode.chromium.png | 658 | Changed |
| vr-tests-react-components/TagPicker.disabled - RTL.chromium.png | 635 | Changed |
vr-tests-web-components/Avatar 1 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-web-components/Avatar. - Dark Mode.normal.chromium_1.png | 298 | Changed |
vr-tests-web-components/RadioGroup 1 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-web-components/RadioGroup. - Dark Mode.2nd selected.chromium_3.png | 119 | Changed |
vr-tests/react-charting-LineChart 1 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests/react-charting-LineChart.Multiple - Dark Mode.default.chromium.png | 181 | Changed |
There were 4 duplicate changes discarded. Check the build logs for more information.
Migrate @fluentui/scripts-cypress to type:module so its TS source loads correctly when imported from migrated (type:module) cypress configs: - package.json: type:module; rename node-run configs to .cjs (eslint/jest) - base.config.ts: __dirname -> import.meta.dirname; add webpack resolve.extensionAlias (.js->.ts) - index.ts: explicit .ts specifier for base.config re-export (resolves under native ESM via require(esm) from CommonJS cypress configs); explicit extension on browser type import - browser/index.ts + support/*.js: fully-specified import specifiers; convert support require() to static ESM import - tsconfig.lib.json: noEmit + allowImportingTsExtensions (source-only package) - rit cypress.config template: __dirname -> process.cwd() for ESM/CJS sandbox parity Validated: react-utilities/babel-preset-global-context/react-headless-components-preview e2e, react-menu-grid-preview test-rit (17) e2e + test, scripts-cypress lint/test/type-check, workspace-plugin (239) and react-integration-tester (37) unit tests.
…odule) Complete the ESM-first migration for the one missed web package (it still shipped the pre-migration shape: main=lib-commonjs/index.js, no type field): - package.json: type:module, main->lib-commonjs/index.cjs, per-subpath exports rewritten to ESM-first nested shape (import.types=.d.ts/default=lib.js, require.types=.d.cts/default=lib-commonjs/*.cjs), dist/*.d.cts in files - jest/eslint/test-setup configs renamed to .cjs - generate-api: read subpath types from nested import.types (not just flat types) so exportSubpaths rollups work with the ESM-first export shape Also simplify @fluentui/scripts-cypress: inline base.config into index.ts to remove the only cross-file relative import. This fixes a type-check regression where the prior explicit .ts specifier leaked an allowImportingTsExtensions requirement into every cypress consumer, while still resolving under Node's native ESM loader for CommonJS cypress configs. Validated: react-headless build/attw(green)/lint/test(712)/type-check/e2e(185), react-utilities & babel-preset-global-context e2e, react-menu-grid-preview test-rit(17) e2e, scripts-cypress lint/type-check, workspace-plugin (239) tests.
- react-examples (v8, tsc --module commonjs) compiled FocusTrapZone.e2e.tsx, which imports @fluentui/scripts-cypress; the inlined base.config uses import.meta (required for type:module) and is invalid under module:commonjs. Exclude e2e cypress test files from the v8 library build (they're bundled by cypress at runtime, never part of the shipped lib). - scripts-monorepo: annotate the getDependencies.spec sort comparator so checkJs doesn't flag implicit-any params (TS7006).
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.
Summary
Migrates converged v9 web packages to ESM-first packaging so bare-Node SSR can
importreal ESM (and tree-shake), whilerequirestill gets CommonJS — no consumer config, no dual-package hazard for single-format graphs.lib/ships valid native ESM (fully-specified.jsvia swcresolveFully).lib-commonjs/ships.cjs(required undertype: module).nodecondition is dropped: bare-Nodeimport→ ESM,require→ CJS; bundlers resolveimport→ ESM and tree-shake.requirepoints at a rolleddist/*.d.ctssonode16/nodenextCJS consumers are@arethetypeswrong-clean.Why (vs the
modulecondition in #36324)The
modulecondition only helps bundled SSR. This unlocks bare-Node externalized ESM (edge/workers, externalized deps) too, by makinglib/genuinely Node-loadable ESM. Latest Griffel (type: module, nonode) already ships this exact shape, so the ecosystem is aligned.What's automated (no per-package manual steps)
lib-commonjs/*.js→*.cjs(+ rewrites relativerequire/maps) and copiesdist/*.d.ts→*.d.cts— gated ontype: module(no-op otherwise).migrate-converged-pkg,react-library): emit the ESM-first shape,.cjsdev configs, nested-types exports, anddist/*.d.ctsinfiles.jest.config.cjs; adds an optionalattwtarget (dependsOn: build, not in CI gates).Scope / exclusions
type: module).platform:nodepackages, storybook addons /preset.js, and just-scripts packages (charts —just.config.*is incompatible withtype: module).Validation
require✅ andimport✅ both load; functionally real.Known limitation (follow-up, not a regression)
Bare-Node
importof icon-dependent packages currently fails inside@fluentui/react-icons— an external package whose ESM (lib/index.js) uses extensionless imports (not valid bare-Node ESM). Bundled SSR, client, andrequire()are unaffected. Needs an upstream fully-specified-ESM fix in react-icons (or temporary exclusion of icon-dependent packages).Reviewer notes
importonly "lights up" once a package's entire transitive@fluentui/*closure is migrated (CJSexport *barrels break Node's named-import interop). Throughout rollout,require+ bundled never regress.type: modulepackages updated to describe the ESM-first state; excluded packages keep the module-condition message.