fix(core): support aliased workspace package dependencies - #36686
Draft
leosvelperez wants to merge 9 commits into
Draft
fix(core): support aliased workspace package dependencies#36686leosvelperez wants to merge 9 commits into
leosvelperez wants to merge 9 commits into
Conversation
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
View your CI Pipeline Execution ↗ for commit d1f8236
☁️ Nx Cloud last updated this comment at |
Contributor
There was a problem hiding this comment.
Nx Cloud has identified a flaky task in your failed CI:
🔂 Since the failure was identified as flaky, we triggered a CI rerun by adding an empty commit to this branch.
🎓 Learn more about Self-Healing CI on nx.dev
…oject metadata Parse package.json dependency specifiers (plain, workspace: and npm: aliases) and record the entries that resolve to workspace packages as metadata.js.packageDependencies descriptors, in both package-json plugins. The descriptors are recomputed on every run and attached outside the per-file plugin cache entries, so target package changes are reflected even when the source manifest is served from the cache. Groundwork for resolving workspace and npm package aliases in the project graph and Nx Release.
…jects "alias": "workspace:pkg@range" entries created no graph edge (or an edge to the project matching the manifest key), and npm:pkg@range aliases never resolved to workspace projects. The locator now parses the specifier first: a target-bearing workspace alias always links the requested package when it exists (pnpm links even on a range mismatch), and an npm alias links it when the workspace version satisfies the range; plain and file: entries behave as before. A workspace alias naming no workspace package is rejected by every package manager at install time; graph construction now fails it per-file with the dependency key, requested package, a close-name suggestion, and a repair hint that preserves registry intent (replace workspace: with npm:). Only manifests the package manager installs (the root or workspace-globs matches) are validated, and packages outside them never satisfy an alias. Task commands surface these messages without --verbose, matching the handleErrors rendering. Pruned lock file creation threw "package was not found in the root lock file" for aliased entries; it now keeps the alias key and traverses the requested workspace target, dispatching workspace-protocol entries before external lookups so a same-named external node cannot shadow the local target. parseDependencySpecifier and the ProjectPackageDependencies type are exposed through @nx/devkit/internal for first-party consumers.
Generated package.json files for buildable projects dropped internal dependencies referenced through workspace: or npm: aliases, or emitted them under the target's canonical package name instead of the alias key the consuming code imports. The buildable-deps path now emits every manifest key referencing the target, expressing aliases in the registry-installable npm:<target>@<version> form using the built output's version.
An internal dependency referenced through a workspace: or npm: alias made the rule report the target's canonical package name as missing and the alias key as obsolete. Expected dependency names now use the manifest identity, so aliased entries are neither missing nor obsolete while genuinely unused aliases are still reported. Version mismatch checks compare an npm: alias against its inner range, and the fixer only nests a root manifest value inside npm:<target>@... when it is a plain registry range, falling back to the installed version otherwise. A key reused across dependency collections resolves to the production entry, matching how the graph builds edges.
…uring versioning Nx Release located manifest dependency entries only by the target package name, so entries like "alias": "workspace:pkg@^1.0.0" or "alias": "npm:pkg@^1.0.0" were invisible: their versions were never bumped when the target package was versioned, VersionData reported no version spec for them, and an entry whose key names one workspace package while aliasing another was treated as belonging to the wrong package and could be rewritten to a plain version, silently retargeting it to a registry package of the same name. Dependency entries are now located by parsing each specifier with the shared dependency specifier parser: an entry references a package when its key or its aliased target names it. Reads return the alias inner range so the versionPrefix logic operates on the actual version, and writes update every referencing entry, preserving the alias key and target and rewriting only the inner range. Workspace aliases are left untouched when preserveLocalDependencyProtocols is enabled (the default) and are converted to registry-compatible npm aliases otherwise. Two related defects surfaced by the change are also fixed: the dependency-update counter is now tracked per manifest so a preserved entry in one manifest no longer aborts processing of later manifests (previously it could also skip catalog updates and go negative when a dependency appeared in several collections), and versionPrefix "auto" now preserves each declaration's own prefix instead of stamping the prefix of the first located entry onto all of them.
…iles
Aliased manifest entries ("alias": "workspace:pkg@range" or "npm:pkg@range")
broke pruned lock file generation. The pruner only resolved workspace:
aliases, so npm: aliases targeting workspace packages failed the external
lookup and createLockFile silently fell back to the stale root lock file.
The pnpm and npm stringifiers matched workspace packages by the raw
manifest key, so aliased entries threw, were dropped from the output, or
were pointed at a module dir named after the alias instead of the target
package.
Entry resolution now routes through a shared
resolveWorkspaceDependencyTarget helper (exported via @nx/devkit/internal):
aliasing specifiers resolve by their requested target under the same
range-satisfaction policy the graph uses, other entries keep the key-based
behavior. The pruner keeps npm-alias-to-workspace entries and traverses the
target. The pnpm stringifier links alias keys to the target's module dir in
the root snapshot and resolves aliased entries in the transitive importer
walks. The npm stringifier links alias keys per referencing entry, nesting
a link under its consumer when an alias key collides with a canonical
package name, no longer mutates the shared manifest object, and emits
name-keyed v1 dependency nodes instead of v3 path keys for lockfileVersion
1 and 2 outputs.
The prune-lockfile executor rewrote aliased workspace dependencies to file:./workspace_modules/<alias-key>, a directory that never exists (modules are copied under their package names), and left npm: aliases pointing at the registry. The copy-workspace-modules executor matched workspace packages by the raw manifest key, so aliased dependencies were never copied and nested aliased entries kept their workspace: specifiers in the copied manifests. Both executors now resolve entries through resolveWorkspaceDependencyTarget: alias keys are kept and pointed at the target package's module dir, and the target modules are copied.
Co-authored-by: leosvelperez <leosvelperez@users.noreply.github.com>
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.
Current Behavior
pnpm workspace aliases (
"alias": "workspace:pkg@range") and npm aliases targeting workspace packages ("alias": "npm:pkg@range") are not resolved anywhere in Nx:nx releasenever sees aliased entries: dependents are not version-bumped, and withpreserveLocalDependencyProtocols: falsean entry whose key names package A but whose value aliases package B is overwritten with A's version, silently retargeting the dependency to the registry package A.generatePackageJson,createPackageJson) drop or corrupt aliased entries.@nx/dependency-checkslint rule reports aliased dependencies as missing and "fixes" them into duplicates.createLockFilesilently falls back to the stale root lock file; the pnpm/npm stringifiers throw or drop the entries; theprune-lockfileexecutor rewrites aliases to aworkspace_modulesdir that never exists;copy-workspace-modulesnever copies the aliased targets.Expected Behavior
Aliased entries resolve to the workspace package they reference, everywhere a plain entry does:
nx releasereads the inner range of aliased entries (soversion.versionPrefix: "auto"preserves each entry's own prefix), updates every entry referencing a bumped dependency while keeping alias keys and targets intact, preservesworkspace:aliases by default, and converts them to registry-compatiblenpm:pkg@versionaliases whenpreserveLocalDependencyProtocolsisfalse."alias": "npm:target@version"under their original keys.@nx/dependency-checksvalidates aliased entries against the target package and inner range.file:./workspace_modules/<target>, lock files link the alias key to the target's module dir (nesting under the consumer when an alias key collides with a canonical package name; name-keyed nodes for v1/v2dependenciessections), andcopy-workspace-modulescopies the aliased targets.Related Issue(s)
Fixes #36630
Implementation Details
A single specifier parser (
parseDependencySpecifier) and matching policy (matchDependencyToWorkspacePackage,resolveWorkspaceDependencyTarget) back every consumer: graph resolution, release version actions, dependency-checks, generated manifests, lock file pruning/stringification, and the deploy executors. The graph attaches per-project manifest descriptors (metadata.js.packageDependencies) so consumers see alias targets without re-reading manifests.Notes and boundaries:
NX_FORCE_REUSE_CACHED_GRAPH, where an older cached graph simply lacks the field and behaves like pre-fix Nx.--frozen-lockfilesemantics.npm:pkg) keep floating: release never pins them, and they match workspace packages regardless of version.View Polygraph session ↗