Skip to content

chore(core)!: build @nx/workspace to local dist and use nodenext#35643

Merged
FrozenPandaz merged 5 commits into
masterfrom
workspace-dist-migration
May 13, 2026
Merged

chore(core)!: build @nx/workspace to local dist and use nodenext#35643
FrozenPandaz merged 5 commits into
masterfrom
workspace-dist-migration

Conversation

@FrozenPandaz
Copy link
Copy Markdown
Contributor

@FrozenPandaz FrozenPandaz commented May 11, 2026

Current Behavior

@nx/workspace builds to the shared workspace-root dist/packages/workspace/ directory, uses CommonJS module/moduleResolution, and has no exports map. Other Nx packages and external plugins reach into @nx/workspace/src/* subpaths for internal utilities.

Expected Behavior

@nx/workspace follows the same local-dist build pattern as nx and @nx/devkit:

  • Builds to packages/workspace/dist/ instead of dist/packages/workspace/.
  • tsconfig.lib.json uses module/moduleResolution: "nodenext" with composite, rootDir: ".", declarationDir: "dist".
  • package.json declares an exports map matching @nx/devkit's locked-down surface — only named entry points, no ./src/* wildcard.
  • generators.json / executors.json factory/schema paths rewritten ./src/..../dist/src/.... Workspace dev still works via the tryResolveFromSource fallback in packages/nx/src/config/schema-utils.ts.
  • README.mdreadme-template.md; build command writes the rendered README to packages/workspace/README.md.
  • project.json adds release.version config (preserveLocalDependencyProtocols: true, matching nx/devkit).
  • scripts/nx-release.ts: adds packages/workspace to packagesToReset.

Internal-leak cleanup so the locked-down exports map doesn't break first-party callers:

  • TypeScriptCompilationOptions + compileTypeScript moved from @nx/workspace to @nx/js (the package that owns TypeScript compilation).
  • @nx/remix inlines directoryExists (was a one-line re-export wrapper).
  • @nx/rspack drops the Nx 15.7-era @nx/workspace/src/utils/create-ts-config fallback.
  • e2e helpers source angularDevkitVersion from @nx/angular/src/utils instead.
  • @nx/workspace and @nx/remix no longer declare @nx/workspace deps they don't use.
  • Adds a preflight step to the dist-build-migration skill that warns about workspace:* deps on not-yet-migrated packages.

Breaking Changes

@nx/workspace/src/* subpath imports are no longer supported. The exports map no longer declares a ./src/* wildcard. ~150 public consumers across GitHub use these subpaths today (the largest cluster is src/utilities/fileutils with ~60 hits). Migration:

  • @nx/workspace/src/utilities/fileutils (directoryExists, fileExists, isRelativePath, createDirectory) — these are thin re-exports from nx/src/utils/fileutils. Inline with node:fs (statSync(p).isDirectory() for directoryExists) or import from @nx/devkit where equivalent.
  • @nx/workspace/src/utilities/typescript/compilation — moved to @nx/js. Internal callers should import from there; if you were depending on the type externally, copy it or use the equivalent from @nx/js.
  • @nx/workspace/src/utils/versions — values are duplicated across packages; reimplement against the version of Nx you're targeting.
  • Other subpaths — check the @nx/workspace's public index.ts re-exports first; otherwise reimplement.

@nx/workspace/tasks-runners/default now throws when invoked. This module was an alias for nx/src/tasks-runner/default-tasks-runner. The Nx 17 (use-minimal-config-for-tasks-runner-options) and Nx 21 (remove-custom-tasks-runner) migrations rewrite/remove legacy tasksRunnerOptions entries. If your nx.json still references @nx/workspace/tasks-runners/default, replace it with nx/tasks-runners/default.

Related Issue(s)

Part of the ongoing migration of Nx packages to the local-dist build layout (following nx and @nx/devkit).

@netlify
Copy link
Copy Markdown

netlify Bot commented May 11, 2026

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit 08a968b
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6a03c0c5a12f140008fdce53
😎 Deploy Preview https://deploy-preview-35643--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 11, 2026

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit 08a968b
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6a03c0c59442e200083abed2
😎 Deploy Preview https://deploy-preview-35643--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented May 11, 2026

View your CI Pipeline Execution ↗ for commit 08a968b

Command Status Duration Result
nx affected --targets=lint,test,build,e2e,e2e-c... ✅ Succeeded 36m 12s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 4s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 16s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 22s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 7s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-13 00:47:12 UTC

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 11, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch 2 times, most recently from 3b8fab3 to 9461a6b Compare May 11, 2026 15:52
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch from 9461a6b to 18c6509 Compare May 11, 2026 15:54
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch from 18c6509 to b524484 Compare May 11, 2026 16:02
nx-cloud[bot]

This comment was marked as outdated.

nx-cloud[bot]

This comment was marked as outdated.

nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch 6 times, most recently from 1b7b9fe to 1b88eec Compare May 11, 2026 19:53
@FrozenPandaz FrozenPandaz changed the title chore(core): build @nx/workspace to local dist and use nodenext chore(core)!: build @nx/workspace to local dist and use nodenext May 11, 2026
nx-cloud[bot]

This comment was marked as outdated.

nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch from 1b88eec to d2b59ae Compare May 11, 2026 20:08
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch 2 times, most recently from 99b5fdd to 5f016c0 Compare May 11, 2026 20:55
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch 2 times, most recently from b938100 to 7e22450 Compare May 11, 2026 21:30
@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch 2 times, most recently from 8b45912 to 07964d5 Compare May 11, 2026 21:42
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz marked this pull request as ready for review May 11, 2026 22:09
@FrozenPandaz FrozenPandaz requested a review from a team as a code owner May 11, 2026 22:09
@FrozenPandaz FrozenPandaz requested a review from JamesHenry May 11, 2026 22:09
@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch 2 times, most recently from 5daf687 to 1ead81f Compare May 12, 2026 01:44
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch from 1ead81f to baa4392 Compare May 12, 2026 15:51
Comment on lines +68 to 69
if (!outputIsDirectory) {
mkdir(options.outputPath);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mkdir call will fail if parent directories don't exist. The function is imported from fs-extra which does not create parent directories by default.

Fix by passing the recursive option:

if (!outputIsDirectory) {
  mkdir(options.outputPath, { recursive: true });
}

Alternatively, use fs-extra's ensureDirSync which was likely the intent:

if (!outputIsDirectory) {
  ensureDirSync(options.outputPath);
}
Suggested change
if (!outputIsDirectory) {
mkdir(options.outputPath);
if (!outputIsDirectory) {
mkdir(options.outputPath, { recursive: true });
}

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing behavior

@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch 2 times, most recently from b41b108 to 387f794 Compare May 12, 2026 18:30
Migrates @nx/workspace to the local-dist build pattern, matching nx and
@nx/devkit. Also cleans up the internal-leak surface that previously
exposed @nx/workspace/src/* subpaths to other packages.

- Build to packages/workspace/dist/ with nodenext + composite tsconfig.
- Lock down exports map (no ./src/* wildcard) to match @nx/devkit.
- Move TypeScriptCompilationOptions and compileTypeScript into @nx/js,
  where the tsc executor already lived.
- Inline directoryExists in @nx/remix (it was a thin re-export from nx).
- Source angularCliVersion default from @nx/angular in e2e helpers.
- Drop the Nx 15.7 @nx/workspace/src/utils/create-ts-config fallback
  from @nx/rspack.
- Guard the @nx/workspace ts-solution-setup mock in unit-test-setup.js
  so workspace-plugin's default-resolver tests don't fail.
- Replace @nx/workspace/tasks-runners/default with a throwing stub
  pointing users at the canonical `nx/tasks-runners/default`.
- Update dist-build-migration skill: add workspace:* preflight, remove
  naive .d.ts gitignore and *.d.ts eslint-ignore instructions.

BREAKING CHANGE: @nx/workspace no longer exposes the
\`@nx/workspace/src/*\` subpath wildcard in its exports map. Consumers
that imported internal modules from \`@nx/workspace/src/utilities/...\`
or \`@nx/workspace/src/utils/...\` must migrate to public alternatives
(@nx/devkit, @nx/js, or reimplement the helper). Most generic file
utilities (directoryExists, fileExists, etc.) are thin wrappers around
node:fs and can be inlined.

BREAKING CHANGE: \`@nx/workspace/tasks-runners/default\` now throws when
invoked. The Nx 17 and Nx 21 migrations have rewritten/removed legacy
\`tasksRunnerOptions\` entries to use \`nx/tasks-runners/default\`. If
you still reference \`@nx/workspace/tasks-runners/default\` in
\`nx.json\`, replace it with \`nx/tasks-runners/default\`.
@FrozenPandaz FrozenPandaz force-pushed the workspace-dist-migration branch from 387f794 to a1b639d Compare May 12, 2026 18:55
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? I'm ok with it but I thought part of the whole goal in a package based repo was to not need @nx/workspace

// Dynamic join() form bypasses the eslint self-circular-import check while
// still resolving to the package's own package.json in both source and built
// (local-dist) contexts.
export const nxVersion = require(join('@nx/workspace', 'package.json')).version;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I hate it. We've gotta come up with a better sln here, ideally we'd be building src/* -> dist/*, so the depth from the package.json wouldn't change

Comment thread .gitignore
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We moved d.ts generation into packages/nx/dist which means those lines are unnecessary because they're covered by dist

Package-based repos shouldn't depend on @nx/workspace by design.
The add-extra-dependencies build hack is sufficient on its own to fix
preset resolution; the template addition was a redundant workaround.
The add-extra-dependencies build hack alone is not sufficient to make
@nx/workspace resolvable from the forked preset generator at workspace
creation time. The template dep is required as a guaranteed-resolution
fallback until the build layout is reworked (packages/<pkg>/src ->
packages/<pkg>/dist) so the depth from built code to package.json
matches source layout.
The local-dist migration removed @nx/js's dependency on @nx/workspace
since the code-level imports it covered were inlined or moved. But that
dep was also load-bearing as the install-graph path that makes
@nx/workspace resolvable in created workspaces — without it, the forked
`nx g @nx/workspace:preset` from create-nx-workspace can't locate the
plugin and the ts-solution e2e tests fail.

Restore it as a dependency with an eslint ignoredDependencies entry
documenting that it's install-graph-only, no code-level import.

Also revert the package-based-repo template addition and its snapshot
updates from the prior workaround commit.
Copy link
Copy Markdown
Contributor

@nx-cloud nx-cloud Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ The fix from Nx Cloud was applied automatically

We add the missing packages/workspace/tsconfig.lib.json project reference to packages/js/tsconfig.lib.json to fix the sync:check failure. The PR moved TypeScriptCompilationOptions and compileTypeScript from @nx/workspace into @nx/js, creating a new compile-time dependency that the TypeScript sync generator requires to be declared.

Tip

We verified this fix by re-running nx-cloud record -- nx sync:check.

Suggested Fix changes
diff --git a/packages/js/tsconfig.lib.json b/packages/js/tsconfig.lib.json
index 3ea109e0..c0890687 100644
--- a/packages/js/tsconfig.lib.json
+++ b/packages/js/tsconfig.lib.json
@@ -20,6 +20,9 @@
     },
     {
       "path": "../nx/tsconfig.lib.json"
+    },
+    {
+      "path": "../workspace/tsconfig.lib.json"
     }
   ]
 }

Revert fix via Nx Cloud  

View interactive diff ↗

🎓 Learn more about Self-Healing CI on nx.dev

Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
@FrozenPandaz FrozenPandaz merged commit 07b16e4 into master May 13, 2026
26 checks passed
@FrozenPandaz FrozenPandaz deleted the workspace-dist-migration branch May 13, 2026 02:27
polygraph-snapshot-app Bot pushed a commit that referenced this pull request May 13, 2026
)

## Current Behavior

`@nx/workspace` builds to the shared workspace-root
`dist/packages/workspace/` directory, uses CommonJS
`module`/`moduleResolution`, and has no `exports` map. Other Nx packages
and external plugins reach into `@nx/workspace/src/*` subpaths for
internal utilities.

## Expected Behavior

`@nx/workspace` follows the same local-dist build pattern as `nx` and
`@nx/devkit`:

- Builds to `packages/workspace/dist/` instead of
`dist/packages/workspace/`.
- `tsconfig.lib.json` uses `module`/`moduleResolution: "nodenext"` with
`composite`, `rootDir: "."`, `declarationDir: "dist"`.
- `package.json` declares an `exports` map matching `@nx/devkit`'s
locked-down surface — only named entry points, no `./src/*` wildcard.
- `generators.json` / `executors.json` factory/schema paths rewritten
`./src/...` → `./dist/src/...`. Workspace dev still works via the
`tryResolveFromSource` fallback in
`packages/nx/src/config/schema-utils.ts`.
- `README.md` → `readme-template.md`; build command writes the rendered
README to `packages/workspace/README.md`.
- `project.json` adds `release.version` config
(`preserveLocalDependencyProtocols: true`, matching nx/devkit).
- `scripts/nx-release.ts`: adds `packages/workspace` to
`packagesToReset`.

Internal-leak cleanup so the locked-down exports map doesn't break
first-party callers:

- `TypeScriptCompilationOptions` + `compileTypeScript` moved from
`@nx/workspace` to `@nx/js` (the package that owns TypeScript
compilation).
- `@nx/remix` inlines `directoryExists` (was a one-line re-export
wrapper).
- `@nx/rspack` drops the Nx 15.7-era
`@nx/workspace/src/utils/create-ts-config` fallback.
- e2e helpers source `angularDevkitVersion` from `@nx/angular/src/utils`
instead.
- `@nx/workspace` and `@nx/remix` no longer declare `@nx/workspace` deps
they don't use.
- Adds a preflight step to the `dist-build-migration` skill that warns
about `workspace:*` deps on not-yet-migrated packages.

## Breaking Changes

**`@nx/workspace/src/*` subpath imports are no longer supported.** The
`exports` map no longer declares a `./src/*` wildcard. ~150 public
consumers across GitHub use these subpaths today (the largest cluster is
`src/utilities/fileutils` with ~60 hits). Migration:

- `@nx/workspace/src/utilities/fileutils` (`directoryExists`,
`fileExists`, `isRelativePath`, `createDirectory`) — these are thin
re-exports from `nx/src/utils/fileutils`. Inline with `node:fs`
(`statSync(p).isDirectory()` for `directoryExists`) or import from
`@nx/devkit` where equivalent.
- `@nx/workspace/src/utilities/typescript/compilation` — moved to
`@nx/js`. Internal callers should import from there; if you were
depending on the type externally, copy it or use the equivalent from
`@nx/js`.
- `@nx/workspace/src/utils/versions` — values are duplicated across
packages; reimplement against the version of Nx you're targeting.
- Other subpaths — check the `@nx/workspace`'s public `index.ts`
re-exports first; otherwise reimplement.

**`@nx/workspace/tasks-runners/default` now throws when invoked.** This
module was an alias for `nx/src/tasks-runner/default-tasks-runner`. The
Nx 17 (`use-minimal-config-for-tasks-runner-options`) and Nx 21
(`remove-custom-tasks-runner`) migrations rewrite/remove legacy
`tasksRunnerOptions` entries. If your `nx.json` still references
`@nx/workspace/tasks-runners/default`, replace it with
`nx/tasks-runners/default`.

## Related Issue(s)

Part of the ongoing migration of Nx packages to the local-dist build
layout (following `nx` and `@nx/devkit`).

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.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.

2 participants