Skip to content

chore: migrate from TS path aliases to workspaces + project references#774

Draft
layershifter wants to merge 26 commits into
microsoft:mainfrom
layershifter:chore/workspace-migration
Draft

chore: migrate from TS path aliases to workspaces + project references#774
layershifter wants to merge 26 commits into
microsoft:mainfrom
layershifter:chore/workspace-migration

Conversation

@layershifter
Copy link
Copy Markdown
Member

Summary

  • Replace 36 TypeScript path aliases in tsconfig.base.json with Yarn workspaces + TypeScript project references
  • Switch moduleResolution to "bundler" with customConditions: ["@griffel/source"] for source-level resolution
  • Enable composite builds with cross-project references in tsconfig files
  • Register @nx/js/typescript plugin to infer type-check targets automatically
  • Standardize all tsconfig files (consistent extends, rootDir, remove redundant options)
  • Add workspace:* dependencies for internal @griffel/* packages
  • Fix Vitest SSR environment conditions, devtools webpack build, vite-plugin test aliases
  • Add tag-processor/tsconfig.build.json for CJS output (wyw-in-js runtime evaluation)

CI status: type-check (22), build (13), test (17 non-e2e), lint (17) all pass.
E2e tests need follow-up for npm pack compatibility with new output paths.

Test plan

  • nx run-many -t type-check — all 22 projects pass
  • nx run-many -t build — all 13 projects pass
  • nx run-many -t test (excluding e2e) — all 17 projects pass
  • nx run-many -t lint — all 17 projects pass
  • E2e tests — need package.json in dist/ for npm pack

🤖 Generated with Claude Code

@layershifter layershifter requested a review from a team as a code owner March 5, 2026 16:53
@layershifter layershifter force-pushed the chore/workspace-migration branch from 0e4468d to 953c290 Compare March 6, 2026 09:08
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 6, 2026

📊 Bundle size report

✅ No changes found

@layershifter layershifter force-pushed the chore/workspace-migration branch from 6c3e83b to 9bd737f Compare March 6, 2026 13:21
@layershifter layershifter reopened this Mar 6, 2026
@layershifter layershifter marked this pull request as draft March 6, 2026 15:48
@layershifter layershifter force-pushed the chore/workspace-migration branch from 9bd737f to e92975d Compare March 9, 2026 14:21
@layershifter layershifter force-pushed the chore/workspace-migration branch from e92975d to 905f100 Compare May 7, 2026 19:16
layershifter and others added 2 commits May 21, 2026 11:47
Squash-rebase of the earlier 4-commit migration onto current main.
The migration replaces TS path aliases with yarn workspaces + project
references and a custom `@griffel/source` exports condition, switches
tsconfig.base.json to `moduleResolution: bundler` with composite
builds, registers `@nx/js/typescript` for inferred type-check targets,
moves each package's build output to `packages/X/dist`, and dual-emits
ESM + CJS via `tools/build-cjs.mjs` for the three runtime-dual packages.

Versions and dep ranges follow current main (post-`10b7b8f3`); the
publishable packages' internal deps stay as `^X.Y.Z` so `npm pack`
survives without yarn's workspace-protocol rewriting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Switch CJS-with-issue packages (babel-preset/jest-serializer/
  webpack-extraction-plugin) build target to tsconfig.build.json so
  they actually emit ./dist/ (their tsconfig.lib.json is typecheck-only
  with emitDeclarationOnly).
- Move per-condition `types` from top-level into the `import`/`require`
  branches of core/react/shadow-dom exports so CJS consumers resolve
  to ./dist/cjs/index.d.cts under moduleResolution: node16/nodenext.
- Drop the beachball patch — the workspace-level dist/packages/X path
  it redirects to no longer exists with per-package dist/. Publish
  works from packages/X/ directly thanks to the per-package `files`
  field.
- Include virtual-loader/ in webpack-extraction-plugin's files, and
  point virtual-loader/index.js at ../dist/constants.js (was source TS).
- Pin @typescript-eslint/utils to a single version via resolutions
  (the nested @typescript-eslint/rule-tester copy caused
  RuleModule type identity mismatch in eslint-plugin tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@layershifter layershifter force-pushed the chore/workspace-migration branch from 905f100 to 863599d Compare May 21, 2026 10:23
layershifter and others added 5 commits May 21, 2026 12:28
Internal-only change (build pipeline + tsconfig restructuring); no
version bumps intended.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bump @typescript-eslint/utils to ^8.59.3 in root and packages/eslint-plugin/
  so the version ranges align with the resolution we already pin.
- Add version: 0.0.0 to apps/website/package.json so syncpack stops
  complaining about VERSION_IS_MISSING.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build outputs moved from `<repo>/dist/packages/X/src/` to
`<repo>/packages/X/dist/` as part of the workspace migration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the workaround that imported from `../../../dist/packages/core/src/index.js`
(broken anyway after the workspace migration moved build outputs).
Add `resolve.conditions: ['@griffel/source']` to the package's
vitest.config so the @griffel/source export condition resolves to
core's TS source under test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
webpackLoader.mts resolves its virtual-loader via path.resolve(__dirname,
'virtual-loader', 'index.cjs') — that path needs to exist next to the
compiled .mjs in dist/. tsc only emits .ts/.mts files, so the .cjs
loader was missing from dist/, breaking rspack's CSS extraction (no
griffel chunk → null .files crash in processAssets).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
layershifter and others added 10 commits May 21, 2026 18:59
…hook

- Move the README/LICENSE staging from a per-build run-commands step
  into beachball's per-package `prepublish` hook. Build no longer
  touches these files; only publish does.
- Drop tools/copy-pkg-assets.mjs and remove its invocation from every
  project.json build target.
- Stop copying package.json into dist/ — `beachball publish` (and the
  matching `npm pack`) runs from the package root, so dist/package.json
  was dead weight.
- Drop tools/deprecate-broken-release.mjs (one-off script no longer needed).
- Drop CHANGELOG.md from every published package.json `files` field.
- Drop the @griffel/* aliases from monosize.config.mjs — webpack now
  resolves them via the workspace symlink + exports map.
- Drop the nxViteTsPaths() plugin from every vitest.config — there are
  no tsconfig paths left for it to expose; resolve.conditions:
  ["@griffel/source"] handles workspace resolution.
- azure-pipelines.yml: add an explicit "Build all packages" step
  before the publish step, since the prepublish hook no longer triggers
  the build itself.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop noEmit and switch composite back to true so apps/website
participates in the workspace's tsc -b graph again. Reference it
from the root tsconfig.json so the @nx/js/typescript plugin can
infer a type-check target.

The Playground code/templates/* JS files are loaded as raw strings via
webpack require.context (not compiled), so exclude them from the
tsconfig include — they were tripping TS2742 ("inferred default type
not portable") when composite emit walked into @griffel/core's source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match the per-package dist/ layout the rest of the workspace is moving
to. Docusaurus now builds into apps/website/dist instead of
<repo>/dist/apps/website.

.github/workflows/deploy.yml: point the Pages artifact upload at the
new path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch the @griffel/* devDependencies from workspace:* to the exact
published versions (matching what's published on npm + the workspace's
@griffel/e2e-utils at "*" since it's a private workspace package).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a semverGroups rule so any semver-pinned @griffel/* internal dep
must use a caret. The "*" workspace marker (used by private packages
like @griffel/e2e-utils) is excluded via the specifierTypes filter.

Restore the carets on e2e/eslint's published @griffel/* devDeps
(@griffel/eslint-plugin, @griffel/react) which the rule now enforces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ript

Configure the @nx/js/typescript plugin to also infer a `build` target
(in addition to type-check) from each package's tsconfig.lib.json.
Drop the redundant nx:run-commands "build" from style-types/project.json
— the inferred target runs the same `tsc --build tsconfig.lib.json`.

The other published packages keep their explicit build targets for
now (build-cjs chains, asset copies, etc.); they migrate package by
package as the workspace migration progresses.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- packages/transform: drop explicit build target — @nx/js/typescript
  infers it from tsconfig.lib.json, identical to the explicit one.
- e2e/utils: collapse the single-target exports map to the short form
  ("./src/index.ts"). e2e-utils is a private workspace package; no
  consumer benefits from the explicit conditions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the explicit build target — @nx/js/typescript can now infer it.
Merge the two-config setup into one:

- Move the CJS build settings (module: node16, declaration: true,
  customConditions: [], outDir: ./dist) into tsconfig.lib.json itself.
- Delete tsconfig.build.json.

The customConditions: [] override (preserved from the old build
config) keeps the @griffel/source resolution disabled here so
typecheck resolves @griffel/core via its require condition →
.d.cts, avoiding the CJS-importing-ESM-source TS1479 error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Only e2e/utils uses .ts extensions in its imports; the other three
e2e packages just import bare module specifiers like @griffel/e2e-utils.
Drop the flag from their tsconfig.lib.json — it doesn't do anything
useful there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Leftover from an earlier iteration that had **/*.d.ts in the include
patterns (which picked up emitted declarations from out-tsc and dist).
That include is long gone; the exclude was guarding against nothing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
layershifter and others added 9 commits May 22, 2026 12:33
…ypescript

Same as transform / style-types — the inferred build runs the same
\`tsc --build tsconfig.lib.json\`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…kage.json

- Same simplification as babel-preset: merge the dual tsconfig.lib.json
  + tsconfig.build.json into a single tsconfig.lib.json (module: node16,
  customConditions: [], outDir ./dist). Delete tsconfig.build.json. Drop
  the explicit build target from project.json — @nx/js/typescript infers
  it now.
- Reorder package.json: metadata first, then entry points (main/types/
  exports), then files, then deps. Matches the order in core/react/
  shadow-dom.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build targets — drop now-redundant explicit build entries in
eslint-plugin, jest-serializer, postcss-syntax, webpack-loader project.json
files. The @nx/js/typescript plugin infers them from tsconfig.lib.json.

jest-serializer: collapse tsconfig.build.json into tsconfig.lib.json
(same pattern as babel-preset / webpack-extraction-plugin — CJS with
internal ESM imports, customConditions: [] sidesteps TS1479).

Storybook configs (devtools, react, shadow-dom): restore composite +
emit, drop noEmit, and reference them from the per-package tsconfig.json
so they participate in the tsc -b graph. Drop unused emitDecoratorMetadata
(would have required experimentalDecorators). Add jsx: react-jsx to
react's storybook config so .tsx stories type-check.

Package.json ordering (webpack-loader, eslint-plugin): metadata →
entry points → files → deps, matching webpack-extraction-plugin /
core / react.

webpack-plugin: drop the lib override on `lib` (defaults from target:
es2020 already include es2020 + dom). Drop the spec config's
target/lib override entirely — spec reaches lib code via project
reference, no need to restate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The override exists for BigInt literal support (used in GriffelPlugin's
build-time stats). \`esnext\` reads more clearly as "newer than the base
default", versus picking a specific year.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the build-time copy of src/virtual-loader/ into dist/virtual-loader/.
Move the source to packages/webpack-plugin/virtual-loader/ (one dir up
from src/) and have `npm pack` ship it via the `files` field, matching
how webpack-extraction-plugin already handles its virtual-loader.

- Move src/virtual-loader/index.cjs → virtual-loader/index.cjs.
- webpackLoader.mts: resolve virtualLoaderPath via ../virtual-loader/
  (the compiled .mjs in dist/ is one level below virtual-loader/).
- package.json files: add "virtual-loader/".
- project.json: drop the explicit build target entirely — the
  @nx/js/typescript-inferred build does the right thing now that there's
  no copy step to chain.
- Update 15 __fixtures__/<name>/output.ts snapshots to reflect the new
  relative path from each fixture to the loader.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n virtual-loader

Match the simpler pattern used by webpack-plugin's virtual-loader:
compute the global symbol inline via Symbol.for() instead of pulling
it in from ../dist/constants. Drops the cross-file dependency and the
awkward pre-build IDE state (where ../dist didn't exist yet).

The JSDoc still references ../src/constants for the
SupplementedLoaderContext type, which exists in workspace dev (source
TS) for tooling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d-cjs

Drop the `_build_tsc` placeholder target. Rename the previous
nx:run-commands "build" (which only invoked tools/build-cjs.mjs) to
"build-cjs". Add a plain "build" target that runs tsc directly. The
@nx/js/typescript plugin can't infer "build" for these three packages
because their nested exports.require.default points outside the lib
config's outDir; the explicit target keeps things simple.

nx.json: targetDefaults wire the chain:
  - build       dependsOn ^build, ^build-cjs
  - build-cjs   dependsOn build, ^build-cjs

That way `nx run X:build-cjs` builds tsc first then the CJS step, and
any consumer's "build" waits for its deps' tsc + cjs outputs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`main` is the fallback for bare `require('@griffel/core')` calls in
older tooling that doesn't honor the exports map. With type: module
+ main: ./dist/index.js, require() would resolve to the ESM build —
fine on Node 22+ via require(esm), but breaks any consumer still on
an older Node or build tool. Restore the CJS path so the fallback
matches the require condition in exports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Convert tools/build-cjs.mjs → tools/build-cjs.ts. Node 24+ runs .ts
  natively (same approach the e2e packages use). Drops the awkward
  \`@type {const}\` JSDoc in favor of real types — SWC_OPTIONS is now
  typed as \`Options\` from @swc/core, and every helper has proper
  parameter/return types.
- Drop the redundant \`!absInput.endsWith('.d.cts')\` guard. A filename
  ending in .d.cts can't also end in .d.ts (the suffixes are literally
  different), so the check was dead.
- Update build-cjs commands in core/react/shadow-dom project.json to
  reference tools/build-cjs.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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