Skip to content

Upgrade Fluent UI to TypeScript 6 - #31

Open
mainframev wants to merge 42 commits into
benchmark/typescript-6-basefrom
feat/36409-typescript-6
Open

Upgrade Fluent UI to TypeScript 6#31
mainframev wants to merge 42 commits into
benchmark/typescript-6-basefrom
feat/36409-typescript-6

Conversation

@mainframev

@mainframev mainframev commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

Upgrades the Fluent UI monorepo to TypeScript 6.0.3 and completes the migration tracked by microsoft#36409.

  • upgrades TypeScript-coupled tooling and published peer ranges
  • removes deprecated TypeScript configuration without ignoreDeprecations
  • migrates browser and Node module resolution intentionally by project role
  • preserves v8 ES5/AMD package output through the SWC artifact pipeline
  • fixes TypeScript 6 source, DOM, Jest, SSR, API Extractor, declaration, Cypress, and React integration regressions
  • adds declaration self-containment and package artifact verification
  • runs fork PR validation on GitHub-hosted Ubuntu while preserving upstream macOS runner behavior

Fork validation

This PR intentionally targets mainframev/fluentui:master before an upstream PR. The fork's CI, package/tooling checks, React-version integration, E2E, VRT screenshot generation, PR website build, and bundle-size build workflows are enabled for mainframev and tuned for four-core Ubuntu runners.

Azure-dependent VRT comparison and website deployment remain upstream-only because fork credentials are unavailable. Bundle-size builds still run; an inaccessible upstream baseline is reported as a warning in the fork rather than masking build failures.

Validation completed locally

  • build: 139/139
  • type-check: 225/225
  • lint: 245/245
  • SSR: 67/67
  • verify-packaging: 6/6
  • API generation: 93/93
  • React integration tester: 40 tests passed
  • workflow YAML parsing, formatting, and affected Jest tooling lint

Notes

  • Registry access was intermittently unavailable during validation.
  • The existing scripts-executors:test interactive hang reproduces on both master and this branch.
  • Browser-dependent coverage will be exercised by this fork PR's Ubuntu workflows.

TypeScript benchmark comparison

Fork baseline: #32 (TypeScript 5.7.3, same base commit and Ubuntu benchmark job).

Compare the TypeScript benchmark / type-check job between both PRs. The job runs all 225 type-check targets uncached with Node 22 and four-way Nx parallelism. Preliminary local results were 376s for TypeScript 5.7.3 and 368s for TypeScript 6.0.3 (8s / 2.1% faster).

mainframev and others added 30 commits July 24, 2026 23:15
- Pin root TypeScript to 6.0.3.
- Update the Vite starter template TypeScript pin to ~6.0.3.
- Upgrade typescript-eslint packages (typescript-eslint, @typescript-eslint/eslint-plugin,
  @typescript-eslint/rule-tester, @typescript-eslint/type-utils, @typescript-eslint/utils)
  to 8.64.0, the closest available release whose typescript peer range
  (>=4.8.4 <6.1.0) officially covers TypeScript 6.0.3 (verified identical to 8.65.0's
  peer contract; 8.65.0 itself only adds an unrelated TS7-detection warning and was
  unreachable from this environment's registry mirror at commit time).
- Upgrade ts-jest to 29.4.11, the closest available release whose typescript peer range
  (>=4.3 <7) covers TypeScript 6.0.3 (same peer contract as 29.4.12; 29.4.12 was
  unreachable from this environment's registry mirror at commit time).
- Regenerate the patch-package patch for @typescript-eslint/eslint-plugin against 8.64.0
  (content unchanged, only the target version differs).
- Regenerate yarn.lock with Yarn classic (v1.23.34, the package manager pinned by this
  worktree's .yarnrc/.yarn/releases) and deduplicate the resulting typescript-eslint
  resolutions.
- Verified ts-loader, ts-node, fork-ts-checker-webpack-plugin, @microsoft/api-extractor,
  and @nx/js declare permissive or bundled typescript version constraints and require no
  changes.

yarn check:installed-dependencies-versions passes. Running nx test/lint on a sample
package now fails with the expected TS5101/TS5107 baseUrl/moduleResolution=node10
deprecation errors from tsconfig.base.json — this is anticipated and left for the
follow-up tsconfig migration task, not fixed here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ependency bump

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rity

Task 2 code-review fix: the regenerated yarn.lock for the TypeScript 6.0.3
compiler-toolchain upgrade introduced internal Azure DevOps mirror URLs
(ms-feed-*.pkgs.visualstudio.com/1es-public/...) and weaker SHA-1 integrity
entries for the 24 packages that were newly resolved or re-resolved by that
task (typescript@6.0.3, the typescript-eslint@8.64.0 family, ts-jest@29.4.11,
and their transitive dependencies).

- Replaced all ms-feed-*.pkgs.visualstudio.com resolved URLs with the
  canonical https://registry.yarnpkg.com/... convention already used
  throughout the rest of the lockfile.
- Replaced all sha1- integrity values (re-encodings of the same shasum
  already present in each entry) with authentic sha512- integrity retrieved
  from the r.cnpmjs.org npm registry mirror, which mirrors npm's original
  signed package metadata verbatim (each entry's metadata carries a valid
  npm registry ECDSA signature, keyid SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U,
  confirming the metadata is unmodified/authentic and not fabricated).
- Verified every replaced shasum (the SHA-1 hex already embedded in each
  resolved URL fragment) matches byte-for-byte between the old lockfile
  entries and the independently-fetched metadata, confirming the underlying
  tarballs were never tampered with -- only the URL/integrity encoding was
  non-canonical.
- No tsconfigs, source files, or peer ranges were changed; no package.json
  version pins were changed. TypeScript 6.0.3, typescript-eslint 8.64.0
  family, and ts-jest 29.4.11 versions are preserved exactly as approved.

Validated:
- yarn install --frozen-lockfile succeeds (integrity checks pass against
  live tarball downloads).
- yarn check:installed-dependencies-versions passes.
- yarn check:change passes (no change file needed for a lockfile-only fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Task 3 of the TypeScript 6 migration: update the shared root tsconfig
base configs and the generator that produces the merged config.

- tsconfig.base.json (v9), tsconfig.base.v8.json, tsconfig.base.wc.json:
  replace deprecated `moduleResolution: node`/`Node` with `bundler`
  (these are bundled/transpiled library configs, not direct-Node
  runtimes), drop the deprecated `baseUrl`, and rewrite every `paths`
  target to be explicitly relative (`./...`) so aliases keep resolving
  identically now that TypeScript 6 resolves `paths` relative to the
  tsconfig file itself instead of `baseUrl`.
- tools/workspace-plugin/src/generators/tsconfig-base-all: update the
  generator to emit `moduleResolution: bundler` and no `baseUrl` in the
  generated tsconfig.base.all.json, and regenerate that file. Added a
  regression test asserting the generated config has no `baseUrl`,
  no legacy Node/Classic module resolution, and keeps every path alias
  explicitly relative.
- scripts/generators/src/create-package/plop-templates-node/tsconfig.json:
  newly generated direct-Node packages now use the modern paired
  `module`/`moduleResolution: NodeNext` setup (matching the existing
  @tsconfig/node20-based direct-Node packages in this repo) and no
  longer set `baseUrl`.

Project-level package tsconfigs are intentionally left untouched here
(Task 4).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Task 4 of the TypeScript 6 migration: audit and migrate every remaining
repository-owned project tsconfig, plus the build tooling that injects
deprecated compiler options into `tsc` invocations.

Classification & module resolution (73 configs)
- browser / package-library / bundler- or Jest-transformed / fixture /
  declaration projects -> `moduleResolution: bundler` (valid with
  `module: commonjs` in TS 6): all v8 packages and legacy apps, the 23
  `scripts/*/tsconfig.spec.json` Jest configs, react-integration-tester
  spec config, codemods mock projects, eslint-rules and build-executor
  fixtures.
- code executed directly by modern Node -> paired `module: NodeNext` +
  `moduleResolution: NodeNext`: `apps/ssr-tests` (mocha on Node 22) and
  the two `ts-minbar-test-*` harnesses. The `scripts/*` packages already
  inherit node16 resolution from `@tsconfig/node20`.

baseUrl removal (34 configs + 9 tooling call sites)
- Dropped every remaining `baseUrl`. All five affected configs that also
  declare `paths` already used targets relative to their own tsconfig, so
  alias resolution is unchanged under TS 6 `pathsBasePath` semantics.
- `--baseUrl <dir>` was also used by build tooling as a hack to "turn off
  path aliases". TS 6 errors on it (TS5101) and it cannot be expressed via
  CLI flags anymore, so a shared `createTsConfigWithoutPathAliases()`
  helper now writes a transient `{"extends": <config>, "paths": null}`
  config for the single `tsc` run and removes it afterwards. Applied to
  the v9 `type-check`/`generate-api` executors, the v8 `ts`/`type-check`/
  `generate-api` just-scripts tasks and the web-components
  `type-check`/`compile`/`generate-ssr` scripts. Verified byte-identical
  declaration output vs. the old `--baseUrl` hack. The API Extractor
  override configs already set `paths: undefined`, so their redundant
  `baseUrl: '.'` was simply removed. Also removed the four
  `tsc -p . --baseUrl .` flags from app `type-check` scripts.

Explicit rootDir (62 configs)
- TS 6 no longer infers the common source directory for emitting projects
  and errors with TS5011. Left unfixed, v8 packages and `scripts/*` would
  emit to `<outDir>/src/**` instead of `<outDir>/**`. Added the reported
  `rootDir: "./src"` which restores the pre-TS6 (and published) layout -
  verified by building `set-version` and `keyboard-key`.

ES5 targets
- `apps/ssr-tests` (SSR) and `apps/perf-test` (performance app) move to
  modern targets; ssr-tests simply inherits `@tsconfig/node20`'s ES2022
  and is `noEmit`, so nothing is emitted differently.
- `react-portal-compat-context` still ships ES5 but downlevels through its
  own `.swcrc` (`jsc.target: es5`), so type-checking at ES2015 changes no
  published JS. Its `lib` stays ES5 so the source keeps to the ES5 API
  surface.
- The remaining v8 packages/apps move from `target: es5` to `es2015`
  (lowest modern target) because TS 6 hard-errors on ES5; `lib` arrays are
  intentionally untouched so the type surface - and therefore the source -
  is unchanged.

Other TS 6 deprecations
- `downlevelIteration` (api-docs), `esModuleInterop: false`
  (chart-docsite) and redundant `DOM.Iterable` (web-components, vite
  starter template) removed. `types: []` inheritance audited across all
  732 configs - every config that relies on ambient globals already
  declares them, so no `types` entries were added.
- The StackBlitz/CodeSandbox tsconfig generators now emit
  `moduleResolution: bundler` and drop `DOM.Iterable`; snapshots updated.

Tooling compatibility (no Nx patching, no `ignoreDeprecations`)
- Added `@swc-node/register@1.9.2` (the version `@nx/js` itself installs)
  as a root devDependency so Nx 21.6.10 stops falling back to ts-node,
  whose hardcoded `moduleResolution: NodeJs` failed with TS5107 before any
  repo target could run. `@swc-node/core` is pinned to `1.13.3` via
  `resolutions` to keep its `@swc/core >= 1.4.13` peer satisfied by the
  repo's `@swc/core@1.11.24`. Lockfile entries use canonical
  registry.yarnpkg.com URLs with authentic sha512 integrity, cross-checked
  against npm-signed registry metadata and recomputed from the tarballs.
- `patches/@swc-node+register+1.9.2.patch`: `@swc-node/register` maps
  tsconfig `baseUrl` to `jsc.baseUrl` but ignores `pathsBasePath`, so SWC
  panics on the now baseUrl-less workspace config whenever `paths` are
  present. The patch implements TS 6 semantics (fall back to
  `pathsBasePath`), matching what Nx 23 does upstream.
- `patches/just-task+1.5.0.patch`: `just-task` hardcodes
  `moduleResolution: 'node'` when registering ts-node for `just.config.ts`,
  which broke every v8 just-scripts target under TS 6.
- `tools/eslint-rules` `base-hook-no-forbidden-runtime` now falls back to
  `ts.resolveModuleName` because TS 6 does not populate the program level
  resolution cache under `moduleResolution: bundler`, which silently
  disabled the rule.

Intentional exceptions (documented)
- `apps/ts-minbar-test-react/files/tsconfig.json` (`target: es5`) and
  `apps/ts-minbar-test-react-components/files/tsconfig.json`
  (`moduleResolution: node`) are consumer scaffolds compiled by
  TypeScript 5.4 - the minimum version this repo supports for consumers -
  inside a temp project, never by the repo compiler. Migrating them would
  defeat the purpose of the minimum-bar test.

Validation
- `nx run-many -t test -p workspace-plugin,eslint-rules,eslint-plugin,
  codemods,scripts-tasks,react-integration-tester,
  react-storybook-addon-export-to-sandbox` - all green (241 + 85 + 85 +
  35 + 10 + 37 + 50 tests).
- `nx run-many -t lint -p workspace-plugin,scripts-tasks,web-components,
  chart-web-components` - green.
- `nx run keyboard-keys:type-check`, `nx run keyboard-keys:build`,
  `nx run set-version:build`, `nx run keyboard-key:build` - green, with
  the v8 output layout unchanged (`lib/index.js`, `lib-commonjs/index.js`).
- `packages/web-components` `type-check` and `apps/ssr-tests` type-check
  run to completion with zero TS5xxx config diagnostics.
- Repo-wide program-level sweep of all 732 tsconfigs reports no
  compiler-option diagnostics outside the two documented minbar fixtures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ed tsc options

Task 4 left the shared v8 build pipeline unbuildable under TypeScript 6:
`scripts/tasks/src/ts.ts` still invoked `tsc` with `target: es5` +
`module: amd` for `ts:amd`, which TS 6 rejects with TS5107 (both options
removed) and - because v8 packages now use `moduleResolution: bundler` -
additionally with TS5095. On top of that, moving the v8 tsconfigs from
`target: es5` to `es2015` silently changed `lib`/`lib-commonjs` of every
v8 package from the ES5 they have always published to ES2015, since no
build stage downleveled them afterwards (`ts:postprocess` is a no-op and
`babel:postprocess` only touches `*.styles.js` of packages with a
`.babelrc.json` - `packages/tokens` is the only one).

The published v8 artifact contract (ES5 `lib`, `lib-commonjs` and AMD
`lib-amd` incl. declarations and source maps) is now preserved by moving
the ES5/AMD emit out of the compiler, which is exactly the split the
converged packages that ship AMD (`@fluentui/react-portal-compat*`)
already use - `tsc` type checks and emits declarations + modern JS, SWC
emits the legacy JS. No `ignoreDeprecations`, no TypeScript downgrade and
no change to `moduleResolution: bundler`.

Shared tasks
- `scripts/tasks/src/swc/transpile.ts`: new `transpileEmittedJs()` which
  runs SWC over already emitted `.js` files - it downlevels to a given
  target and either keeps (`es6`/`commonjs`) or converts (`amd`) the
  module format. `tsc` source maps are chained via `inputSourceMap`, so
  the final `.map` still points at the original `.ts` (verified:
  `sources: ["<file>.ts"]`, `sourceRoot: "../src/"`, `sourcesContent`),
  and the `//# sourceMappingURL` comment is re-appended at the end of the
  file so it doesn't end up inside the AMD module factory. `.swcrc`
  lookup is disabled - the config is fully owned by this task - and
  helpers are inlined, because v8 packages ship `tslib` and SWC can only
  emit `@swc/helpers` imports, which would mean adding a runtime
  dependency to ~40 published packages.
- `ts.downlevel` (`ts:downlevel`): downlevels `lib` and `lib-commonjs` in
  place to ES5. Runs for every package where `shipsAMD()` is true (all 42
  projects built by the v8 preset), honours `--module` and skips module
  outputs that were not compiled.
- `ts.amd` (`ts:amd`): builds `lib-amd` from the emitted ESM output and
  copies `**/*.d.ts` from `lib` (declarations are module format agnostic
  - the previous tsc AMD run emitted byte identical ones). Still followed
  by `postprocess:amd`, which keeps stripping `process.env` checks from
  the SWC output.

Sequencing
- `ts:compile` now only runs the compiler (`ts:commonjs`, `ts:esm`) and
  the new `ts:transpile` stage runs the post compilation transforms after
  `copy-compiled`, because that is what materializes `lib`/`lib-commonjs`
  for packages compiling into `dist/out-tsc` (`chart-utilities`).
  `ts:amd` keeps its previous condition (production, non converged).

Tests (`nx run scripts-tasks:test` - 31 tests, was 10)
- `swc/transpile.spec.ts`: module format preservation, AMD conversion,
  ES5 output asserted through the TS parser (no arrow/class/template/
  let-const/destructuring nodes), source map chaining and the actionable
  error when there is no compiler output.
- `ts.spec.ts`: `tscTask` is no longer configured with the removed
  `target: es5`/`module: amd`, downlevel/amd behaviour incl. declaration
  copying and `--module` handling.
- `presets.spec.ts`: new preset sequencing suite (`ts`, `ts:compile`,
  `ts:transpile`, `ts:amd`, `build:react`) for production, non
  production, converged and `--module` variants.

Verification
- `nx run scripts-tasks:test` and `nx run scripts-tasks:lint` (0 errors)
  green; `tsc -p scripts/tasks/tsconfig.lib.json --noEmit` reports only
  the two pre-existing errors in `storybook.ts`/`webpack.ts`.
- Production builds (`FLUENT_PROD_BUILD=1`) of `keyboard-key`,
  `dom-utilities`, `merge-styles`, `react-window-provider`, `theme`,
  `style-utilities`, `react-icon-provider`, `utilities` (166 files) and
  `react-icons-mdl2` (1744 files): every `lib`, `lib-commonjs` and
  `lib-amd` file is ES5 clean (TS parser sweep), keeps its module format
  (all `lib-amd` files are `define(...)` wrapped), ships declarations and
  chained source maps; `dist` bundles of the `ships-bundle`/`ships-umd`
  packages stay ES5. `lib-amd` + `lib-commonjs` of `keyboard-key` were
  additionally executed (AMD loader shim / `require`) and behave
  identically.
- Non production builds keep skipping AMD, as before.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Follow up on the project-level TypeScript 6 migration, fixing the blocking
and important quality findings of the Task 4 review.

C1 - preserve the pre-TS6 published ECMAScript baseline
- `ts:downlevel` was gated by `shipsAMD()`, which is unrelated to the ES5
  baseline: it downleveled modern packages (`react-icons-mdl2(-branded)`,
  `react-icon-provider`, `chart-utilities`, `storybook`) while packages
  built via `build:node-lib` (`api-docs`, `jest-serializer-merge-styles`,
  `test-utilities`, `webpack-utilities`) and `monaco-editor` silently lost
  their ES5 emit.
- Introduced the explicit `ships-es5` project tag (same mechanism as the
  existing `ships-amd`/`ships-bundle`/`ships-umd` tags) carried by the 36
  projects whose tsconfig declared `target: es5` before the migration, and
  gate `ts:downlevel` on `metadata.shipsES5()` only. `build:node-lib` and
  the custom `monaco-editor` `ts` task run it as well.
- `lib-amd` keeps its pre-TS6 shape: it was always emitted by a dedicated
  `tsc --target es5 --module amd` run which overrode the package target, so
  the SWC AMD transform stays on ES5 for every package which ships it -
  verified for `react-icons-mdl2` (ES2019 `lib`, ES5 `lib-amd`).

C2 - verify the published artifacts, not only the file list
- Extracted the AST syntax check into `scripts/tasks/src/ecma-syntax.ts`
  (`findSyntaxAboveTarget`, `detectModuleShape`), reused by the SWC specs
  and by `verify-packaging`.
- `verify-packaging` now asserts, for `lib`/`lib-commonjs`/`lib-amd`: the
  expected ECMAScript baseline (`ships-es5` -> ES5, otherwise the resolved
  tsconfig target, `lib-amd` -> ES5), the module shape (ESM/CJS/AMD
  wrapping), declaration counterpart coverage, and `lib-amd` <-> `lib`
  parity. Whole-file-list checks are cheap; only an evenly spread sample of
  40 files per folder is parsed to keep CI fast.

I1 - stop inlining SWC helpers into every emitted file
- `externalHelpers: true` for the downlevel/AMD transform; `@swc/helpers`
  is declared as a runtime dependency by all 36 affected published packages
  (with change files), which is the contract converged SWC compiled
  packages already ship.
- `@swc/helpers` had to move from 0.5.1 to 0.5.23 (within the `^0.5.1`
  range every package already declares): 0.5.1 predates `_call_super`,
  which `@swc/core@1.11.24` emits for downleveled classes, so bundling
  `merge-styles` failed with an unresolved subpath export.
- Measured on `@fluentui/utilities`: `lib` 565 -> 505 KB (281 -> 24 inlined
  helper definitions), `lib-commonjs` 644 -> 586 KB, `lib-amd` 730 -> 601 KB.

I2 - restore the alias disabled type-check of apps
- Added the `type-check-project` wrapper (`scripts/tasks/bin`), which runs
  `tsc` through the transient "no path aliases" config, and pointed
  `public-docsite`, `vr-tests`, `vr-tests-react-components` and
  `vr-tests-web-components` at it. Behavioural test included; verified that
  plain `tsc -p .` type checks workspace *sources* while the wrapper
  resolves to built declarations.

I3 - Node executed jest/ts-jest configs
- The 23 `scripts/*/tsconfig.spec.json` use paired `NodeNext` now (matching
  `@tsconfig/node20`), no new diagnostics.
- `tools/react-integration-tester/tsconfig.spec.json` keeps `commonjs` +
  `bundler` (both valid and non deprecated in TS 6) because jest resolves
  the extensionless dynamic imports of its tests - documented in the config.
- `scripts/ts-node/src/register.js` passed no compiler options, so ts-node
  fell back to `moduleResolution: node10` and TS 6 rejected it (TS5107),
  breaking `yarn start`/`yarn generate` and the `scripts-executors` tests.

I4 - cache module resolution in the eslint rule fallback
- `base-hook-no-forbidden-runtime` reuses one `ts.ModuleResolutionCache`
  per program instead of resolving every import from scratch.

I5 - harden the transient tsconfig helpers (4 aligned implementations)
- Unique file name per process/invocation, explicit cleanup in
  `scripts/tasks/src/ts.ts` (also on failure) instead of relying on the exit
  hook, one process listener per module (plus SIGINT/SIGTERM which re-raise),
  and an actionable error when the config to extend does not exist.

I6 - idempotent, prunable SWC output
- The transform keeps a content addressed manifest in the project's
  `node_modules/.cache`, so already transpiled output is never transpiled
  (nor its source maps re-chained) again, partial invocations only process
  what changed, and `lib-amd` is treated as a derived directory whose stale
  files (js/map/d.ts) are pruned.

I7 - document/test the `esModuleInterop` behaviour change
- `docs/architecture/v8-published-artifacts.md` documents the artifact
  contract, the new `@swc/helpers` dependency and the CJS/AMD interop change
  TS 6 forces (`esModuleInterop` defaults to true and cannot be turned off).
- `scripts/tasks/src/swc/interop.spec.ts` executes the emitted AMD module in
  a minimal AMD loader and asserts the namespace/default interop shape and
  module identity.

I8 - harden the `@swc-node/register` patch
- Fails with an actionable message when `paths` are declared but neither
  `baseUrl` nor `pathsBasePath` is available, instead of silently resolving
  aliases against the cwd. Regression tests in
  `scripts/package-manager/src/patches.spec.ts`, and `patches/README.md`
  documents both patches and the `@swc-node/core` resolution pin.

Also fixes two `rootDir` regressions from the previous commit
(`font-icons-mdl2`, `react-file-type-icons` failed with TS5011).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
C1/I1 follow-up - fully close the "@swc/helpers" floor gap:
- Bump every repository declaration of "@swc/helpers" from "^0.5.1"/"~0.5.1"
  to "^0.5.23" (132 package.json files across packages/, packages/react-components/,
  and tools/) so the declared lower bound actually includes the version that
  provides "_call_super" - the previous "^0.5.1" floor was technically still
  satisfied by a version that predates it, only the root devDependency pin
  (already "0.5.23") masked that in this workspace's hoisted install. Root
  package.json is intentionally left as its pre-existing exact "0.5.23" pin,
  which syncpack.config.js already exempts from the workspace-wide semver
  group.
- Declare "@swc/helpers": "^0.5.23" as a runtime dependency on the 5 private
  ES5/AMD producers that emit external helper imports without declaring it:
  packages/storybook, packages/react-examples, apps/public-docsite-resources,
  apps/public-docsite and apps/theming-designer (all non-converged v8
  packages built through the default `ts` pipeline, so they emit "lib-amd"
  in production and/or run the ES5 downlevel via their `ships-es5` tag).
  Audited every other `ships-es5`/`ships-amd` producer and every private v8
  package's build task: `packages/codemods`, `packages/common-styles`,
  `apps/perf-test`, `apps/vr-tests`, `apps/ssr-tests` and `apps/rit-tests-v8`
  either never run the SWC downlevel/AMD transform (`build:node-lib` without
  the `ships-es5` tag) or are explicitly excluded from the production v8
  build in `azure-pipelines.release.yml`, so they don't need the dependency.

C2 follow-up - `verifyRuntimeHelpers` (scripts/tasks/src/verify-packaging.ts)
now validates the package's *declared dependency range*, not only the
hoisted copy that happens to be installed:
- it still resolves every imported helper specifier against the installed
  copy (existing behaviour, unchanged),
- it now also asserts the package declares "@swc/helpers" at all once a
  helper import is found, and
- it computes the declared range's lower bound with `semver.minVersion` and
  asserts it is `>=` the version that was just verified to provide every
  imported helper - so a future regression of the range back to "^0.5.1"
  fails verification even though some other package in the workspace still
  pins a newer copy and keeps it physically resolved/hoisted.
- Added 3 new scripts/tasks/src/verify-packaging.spec.ts cases (missing
  dependency declaration, range regression, accepted range) and extended
  `setup()` with a `dependencies` option. Manually verified against a real
  package (`@fluentui/utilities`, production build): passes as-is, fails
  with the added assertion message when its declared range is edited back
  to "^0.5.1", passes again once reverted.

Sandbox scaffold - packages/react-components/react-storybook-addon-export-to-sandbox
bumped the generated sandbox's "typescript" devDependency from "~4.7.0" to
"~6.0.0": the scaffolded tsconfig already sets `moduleResolution: "bundler"`,
which TypeScript 4.7 does not support. Updated the 5 inline snapshots in
sandbox-scaffold.spec.ts to match.

Dependency bookkeeping:
- yarn.lock regenerated (`yarn install --offline`) for the range bump; fixed
  up the single re-resolved "@swc/helpers" entry back to the canonical
  "registry.yarnpkg.com" URL and authentic sha512 integrity (this sandbox's
  registry mirror re-resolves through an internal Azure DevOps feed with
  sha1 hashes, same issue fixed for other packages by 179179d) -
  cross-checked shasum/integrity against the r.cnpmjs.org registry mirror.
- Added 88 beachball change files (87 for the "@swc/helpers" range bump on
  published react-components/v8 packages that don't already have a pending
  change file for this branch, plus 1 for the sandbox scaffold TypeScript
  bump); the 36 packages already covered by an existing pending "add
  @swc/helpers" change file were left alone to avoid duplicates. The 5
  producers that gained the dependency for the first time are all private,
  so beachball does not require a change file for them.
- `yarn syncpack list-mismatches`, `yarn check:installed-dependencies-versions`
  and `yarn install --frozen-lockfile` all pass.

Verified: `nx run scripts-tasks:test`, `nx run scripts-tasks:lint`,
`nx run react-storybook-addon-export-to-sandbox:test` all pass; `yarn
check:change` passes for every package touched by this commit (the
pre-existing `@fluentui/chart-web-components`/`@fluentui/codemods`/
`@fluentui/web-components` gaps predate this change and are unrelated
Task 5 work, left untouched per scope).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- @fluentui/codemods: @swc/helpers dependency
- @fluentui/chart-web-components: @swc/helpers dependency
- @fluentui/web-components: @swc/helpers dependency

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Private packages (@fluentui/eslint-plugin, @fluentui/theme-designer) should not have change files.
…nges

Packages with disallowedChangeTypes [major, minor, patch] must use
'none' or 'prerelease' change types. Updated the following packages:
- @fluentui/tokens
- @fluentui/react-theme-sass
- @fluentui/babel-preset-global-context
- @fluentui/global-context
- @fluentui/react-alert
- @fluentui/react-infobutton
- @fluentui/react-virtualizer

This allows yarn check:change to pass without errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add the ambient find-free-port declaration to
  scripts/tasks/tsconfig.spec.json (mirroring tsconfig.lib.json) so
  tsc -p scripts/tasks/tsconfig.spec.json --noEmit no longer reports
  TS7016 for presets.spec.ts's transitive import of webpack.ts.
- Fix verifyRuntimeHelpers in verify-packaging.ts: use
  semver.satisfies(resolvedVersion, declaredRange) instead of requiring
  the declared range's floor to be >= the resolved version, so a
  correctly-widening range like ^0.5.1 is accepted when the resolved
  helper is 0.5.23, while a range that truly excludes the resolved
  version (eg ^0.4.0) is still caught. Updates error text and tests
  accordingly so future compatible @swc/helpers bumps don't break every
  package.
- Correct patches/README.md and the @swc-node/register patch comments:
  @swc-node/register is only an optional peer dependency of nx, not
  something @nx/js installs. This branch adds it directly to
  devDependencies to make Nx pick SWC transpile-only execution over its
  TS6-incompatible ts-node fallback. The removal criterion is now
  actionable: bump/remove the pin and patch once an unpatched
  @swc-node/register release maps pathsBasePath upstream.
- Fix the import-order lint warning in
  scripts/tasks/src/swc/transpile.spec.ts (parent vs sibling import
  groups need a blank line between them).
- Robustness: make withCleanup recognize any thenable, not just native
  Promise; make the type-check-project CLI reject -p/--project without
  a value instead of silently falling back to the default project; add
  direct test coverage (incl. node --check) for
  scripts/tasks/bin/type-check-project.js.

Verified: scripts-tasks test/lint/type-check (TS7016 gone; only the
pre-existing unrelated storybook.ts TS2578 and deferred Task 5
eslint-plugin-react-components API Extractor failures remain),
scripts-package-manager test/lint, workspace-plugin test/lint (244
tests, 37 suites).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…essions

Compiler and DOM/lib regressions:

- pin `strict: false` in the v8/legacy tsconfig family: TypeScript 6 flipped the
  `strict` default to `true`, which silently turned on `strictFunctionTypes`,
  `strictPropertyInitialization` and friends for maintenance-only v8 packages
  (`@fluentui/react` alone reported 400 new errors). The configs already opted
  into `strictNullChecks`/`noImplicitAny` explicitly, so the previous contract is
  now written down instead of inherited from a compiler default.
- drop the obsolete `@ts-expect-error` directives around `style.anchorName` /
  `style.positionAnchor` in web-components `tooltip`; TypeScript 6 ships those
  properties in `lib.dom.d.ts`.
- add `overallProgress` to the react-motion-components-preview `Animation` mock,
  which the updated DOM lib now requires.
- keep the deprecated `document.createEvent` fallback in `@fluentui/utilities`
  `EventGroup.raise`, which the updated DOM lib now flags, behind the same
  targeted `@typescript-eslint/no-deprecated` suppression the adjacent
  `initEvent` call already uses.
- type the `date-time-utilities` `toLocaleTimeString` mock with
  `Intl.LocalesArgument` instead of `string | string[]`.
- make the `getMetadata` `_options` parameter in `scripts/storybook` optional -
  TypeScript 6 no longer treats an inline JSDoc-typed trailing parameter as
  optional.
- remove the now unused `@ts-expect-error` on the `storybook/internal/core-server`
  import in `scripts/tasks`.
- declare `*.css` in `typings/static-assets` and request the `static-assets`
  ambient types in the web-components/chart-web-components storybook configs, so
  side-effect stylesheet imports resolve under `noUncheckedSideEffectImports`.
- resolve `markdown-to-jsx` types through its `import` condition in
  react-docsite-components; its `require` declarations omit `MarkdownToJSX`.
- make `useDebounce` generic in public-docsite-v9 and drop the now provably dead
  `platform === 'web'` comparison in the public-docsite template page, which
  TypeScript 6 narrows out of the `default` switch branch.
- import `SlotClassNames`/`JSXElement`/`TagAppearance`/`TagSize` from the
  `@fluentui/react-utilities` and `@fluentui/react-tags` package entries instead
  of their internal `/src/index` paths, which modern module resolution rejects.
- replace the remaining `React.RefAttributes` usages in react-charts and
  react-calendar-compat with `RefAttributes` from `@fluentui/react-utilities`,
  which typescript-eslint 8.64 now reports through the workspace rule.

Declaration/API Extractor regressions:

- API Extractor 7.51.0 classifies relative `import('./module')` types in
  declaration output as external packages whenever a modern `moduleResolution`
  is used, which produced `.d.ts` rollups importing unpublished modules
  (react-breadcrumb, react-field, react-overflow,
  react-headless-components-preview) and failed `react-components:build`.
  The affected exports are now annotated with statically imported types.
- `@fluentui/eslint-plugin-react-components` exports `RuleOptions` from the entry
  point and gives the flat config `plugins` map an explicit index signature, so
  TypeScript 6 stops emitting an unusable `[name]` computed key plus a dangling
  `../package.json` import.
- guard the regression class: `api-extractor` now fails the build when a rollup
  contains relative imports, covered by `scripts/tasks/src/api-extractor.spec.ts`.

Test/transform regressions:

- resolve `compilerOptions.paths` in the SSR esbuild plugin through TypeScript's
  `pathsBasePath` instead of the removed `baseUrl`; 61 of 67 `test-ssr` targets
  were failing to bundle.
- request `types: ["node"]` in the tool tsconfigs that ts-node uses to load
  `jest.config.ts`, since TypeScript 6 defaults `types` to `[]`.
- spy on the underlying CommonJS `react` module in `foundation-legacy` slots
  tests: TypeScript 6 forces `esModuleInterop`, so the namespace object is now
  built from non-configurable accessors.
- hoist `tickValues` above its use in the react-charting LineChart RTL tests; the
  previous ES5 output hoisted the binding as `var`, which hid the temporal dead
  zone violation.
- exclude the lint-only `@fluentui/eslint-plugin` edge from the three
  `@nx/js:swc` tool builds, matching `workspace-plugin`. Nx rewrites dependency
  path mappings to workspace-relative paths, which TypeScript 6 rejects without
  `baseUrl`.

API reports are updated for the intentional declaration changes only: canonical
`@typescript-eslint/utils/ts-eslint` import paths, the `RuleModule & { name }`
intersection from typescript-eslint 8.64, the preserved
`<Value = AccordionItemValue>` generic default, the alias-preserving provider and
forward-ref annotations, and the `RefAttributes` swap.
…parsing

Declaration rollup guard:

- move the self contained `.d.ts` rollup guard into a dedicated `dts-rollup`
  module and wire it into `tools/workspace-plugin/src/executors/generate-api`,
  which is the path all 93 vNext packages use. The guard previously only ran on
  the legacy `just-scripts` task, so the regression class it was added for was
  unguarded for v9.
- the executor now checks every rollup variant api-extractor emitted
  (untrimmed + public/beta/alpha trimmed) after each `Extractor.invoke`,
  including the programmatic configs produced by `createSubpathEntryConfig` for
  export subpath entries. A `Set` of already scanned paths is threaded through a
  single run so a rollup emitted by more than one entry point config is parsed
  only once.
- the guard runs after the actionable api-extractor diagnostics are surfaced, in
  both the executor and the legacy task, so a broken rollup can no longer mask
  the api-extractor error output.
- replace the line based regular expression with TypeScript AST scanning. Import
  declarations, export declarations, `import x = require()` and `ImportTypeNode`
  (inline `import('./types').Foo`, exported or not, anywhere in a declaration)
  are all detected, while string literal *types* such as `'.2f'`, `'.'` or
  `'../not-an-import'` are no longer false positives.
- `tools/workspace-plugin` must not depend on any project in the monorepo
  (`scripts/check-dep-graph.js`), so the module is duplicated byte identical in
  `scripts/tasks/src/dts-rollup.ts`; the spec next to each copy contains the same
  test matrix plus a parity assertion that fails if the two ever diverge.

SSR tsconfig parsing:

- `tsConfigPathsPlugin` now inspects `parsedConfig.errors` and fails with
  `ts.formatDiagnostics` output before any path alias is used. Previously an
  invalid `extends` or an unknown compiler option produced an empty `paths` map
  and the bundle silently fell back to `node_modules` resolution.
- resolve mappings against `baseUrl ?? pathsBasePath`, mirroring TypeScript's own
  `getPathsBasePath`, and fail with an actionable message when neither is
  available instead of guessing the config directory.
- add `esbuild-plugin.test.ts` with a fixture that mirrors the repository
  topology (child config extending a base config that declares `paths`, no
  `baseUrl`, alias target outside the child config directory) plus
  invalid-extends and invalid-option cases proving parse errors throw.

Cleanups:

- drop the unused root `tsconfig-paths` dependency - the SSR plugin was its only
  consumer and now uses the TypeScript API; `tsconfig-paths-webpack-plugin` keeps
  it as a transitive dependency.
- use type only imports for `ApiExtractorOptions`/`TaskFunction` in
  `scripts/tasks/src/api-extractor.ts`.
- explain why `Overflow`/`Provider` keep their inferred
  `ForwardRefExoticComponent` shape instead of `ForwardRefComponent`.
- drop the stale "classNames that may be falsey" note in the public-docsite
  template page, which no longer passes a falsey argument to `css()`.

`tools/visual-regression-utilities` intentionally does not get the
`!eslint-plugin` implicit dependency exclusion: unlike the three tools that did,
it has no `@nx/js:swc` build target, so the lint only edge never reaches the
dependency path mapping rewrite that motivated the exclusion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- api-extractor.ts (scripts/tasks): only run the self contained dts
  rollup guard when API Extractor succeeded, matching the vNext
  generate-api executor so API Extractor diagnostics are never masked.
  Add api-extractor.spec.ts covering onResult wiring for the
  succeeded/failed ordering.
- dts-rollup.ts (both parity copies): expand the rollup guard to also
  flag absolute filesystem module specifiers and triple-slash
  'reference path' directives, while still ignoring package
  specifiers, node: builtins, URLs, and harmless string literal types.
  Update the parity spec accordingly.
- Move dts-rollup.spec.ts and esbuild-plugin.test.ts fixtures from
  source-tree __fixtures__ directories to os.tmpdir(), with cleanup in
  afterEach so interrupted runs cannot leave files behind.
- typings/static-assets/index.d.ts: give 'declare module *.css' an
  explicit empty body so side-effect imports still resolve but
  value/default/named imports no longer silently become any. Add a
  focused behavioural test (static-assets-css.spec.ts) that fails
  against the old shorthand declaration.
- react-docsite-components/tsconfig.json: replace the workspace-wide
  customConditions: ["import"] override with a narrowly scoped 'paths'
  mapping that only redirects markdown-to-jsx's type resolution to its
  import-condition declaration file (which declares the MarkdownToJSX
  namespace), instead of flipping export conditions for every
  dependency.
- Make the dts-rollup parity check cache-safe: scripts-tasks and
  workspace-plugin project.json now declare the other project's
  dts-rollup.ts/.spec.ts as explicit test target inputs, so changing
  either copy busts the other project's test cache. Verified with a
  content-change probe.
- esbuild-plugin.ts (scripts/test-ssr): filter parsedConfig.errors to
  Error category diagnostics only, since TypeScript can also return
  Warning/Suggestion category diagnostics in that array. Added tests
  covering both the non-error and still-fails-on-real-error cases.

Validated: scripts-tasks test/lint/type-check, workspace-plugin
generate-api/executor tests and full test suite, scripts-test-ssr
test/lint/type-check, react-docsite-components type-check/test/lint,
react-button generate-api (clean diff), react-charting build (legacy
api-extractor task), and 'yarn check:change' (no beachball change
files required - all touched packages are private except
react-docsite-components, whose tsconfig change is build-tooling only
and does not alter emitted declarations).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The repo toolchain now builds and tests on TypeScript 6.0.3, but the
published peer ranges of the tooling packages still described a
TypeScript 4-only (or unbounded) world:

- `@fluentui/react-conformance` and `@fluentui/react-conformance-griffel`
  declared `typescript: ^4.3.0`, which excludes every release after 4.9 -
  consumers on TS 5.x/6.x got an unmet peer warning even though the
  packages work.
- `@fluentui/eslint-plugin-react-components` declared `typescript: >= 5.0.0`
  with no upper bound, so it also advertised the unreleased TypeScript 7
  compiler.
- `@fluentui/eslint-plugin` (private) declared `typescript: ^5.0.0`, which
  the repo's own TypeScript 6.0.3 install no longer satisfies.

Ranges are widened, not replaced: the conformance packages keep their
4.3 floor (inherited from `react-docgen-typescript`'s own `>= 4.3.x`
peer) and now read `>=4.3.0 <7.0.0`; the ESLint plugins read
`>=5.0.0 <7.0.0`.

Verified by type-checking each package's published `dist/index.d.ts`
through a consumer-style program on TypeScript 4.9.5, 5.4.5, 5.7.3,
5.9.3 and 6.0.3 (with version-matched `typescript` declarations), and by
running the `@fluentui/react-conformance` jest suite - which drives the
compiler API at runtime - against each of those versions. The compiler
APIs used (`createProgram`, `createCompilerHost`, `findConfigFile`,
`parseJsonConfigFileContent`, `SyntaxKind`, node type guards) predate the
declared 4.3 floor and still exist in 6.0.3.

The `typescript-eslint: >= 8.46.2` peer is intentionally left alone:
typescript-eslint 8.46.2 itself declares `typescript: >=4.8.4 <6.0.0`, so
the TS6 + tseslint 8.46.2 combination is already excluded by composition
and raising our floor would only break working TS5 consumers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sions

Resolves the six failures found during final TypeScript 6 validation.

F1 - `NX_PREFER_TS_NODE: true` forced Nx onto its `ts-node` fallback, which
hardcodes `moduleResolution: node10` and therefore fails with TS5107 under
TypeScript 6. Removed from all seven GitHub workflows and from
`.devops/templates/variables.yml` so Nx uses the `@swc-node/register` path this
branch installs and tests.

F2/F4 - `@swc-node/register@1.9.2` falls back to the pre TypeScript 6 default of
`esModuleInterop: false` when the option is not spelled out, so every
`import x from '<commonjs module>'` inside the workspace TypeScript Nx executes
resolved to `undefined` (`path`, `semver`, `micromatch`). `tsconfig.base.json`
now declares `"esModuleInterop": true` explicitly, which is a no-op for the
compiler (it is the TypeScript 6 default) and restores the interop wrappers for
generators, executors and `verify-packaging`.

F3 - `apps/ts-minbar-test-react*/tsconfig.json` got an explicit `rootDir` of
`./src`, which the NodeNext migration made mandatory (TS5011).

F5 - `packages/react-examples/tsconfig.rit.json` sets
`resolvePackageJsonExports: false`, so the compatibility test keeps seeing the
built deep subpaths of `@fluentui/react-experiments` under `bundler` resolution
without broadening that package's public `exports`.

F6 - Cypress loads its config in a child process with its own bundled `ts-node`
pinned to node10, so no TypeScript file reachable from a `cypress.config.*` can
be compiled under TypeScript 6. The whole Node side is now CommonJS JavaScript
with `// @ts-check`: all 38 configs, the shared `@fluentui/scripts-cypress`
entry point and the RIT/generator templates. Path aliases are handed to the
bundler explicitly through the compiler reported `pathsBasePath`
(`scripts/cypress/src/ts-paths.js`) instead of the removed `baseUrl`. The RIT
generated tsconfig lost its `baseUrl` (TS5101) and defaults to `bundler`
resolution (TS5107) for the same reason.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…TS6 migration

- ts-paths.js: pass tsConfigPath as configFileName to parseJsonConfigFileContent
  so pathsBasePath is computed correctly through extends chains. Inspect and
  throw formatted Error-category diagnostics from parseJsonConfigFileContent
  instead of silently ignoring invalid extends targets/compiler options.
- Add ts-paths.test.js covering inherited paths with no baseUrl and parse
  errors (missing extends target, unknown/invalid compiler options, unreadable
  config, and Warning/Suggestion vs Error diagnostic filtering), analogous to
  the test-ssr esbuild-plugin helper tests.
- Route the new .test.js file through the Jest-typed tsconfig.spec.json
  project and exclude src/index.d.ts from it (its ambient Cypress globals
  collide with @types/jest globals); exclude .test.js/.spec.js from
  tsconfig.lib.json.
- Add explicit BaseConfig JSDoc annotation (from @fluentui/scripts-cypress) to
  packages/react-examples/cypress.config.js and the react-integration-tester
  cypress.config.js.template so @ts-check no longer reports a duplicate
  component identifier when spreading baseConfig. Update the RIT e2e inline
  snapshot to match.
- Remove the stale ./cypress.config.ts entry from eslint-plugin's
  configHelpers.js configFiles list (redundant with the existing ./*.js
  glob, and cypress.config.js is JS-only per docs/workflows/testing.md).
- Correct comments/docs that falsely claimed tsconfig-paths@4.2 skips configs
  without baseUrl; it only skips when no tsconfig/jsconfig is found at all,
  and otherwise anchors paths at the nearest config file rather than the one
  declaring them. Explain the real reason ts-paths.js uses TypeScript's own
  parser and pathsBasePath: to keep inherited paths mapping roots correct
  through extends chains under TS6 baseUrl deprecation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e, RIT alias fix, POSIX paths

1. scripts/cypress/src/ts-paths.js: parse tsconfig with a restricted
   ParseConfigHost (readDirectory -> []) instead of ts.sys, so resolving
   the workspace path-alias base directory no longer enumerates the
   entire monorepo (~710ms -> ~1.3ms). Filters the resulting TS18003
   "no inputs found" diagnostic, which is an expected artifact of the
   stubbed readDirectory rather than a real config error. Removed the
   unused "paths" field from WorkspacePathAliases (no caller consumed
   it) and updated JSDoc/tests accordingly, including a new regression
   test asserting no real filesystem walk occurs.

2. scripts/cypress/src/index.d.ts is now the single source of truth for
   BaseConfig; base.config.js's duplicate @typedef is replaced with an
   import of it from ./index.

3. registerTsPaths (scripts/storybook/src/utils.js) now accepts an
   optional explicit baseUrl, threaded to TsconfigPathsPlugin, so paths
   declared in an extended base tsconfig (pathsBasePath-relative under
   TS6) aren't silently anchored at the wrong directory. Backward
   compatible for all other callers. apps/rit-tests-v8/cypress.config.js
   now computes and passes that baseUrl via the newly exported
   readWorkspacePathAliases (re-exported from @fluentui/scripts-cypress's
   public entry). Added tests for registerTsPaths and the new public
   export.

4. tools/react-integration-tester/src/setup.ts: added toPosixPath() and
   applied it to every relative-path template value embedded in
   generated tsconfig/RIT project files, so Windows-style backslashes
   don't corrupt generated JSON/JS module specifiers. Updated
   setup.test.ts with unit tests and a Windows-path integration test.

5. Added an inline comment in packages/react-examples/tsconfig.rit.json
   explaining why resolvePackageJsonExports:false is required to keep
   @fluentui/react-experiments/lib/* deep-import compatibility tests
   working.

Validated: scripts-cypress test/type-check/lint, scripts-storybook
test/type-check/lint, react-integration-tester test/type-check/lint,
rit-tests-v8 cypress.config.js runtime load (TsconfigPathsPlugin.baseUrl
now resolves to the workspace root), and manual checkJs across
base.config.js, packages/react-examples/cypress.config.js, and
apps/rit-tests-v8/cypress.config.js.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mainframev
mainframev changed the base branch from master to benchmark/typescript-6-base July 28, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant