nx 23.1.0 → 23.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/dist/bin/nx.js +2 -1
  2. package/dist/release/changelog-renderer/index.js +4 -5
  3. package/dist/release/index.d.ts +1 -1
  4. package/dist/src/ai/configure-ai-agents-disclaimer.d.ts +5 -0
  5. package/dist/src/ai/configure-ai-agents-disclaimer.js +31 -0
  6. package/dist/src/analytics/analytics.d.ts +6 -0
  7. package/dist/src/analytics/analytics.js +16 -8
  8. package/dist/src/analytics/index.d.ts +1 -1
  9. package/dist/src/analytics/index.js +2 -1
  10. package/dist/src/command-line/add/add.js +13 -1
  11. package/dist/src/command-line/affected/affected.js +1 -0
  12. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
  13. package/dist/src/command-line/graph/graph.d.ts +1 -0
  14. package/dist/src/command-line/graph/graph.js +15 -6
  15. package/dist/src/command-line/import/import.js +2 -3
  16. package/dist/src/command-line/init/ai-agent-prompts.d.ts +1 -1
  17. package/dist/src/command-line/init/ai-agent-prompts.js +7 -6
  18. package/dist/src/command-line/init/command-object.js +10 -2
  19. package/dist/src/command-line/init/configure-plugins.js +3 -2
  20. package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +4 -2
  21. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +4 -3
  22. package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +4 -3
  23. package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +4 -3
  24. package/dist/src/command-line/init/implementation/angular/index.js +4 -2
  25. package/dist/src/command-line/init/implementation/angular/integrated-workspace.js +2 -5
  26. package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +3 -0
  27. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +4 -1
  28. package/dist/src/command-line/init/implementation/utils.d.ts +3 -3
  29. package/dist/src/command-line/init/implementation/utils.js +22 -3
  30. package/dist/src/command-line/init/init-v2.js +4 -5
  31. package/dist/src/command-line/migrate/execute-migration.d.ts +72 -0
  32. package/dist/src/command-line/migrate/execute-migration.js +389 -0
  33. package/dist/src/command-line/migrate/migrate-ui-api.js +17 -12
  34. package/dist/src/command-line/migrate/migrate.d.ts +5 -45
  35. package/dist/src/command-line/migrate/migrate.js +122 -402
  36. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +1 -1
  37. package/dist/src/command-line/nx-cloud/connect/view-logs.js +2 -0
  38. package/dist/src/command-line/release/changelog.js +5 -0
  39. package/dist/src/command-line/release/index.d.ts +1 -1
  40. package/dist/src/command-line/release/utils/git.js +7 -3
  41. package/dist/src/command-line/release/utils/release-graph.d.ts +19 -2
  42. package/dist/src/command-line/release/utils/release-graph.js +71 -19
  43. package/dist/src/command-line/release/utils/remote-release-clients/github.js +7 -1
  44. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +1 -1
  45. package/dist/src/command-line/release/utils/shared.d.ts +2 -2
  46. package/dist/src/command-line/release/version/release-group-processor.d.ts +24 -0
  47. package/dist/src/command-line/release/version/release-group-processor.js +80 -6
  48. package/dist/src/command-line/release/version/version-actions.d.ts +27 -2
  49. package/dist/src/command-line/release/version/version-actions.js +9 -1
  50. package/dist/src/command-line/release/version.js +9 -5
  51. package/dist/src/command-line/reset/reset.js +6 -1
  52. package/dist/src/command-line/run/run-one.js +1 -0
  53. package/dist/src/command-line/run-many/run-many.js +1 -0
  54. package/dist/src/command-line/show/show-target/info.js +11 -2
  55. package/dist/src/command-line/show/show-target/inputs.js +50 -153
  56. package/dist/src/command-line/show/show-target/outputs.js +48 -173
  57. package/dist/src/command-line/show/show-target/utils.d.ts +28 -0
  58. package/dist/src/command-line/show/show-target/utils.js +83 -3
  59. package/dist/src/command-line/watch/watch.js +13 -0
  60. package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
  61. package/dist/src/commands-runner/create-command-graph.js +5 -0
  62. package/dist/src/config/nx-json.d.ts +5 -3
  63. package/dist/src/core/graph/main.js +1 -1
  64. package/dist/src/core/graph/styles.js +1 -1
  65. package/dist/src/daemon/client/client.d.ts +52 -3
  66. package/dist/src/daemon/client/client.js +177 -55
  67. package/dist/src/daemon/client/daemon-environment.d.ts +17 -0
  68. package/dist/src/daemon/client/daemon-environment.js +110 -2
  69. package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
  70. package/dist/src/daemon/message-types/daemon-message.d.ts +32 -0
  71. package/dist/src/daemon/message-types/daemon-message.js +25 -0
  72. package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +5 -0
  73. package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +16 -0
  74. package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +1 -1
  75. package/dist/src/daemon/server/latest-nx.js +5 -2
  76. package/dist/src/daemon/server/server.js +31 -4
  77. package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
  78. package/dist/src/daemon/server/shutdown-utils.js +11 -4
  79. package/dist/src/daemon/socket-utils.d.ts +1 -0
  80. package/dist/src/daemon/socket-utils.js +24 -3
  81. package/dist/src/daemon/tmp-dir.d.ts +43 -3
  82. package/dist/src/daemon/tmp-dir.js +437 -15
  83. package/dist/src/devkit-internals.d.ts +96 -3
  84. package/dist/src/devkit-internals.js +200 -4
  85. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
  86. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
  87. package/dist/src/generators/tree.d.ts +4 -2
  88. package/dist/src/generators/utils/nx-json.js +13 -3
  89. package/dist/src/hasher/check-task-files.d.ts +86 -0
  90. package/dist/src/hasher/check-task-files.js +378 -0
  91. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
  92. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
  93. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
  94. package/dist/src/native/index.d.ts +31 -2
  95. package/dist/src/native/index.js +71 -28
  96. package/dist/src/native/native-bindings.js +3 -0
  97. package/dist/src/native/native-file-cache-location.d.ts +17 -1
  98. package/dist/src/native/native-file-cache-location.js +86 -12
  99. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  100. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  101. package/dist/src/nx-cloud/utilities/get-cloud-options.js +5 -2
  102. package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
  103. package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
  104. package/dist/src/plugins/js/lock-file/npm-parser.js +71 -27
  105. package/dist/src/plugins/js/lock-file/pnpm-parser.js +110 -17
  106. package/dist/src/plugins/js/lock-file/project-graph-pruning.js +15 -9
  107. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +3 -0
  108. package/dist/src/plugins/js/lock-file/yarn-parser.js +32 -5
  109. package/dist/src/plugins/js/project-graph/affected/npm-packages.js +51 -20
  110. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +14 -4
  111. package/dist/src/plugins/js/utils/register.d.ts +18 -0
  112. package/dist/src/plugins/js/utils/register.js +81 -0
  113. package/dist/src/plugins/js/utils/typescript.d.ts +9 -0
  114. package/dist/src/plugins/js/utils/typescript.js +15 -0
  115. package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
  116. package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
  117. package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
  118. package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
  119. package/dist/src/project-graph/build-project-graph.js +7 -1
  120. package/dist/src/project-graph/error-types.d.ts +12 -1
  121. package/dist/src/project-graph/error-types.js +68 -3
  122. package/dist/src/project-graph/file-utils.js +6 -1
  123. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
  124. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +22 -1
  125. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
  126. package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
  127. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
  128. package/dist/src/project-graph/plugins/loaded-nx-plugin.js +1 -0
  129. package/dist/src/project-graph/plugins/transpiler.js +4 -0
  130. package/dist/src/project-graph/project-graph.js +23 -0
  131. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.d.ts +10 -12
  132. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.js +93 -63
  133. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +5 -8
  134. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +16 -15
  135. package/dist/src/project-graph/utils/project-configuration/source-maps.d.ts +12 -5
  136. package/dist/src/project-graph/utils/project-configuration/source-maps.js +21 -7
  137. package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +1 -1
  138. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +26 -27
  139. package/dist/src/project-graph/utils/project-configuration/target-merging.js +50 -8
  140. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +149 -6
  141. package/dist/src/project-graph/utils/project-configuration/utils.d.ts +15 -0
  142. package/dist/src/project-graph/utils/project-configuration/utils.js +42 -6
  143. package/dist/src/project-graph/utils/project-configuration-utils.d.ts +15 -12
  144. package/dist/src/project-graph/utils/project-configuration-utils.js +79 -101
  145. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +11 -4
  146. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +15 -6
  147. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +5 -4
  148. package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +18 -8
  149. package/dist/src/tasks-runner/life-cycles/performance-report.js +66 -29
  150. package/dist/src/tasks-runner/run-command.js +4 -2
  151. package/dist/src/tasks-runner/task-env.d.ts +9 -0
  152. package/dist/src/tasks-runner/task-env.js +31 -4
  153. package/dist/src/tasks-runner/task-orchestrator.d.ts +4 -1
  154. package/dist/src/tasks-runner/task-orchestrator.js +23 -10
  155. package/dist/src/tasks-runner/utils.js +2 -6
  156. package/dist/src/utils/acknowledge-build-scripts.d.ts +13 -0
  157. package/dist/src/utils/acknowledge-build-scripts.js +102 -0
  158. package/dist/src/utils/analytics-prompt.d.ts +0 -6
  159. package/dist/src/utils/analytics-prompt.js +0 -51
  160. package/dist/src/utils/assert-workspace-validity.js +7 -2
  161. package/dist/src/utils/catalog/manager-utils.d.ts +1 -2
  162. package/dist/src/utils/catalog/manager-utils.js +22 -5
  163. package/dist/src/utils/catalog/pnpm-manager.d.ts +1 -0
  164. package/dist/src/utils/catalog/pnpm-manager.js +3 -15
  165. package/dist/src/utils/catalog/yarn-manager.d.ts +1 -0
  166. package/dist/src/utils/catalog/yarn-manager.js +3 -15
  167. package/dist/src/utils/command-line-utils.js +30 -20
  168. package/dist/src/utils/fileutils.d.ts +5 -0
  169. package/dist/src/utils/fileutils.js +7 -2
  170. package/dist/src/utils/git-revision.d.ts +12 -0
  171. package/dist/src/utils/git-revision.js +25 -0
  172. package/dist/src/utils/git-utils.d.ts +16 -3
  173. package/dist/src/utils/git-utils.js +146 -52
  174. package/dist/src/utils/git-worktrees.d.ts +30 -0
  175. package/dist/src/utils/git-worktrees.js +212 -0
  176. package/dist/src/utils/ignore.d.ts +128 -1
  177. package/dist/src/utils/ignore.js +182 -12
  178. package/dist/src/utils/long-running-target.d.ts +10 -0
  179. package/dist/src/utils/long-running-target.js +19 -0
  180. package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
  181. package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
  182. package/dist/src/utils/min-release-age/behavior/pnpm.js +9 -7
  183. package/dist/src/utils/min-release-age/packument.js +1 -1
  184. package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
  185. package/dist/src/utils/nx-tmp-dir.js +40 -0
  186. package/dist/src/utils/owned-private-dir.d.ts +132 -0
  187. package/dist/src/utils/owned-private-dir.js +323 -0
  188. package/dist/src/utils/package-json.js +11 -7
  189. package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
  190. package/dist/src/utils/package-manager-config/bunfig.js +49 -0
  191. package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
  192. package/dist/src/utils/package-manager-config/npmrc.js +124 -0
  193. package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
  194. package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
  195. package/dist/src/utils/package-manager.d.ts +23 -2
  196. package/dist/src/utils/package-manager.js +264 -43
  197. package/dist/src/utils/provenance.js +18 -16
  198. package/dist/src/utils/registry-config/bun.d.ts +2 -0
  199. package/dist/src/utils/registry-config/bun.js +257 -0
  200. package/dist/src/utils/registry-config/index.d.ts +13 -0
  201. package/dist/src/utils/registry-config/index.js +102 -0
  202. package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
  203. package/dist/src/utils/registry-config/pnpm.js +1551 -0
  204. package/dist/src/utils/registry-config/utils.d.ts +195 -0
  205. package/dist/src/utils/registry-config/utils.js +496 -0
  206. package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
  207. package/dist/src/utils/registry-config/yarn-berry.js +612 -0
  208. package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
  209. package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
  210. package/dist/src/utils/safe-spawn.d.ts +24 -0
  211. package/dist/src/utils/safe-spawn.js +104 -0
  212. package/dist/src/utils/shell-quoting.d.ts +15 -0
  213. package/dist/src/utils/shell-quoting.js +35 -0
  214. package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
  215. package/dist/src/utils/wait-for-socket-connection.js +14 -8
  216. package/dist/src/utils/workspace-id.d.ts +20 -0
  217. package/dist/src/utils/workspace-id.js +54 -0
  218. package/migrations.json +13 -1
  219. package/package.json +22 -17
  220. package/dist/src/migrations/update-17-3-0/nx-release-path.d.ts +0 -3
  221. package/dist/src/migrations/update-17-3-0/nx-release-path.js +0 -47
  222. package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
  223. package/dist/src/utils/min-release-age/npmrc.js +0 -45
@@ -0,0 +1,50 @@
1
+ #### Set cache on executor-based target defaults
2
+
3
+ Maintains compatibility with how cacheability was resolved before Nx 23.
4
+
5
+ Target defaults resolve to a single key rather than merging them. When a target matches both a target name key and an executor key, the executor key is the one that applies, and the target name key is not read at all. Earlier versions of Nx also derived cacheability from target names, so `"cache": true` on the target name key still took effect even when an executor key applied. Nx 23 reads cacheability from the resolved target only.
6
+
7
+ This migration copies `cache` onto each executor key that applies to a target whose target name key enables it, so those targets remain cacheable.
8
+
9
+ An executor key applies to every target that resolves through it, so the migration updates a key only when it can establish that caching is right for all of them. It leaves the key unchanged when the key already declares `cache`, when it declares `continuous: true`, when every entry on the key is filtered so none of them always applies, when a target through it is continuous or is a long-running target such as `serve`, `dev`, or `start`, when the executor's own schema marks its targets continuous, or when a target through it has no target name key enabling `cache`. It never updates the `nx:run-commands` or `nx:run-script` keys, because targets written with `command` and targets derived from `package.json` scripts resolve through those keys without naming them.
10
+
11
+ Where the migration makes no change, Nx still decides at run time, per target rather than per key: a target whose target name key enables `cache`, and whose resolved configuration still leaves `cache` undefined, gets it back and is reported as a deprecated fallback. Because that test is applied to the resolved target rather than to the key, it also covers the cases where a key looks decided but is not for this project — an executor key whose entries are all filtered, or whose only `cache` declaration is filtered out here. It equally covers the targets left behind because a sibling target through the same key does not opt in, and the targets reaching the `nx:run-commands` and `nx:run-script` keys. Three cases get no fallback and no warning. A continuous or long-running target, because caching it is invalid. A target whose resolved configuration already sets `cache`. And a target whose target name key was discarded as incompatible, because its entry named an executor the target does not use — before Nx 23 that target was cacheable, since cacheability was matched on target name alone and the entry's `executor` was never read. Restoring it here would mean setting `cache` with no executor key to point the user at and no migration able to retire the behavior, so it is deliberately left alone. Set `cache` on those targets directly.
12
+
13
+ Targets are read from each project's `project.json` and from the `nx.targets` block of its `package.json`. Targets inferred by a plugin, and targets derived from `package.json` scripts, are not visible to the migration — the latter is why the `nx:run-script` key is never updated. A key that looks safe here may still serve an inferred target. Set `cache` on the executor key by hand only after checking that no target reaching it is continuous; Nx rejects a target that is both cacheable and continuous. Nx names both keys in a run-time warning. That warning is emitted during project graph construction, so while the daemon is running it goes to `.nx/workspace-data/d/daemon.log` rather than the terminal; CI runs, where the daemon is disabled, print it normally.
14
+
15
+ #### Sample code changes
16
+
17
+ Given a project with a `build` target that uses the `@nx/angular:webpack-browser` executor:
18
+
19
+ ##### Before
20
+
21
+ ```json title="nx.json"
22
+ {
23
+ "targetDefaults": {
24
+ "build": {
25
+ "cache": true,
26
+ "dependsOn": ["^build"]
27
+ },
28
+ "@nx/angular:webpack-browser": {
29
+ "inputs": ["production", "^production"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ##### After
36
+
37
+ ```json title="nx.json"
38
+ {
39
+ "targetDefaults": {
40
+ "build": {
41
+ "cache": true,
42
+ "dependsOn": ["^build"]
43
+ },
44
+ "@nx/angular:webpack-browser": {
45
+ "inputs": ["production", "^production"],
46
+ "cache": true
47
+ }
48
+ }
49
+ }
50
+ ```
@@ -326,6 +326,7 @@ export interface EventDimensions {
326
326
  packageName: string
327
327
  packageVersion: string
328
328
  duration: string
329
+ sampleRate: string
329
330
  taskCount: string
330
331
  projectCount: string
331
332
  cachedTaskCount: string
@@ -411,6 +412,16 @@ export declare function getEventDimensions(): EventDimensions
411
412
  */
412
413
  export declare function getFilesForOutputsBatch(directory: string, entriesBatch: Array<Array<string>>): Array<Array<string>>
413
414
 
415
+ /**
416
+ * The same list, for JavaScript callers that walk a tree rather than the
417
+ * filesystem - `visitNotIgnoredFiles` - so both sides apply one baseline
418
+ * instead of maintaining a second copy that drifts.
419
+ *
420
+ * The patterns are gitignore-shaped, so they read the same to the `ignore`
421
+ * crate here and the `ignore` npm package there.
422
+ */
423
+ export declare function getHardcodedIgnorePatterns(): Array<string>
424
+
414
425
  /**
415
426
  * If `workspace_root` is inside a git worktree, returns the main repo root.
416
427
  * Returns `None` when already in the main repo (or not in a git repo at all).
@@ -487,14 +498,14 @@ export interface HashInputs {
487
498
  * so the caller can set it as an env var for child processes.
488
499
  * Used by CLI and daemon.
489
500
  */
490
- export declare function initializeTelemetry(connection: ExternalObject<NxDbConnection>, workspaceId: string, userId: string, nxVersion: string, packageManagerName: string, packageManagerVersion: string | undefined | null, nodeVersion: string, osArch: string, osPlatform: string, osRelease: string, isCi: boolean, isNxCloud: boolean): string
501
+ export declare function initializeTelemetry(connection: ExternalObject<NxDbConnection>, workspaceId: string, userId: string | undefined | null, nxVersion: string, packageManagerName: string, packageManagerVersion: string | undefined | null, nodeVersion: string, osArch: string, osPlatform: string, osRelease: string, isCi: boolean, isNxCloud: boolean): string
491
502
 
492
503
  /**
493
504
  * Initialize telemetry with a pre-fetched session ID.
494
505
  * No DB connection — used by plugin workers that inherit the
495
506
  * session ID from their parent process via env var.
496
507
  */
497
- export declare function initializeTelemetryWithSessionId(sessionId: string, workspaceId: string, userId: string, nxVersion: string, packageManagerName: string, packageManagerVersion: string | undefined | null, nodeVersion: string, osArch: string, osPlatform: string, osRelease: string, isCi: boolean, isNxCloud: boolean): void
508
+ export declare function initializeTelemetryWithSessionId(sessionId: string, workspaceId: string, userId: string | undefined | null, nxVersion: string, packageManagerName: string, packageManagerVersion: string | undefined | null, nodeVersion: string, osArch: string, osPlatform: string, osRelease: string, isCi: boolean, isNxCloud: boolean): void
498
509
 
499
510
  export interface InputsInput {
500
511
  input: string
@@ -557,6 +568,24 @@ export interface Link {
557
568
 
558
569
  export declare function logDebug(message: string): void
559
570
 
571
+ /**
572
+ * Checks which `paths` match the given `globs`, using the same glob engine
573
+ * as the task hasher (`build_glob_set`). Used to statically match
574
+ * `dependentTasksOutputFiles` globs against candidate paths.
575
+ */
576
+ export declare function matchGlobPaths(globs: Array<string>, paths: Array<string>): Array<boolean>
577
+
578
+ /**
579
+ * Statically checks which `paths` would be captured by the given output
580
+ * `entries`, without touching the file system. Mirrors `expand_outputs`
581
+ * semantics: entries match themselves and anything nested under them (so a
582
+ * directory entry captures its contents), negated (`!`-prefixed) entries
583
+ * exclude matches from the whole entry set, and a non-empty list with only
584
+ * negated entries matches everything not excluded. An empty list matches
585
+ * nothing.
586
+ */
587
+ export declare function matchOutputPaths(entries: Array<string>, paths: Array<string>): Array<boolean>
588
+
560
589
  /** Combined metadata for groups and processes */
561
590
  export interface Metadata {
562
591
  /** Group-level metadata */
@@ -1,15 +1,11 @@
1
1
  const { join, basename } = require('path');
2
- const {
3
- copyFileSync,
4
- existsSync,
5
- mkdirSync,
6
- renameSync,
7
- statSync,
8
- unlinkSync,
9
- } = require('fs');
2
+ const { copyFileSync, renameSync, statSync, unlinkSync } = require('fs');
3
+ const { createHash } = require('crypto');
10
4
  const Module = require('module');
11
5
  const { nxVersion } = require('../utils/versions');
12
- const { getNativeFileCacheLocation } = require('./native-file-cache-location');
6
+ const {
7
+ ensureSecureNativeFileCacheLocation,
8
+ } = require('./native-file-cache-location');
13
9
 
14
10
  const MAX_COPY_RETRIES = 3;
15
11
 
@@ -73,6 +69,14 @@ function statsOrNull(path) {
73
69
  }
74
70
  }
75
71
 
72
+ function removeIfPresent(path) {
73
+ try {
74
+ unlinkSync(path);
75
+ } catch {
76
+ // Ignore cleanup errors
77
+ }
78
+ }
79
+
76
80
  function isNoExecError(e) {
77
81
  return e.code === 'EACCES' || e.code === 'EPERM';
78
82
  }
@@ -95,20 +99,44 @@ Module._load = function (request, parent, isMain) {
95
99
  const nativeLocation = require.resolve(modulePath);
96
100
  const fileName = basename(nativeLocation);
97
101
 
98
- // we copy the file to a workspace-scoped tmp directory and prefix with nxVersion to avoid stale files being loaded
99
- const nativeFileCacheLocation = getNativeFileCacheLocation();
102
+ // null when the cache dir cannot be created *and trusted*; loading in place
103
+ // from node_modules is safer than loading out of a directory we don't own.
104
+ const nativeFileCacheLocation = ensureSecureNativeFileCacheLocation();
105
+ if (!nativeFileCacheLocation) {
106
+ return originalLoad.apply(this, [nativeLocation, parent, isMain]);
107
+ }
108
+
109
+ // nxVersion alone is not enough: in a source checkout it is the placeholder
110
+ // 0.0.1 for every worktree, so hashing the resolved binding path keeps each
111
+ // checkout in its own entry.
112
+ const cacheKey =
113
+ nxVersion +
114
+ '-' +
115
+ createHash('sha256')
116
+ .update(nativeLocation)
117
+ .digest('hex')
118
+ .substring(0, 12);
119
+
100
120
  // This is a path to copy to, not the one that gets loaded
101
121
  const tmpTmpFile = join(
102
122
  nativeFileCacheLocation,
103
- nxVersion + '-' + Math.random() + fileName
123
+ cacheKey + '-' + Math.random() + fileName
104
124
  );
105
125
  // This is the path that will get loaded
106
- const tmpFile = join(nativeFileCacheLocation, nxVersion + '-' + fileName);
107
- const expectedFileSize = statSync(nativeLocation).size;
126
+ const tmpFile = join(nativeFileCacheLocation, cacheKey + '-' + fileName);
127
+ const sourceStats = statSync(nativeLocation);
128
+ const expectedFileSize = sourceStats.size;
108
129
  const existingFileStats = statsOrNull(tmpFile);
109
130
 
110
- // If the file to be loaded already exists, just load it
111
- if (existingFileStats?.size === expectedFileSize) {
131
+ // Size alone does not detect a rebuilt binding: a small Rust edit routinely
132
+ // produces a byte-identical size. The copy is taken after the build, so a
133
+ // cache entry older than the source means the source was rebuilt since.
134
+ const isFresh =
135
+ existingFileStats?.size === expectedFileSize &&
136
+ existingFileStats.mtimeMs >= sourceStats.mtimeMs;
137
+
138
+ // If the file to be loaded already exists and is current, just load it
139
+ if (isFresh) {
112
140
  try {
113
141
  return originalLoad.apply(this, [tmpFile, parent, isMain]);
114
142
  } catch (e) {
@@ -119,20 +147,36 @@ Module._load = function (request, parent, isMain) {
119
147
  throw e;
120
148
  }
121
149
  }
122
- if (!existsSync(nativeFileCacheLocation)) {
123
- mkdirSync(nativeFileCacheLocation, { recursive: true });
124
- }
125
150
 
126
151
  // Retry copying up to 3 times, validating after each copy
152
+ let attemptsMade = 0;
153
+ let cacheError = null;
127
154
  for (let attempt = 1; attempt <= MAX_COPY_RETRIES; attempt++) {
155
+ attemptsMade = attempt;
128
156
  // First copy to a unique location for each process
129
- copyFileSync(nativeLocation, tmpTmpFile);
157
+ try {
158
+ copyFileSync(nativeLocation, tmpTmpFile);
159
+ } catch (e) {
160
+ // Permission errors won't heal on retry, and a throwing copy can still
161
+ // have left a partial file behind.
162
+ cacheError = e;
163
+ removeIfPresent(tmpTmpFile);
164
+ break;
165
+ }
130
166
 
131
167
  // Validate the copy - check file size matches expected
132
168
  const copiedFileStats = statsOrNull(tmpTmpFile);
133
169
  if (copiedFileStats?.size === expectedFileSize) {
134
170
  // Copy succeeded, rename to final location and load
135
- renameSync(tmpTmpFile, tmpFile);
171
+ try {
172
+ renameSync(tmpTmpFile, tmpFile);
173
+ } catch (e) {
174
+ // Unguarded, this throws out of `require` and native-bindings.js
175
+ // reports it as a missing optional dependency, which it is not.
176
+ cacheError = e;
177
+ removeIfPresent(tmpTmpFile);
178
+ break;
179
+ }
136
180
  try {
137
181
  return originalLoad.apply(this, [tmpFile, parent, isMain]);
138
182
  } catch (e) {
@@ -145,16 +189,15 @@ Module._load = function (request, parent, isMain) {
145
189
  }
146
190
 
147
191
  // Copy failed validation, clean up the malformed file
148
- try {
149
- unlinkSync(tmpTmpFile);
150
- } catch {
151
- // Ignore cleanup errors
152
- }
192
+ removeIfPresent(tmpTmpFile);
153
193
  }
154
194
 
155
- // All retries failed - warn and load from original location
195
+ // Name the errno: ENOSPC, EROFS, EACCES and EDQUOT are all actionable and
196
+ // otherwise indistinguishable.
156
197
  console.warn(
157
- `Warning: Failed to copy native module to cache after ${MAX_COPY_RETRIES} attempts. ` +
198
+ `Warning: Failed to copy native module to cache after ${attemptsMade} attempt${
199
+ attemptsMade === 1 ? '' : 's'
200
+ }${cacheError ? ` (${cacheError.code ?? cacheError.message})` : ''}. ` +
158
201
  `Loading from original location instead. ` +
159
202
  `This may cause file locking issues on Windows.`
160
203
  );
@@ -610,6 +610,7 @@ module.exports.getBinaryTarget = nativeBinding.getBinaryTarget
610
610
  module.exports.getDefaultMaxCacheSize = nativeBinding.getDefaultMaxCacheSize
611
611
  module.exports.getEventDimensions = nativeBinding.getEventDimensions
612
612
  module.exports.getFilesForOutputsBatch = nativeBinding.getFilesForOutputsBatch
613
+ module.exports.getHardcodedIgnorePatterns = nativeBinding.getHardcodedIgnorePatterns
613
614
  module.exports.getMainWorktreeRoot = nativeBinding.getMainWorktreeRoot
614
615
  module.exports.getTransformableOutputs = nativeBinding.getTransformableOutputs
615
616
  module.exports.GroupType = nativeBinding.GroupType
@@ -625,6 +626,8 @@ module.exports.isEditorInstalled = nativeBinding.isEditorInstalled
625
626
  module.exports.killProcessTree = nativeBinding.killProcessTree
626
627
  module.exports.killProcessTreeGraceful = nativeBinding.killProcessTreeGraceful
627
628
  module.exports.logDebug = nativeBinding.logDebug
629
+ module.exports.matchGlobPaths = nativeBinding.matchGlobPaths
630
+ module.exports.matchOutputPaths = nativeBinding.matchOutputPaths
628
631
  module.exports.parseTaskStatus = nativeBinding.parseTaskStatus
629
632
  module.exports.remove = nativeBinding.remove
630
633
  module.exports.restoreTerminal = nativeBinding.restoreTerminal
@@ -1 +1,17 @@
1
- export declare function getNativeFileCacheLocation(): string;
1
+ /**
2
+ * Path of the current user's native binary cache. Nothing about the constant is
3
+ * owner-only — `ensureSecureNativeFileCacheLocation` is what establishes that,
4
+ * and on Windows nothing does, since the OS temp dir is already per-account.
5
+ */
6
+ export declare const NATIVE_CACHE_ROOT: string;
7
+ export declare function getNativeFileCacheLocationToDelete(): string | null;
8
+ /**
9
+ * Create the native file cache dir, or return `null` if it cannot be created
10
+ * *securely* — in which case the caller loads the binding in place.
11
+ *
12
+ * The stable top-level container is verified as safe for private children. The
13
+ * uid directory and every directory loaded through are owner-only.
14
+ * `ensureOwnedPrivateDir` refuses a directory or symlink another local user
15
+ * planted before us.
16
+ */
17
+ export declare function ensureSecureNativeFileCacheLocation(cacheRoot?: string): string | null;
@@ -1,23 +1,97 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getNativeFileCacheLocation = getNativeFileCacheLocation;
4
- const os_1 = require("os");
3
+ exports.NATIVE_CACHE_ROOT = void 0;
4
+ exports.getNativeFileCacheLocationToDelete = getNativeFileCacheLocationToDelete;
5
+ exports.ensureSecureNativeFileCacheLocation = ensureSecureNativeFileCacheLocation;
5
6
  const path_1 = require("path");
6
- const crypto_1 = require("crypto");
7
- const workspace_root_1 = require("../utils/workspace-root");
7
+ const fs_1 = require("fs");
8
+ const nx_tmp_dir_1 = require("../utils/nx-tmp-dir");
8
9
  const versions_1 = require("../utils/versions");
9
- function getNativeFileCacheLocation() {
10
- if (process.env.NX_NATIVE_FILE_CACHE_DIRECTORY) {
11
- return process.env.NX_NATIVE_FILE_CACHE_DIRECTORY;
10
+ const owned_private_dir_1 = require("../utils/owned-private-dir");
11
+ /**
12
+ * Path of the current user's native binary cache. Nothing about the constant is
13
+ * owner-only — `ensureSecureNativeFileCacheLocation` is what establishes that,
14
+ * and on Windows nothing does, since the OS temp dir is already per-account.
15
+ */
16
+ exports.NATIVE_CACHE_ROOT = (0, path_1.join)(nx_tmp_dir_1.NX_USER_TMP_DIR, 'native-cache');
17
+ /**
18
+ * The configured cache dir, normalized. `resolve` strips a trailing slash, which
19
+ * would otherwise defeat the guards downstream: `lstat` on a path ending in `/`
20
+ * resolves a symlink rather than reporting it, and `O_NOFOLLOW` then opens the
21
+ * target. `NX_SOCKET_DIR` is normalized for the same reason.
22
+ */
23
+ function configuredCacheDir() {
24
+ const dir = process.env.NX_NATIVE_FILE_CACHE_DIRECTORY;
25
+ return dir ? (0, path_1.resolve)(dir) : undefined;
26
+ }
27
+ function getNativeFileCacheLocationToDelete() {
28
+ const configured = configuredCacheDir();
29
+ if (configured !== undefined) {
30
+ // Checked for ownership before it is handed to a recursive delete: being
31
+ // configured does not make it ours. Weaker than the load path below, which
32
+ // also enforces mode and validates the version directory.
33
+ return (0, owned_private_dir_1.isOwnedRealDirectory)(configured) ? configured : null;
12
34
  }
13
- else {
14
- const hash = (0, crypto_1.createHash)('sha256').update(workspace_root_1.workspaceRoot).update(versions_1.nxVersion);
35
+ return (0, owned_private_dir_1.isSafeSharedRoot)(nx_tmp_dir_1.NX_TMP_DIR).status === 'ok' &&
36
+ (0, owned_private_dir_1.isOwnedRealDirectory)(nx_tmp_dir_1.NX_USER_TMP_DIR) &&
37
+ (0, owned_private_dir_1.isOwnedRealDirectory)(exports.NATIVE_CACHE_ROOT)
38
+ ? (0, path_1.join)(exports.NATIVE_CACHE_ROOT, versions_1.nxVersion)
39
+ : null;
40
+ }
41
+ /**
42
+ * Create the native file cache dir, or return `null` if it cannot be created
43
+ * *securely* — in which case the caller loads the binding in place.
44
+ *
45
+ * The stable top-level container is verified as safe for private children. The
46
+ * uid directory and every directory loaded through are owner-only.
47
+ * `ensureOwnedPrivateDir` refuses a directory or symlink another local user
48
+ * planted before us.
49
+ */
50
+ function ensureSecureNativeFileCacheLocation(
51
+ // Test seam: lets a spec plant a hostile directory under a root it controls.
52
+ cacheRoot = exports.NATIVE_CACHE_ROOT) {
53
+ const configured = configuredCacheDir();
54
+ if (configured !== undefined) {
55
+ const dir = configured;
15
56
  try {
16
- hash.update((0, os_1.userInfo)().username);
57
+ // Held to the same 0700 bar as the default location: a `.node` is loaded
58
+ // out of here. Unlike NX_SOCKET_DIR it is not additionally refused for
59
+ // naming one of Nx's own roots, which is the only case that throws.
60
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(dir), { recursive: true });
61
+ const established = (0, owned_private_dir_1.ensureOwnedPrivateDir)(dir);
62
+ if (established.status !== 'ok') {
63
+ // The guard's own reason rather than a fixed sentence: `not-created
64
+ // (EACCES)` and `foreign-owner` are exactly what the catch below
65
+ // promises to tell apart, and one string for all of them cannot.
66
+ throw new owned_private_dir_1.DirectoryRefusedError(established.refusal);
67
+ }
68
+ return dir;
17
69
  }
18
70
  catch (e) {
19
- // if there's no user, we only use the workspace root for the hash and move on
71
+ // Never discard a configured directory silently — the socket directory
72
+ // follows the same rule. A typo, EACCES or EROFS is otherwise
73
+ // indistinguishable from a working cache.
74
+ console.warn(`Nx could not use the configured native file cache directory ${dir} (${e?.code ?? e?.message ?? e}). Loading the native binding in place instead.`);
75
+ return null;
76
+ }
77
+ }
78
+ const userRoot = (0, path_1.dirname)(cacheRoot);
79
+ const sharedRoot = (0, path_1.dirname)(userRoot);
80
+ // Outermost first: the stable shared container must either belong to root or
81
+ // to us, and must be sticky if peers can write there. The uid directory then
82
+ // becomes the owner-only boundary for sockets and native-cache alike.
83
+ if ((0, owned_private_dir_1.ensureSafeSharedRoot)(sharedRoot).status !== 'ok') {
84
+ return null;
85
+ }
86
+ for (const root of [userRoot, cacheRoot]) {
87
+ if ((0, owned_private_dir_1.ensureOwnedPrivateDir)(root).status !== 'ok') {
88
+ return null;
20
89
  }
21
- return (0, path_1.join)((0, os_1.tmpdir)(), `nx-native-file-cache-${hash.digest('hex').substring(0, 7)}`);
22
90
  }
91
+ // Verified, not assumed: this is the directory we load a `.node` out of.
92
+ const versionDir = (0, path_1.join)(cacheRoot, versions_1.nxVersion);
93
+ if ((0, owned_private_dir_1.ensureOwnedPrivateDir)(versionDir).status !== 'ok') {
94
+ return null;
95
+ }
96
+ return versionDir;
23
97
  }
Binary file
@@ -5,12 +5,15 @@ exports.getCloudUrl = getCloudUrl;
5
5
  exports.removeTrailingSlash = removeTrailingSlash;
6
6
  exports.isNxCloudId = isNxCloudId;
7
7
  const nx_json_1 = require("../../config/nx-json");
8
- const run_command_1 = require("../../tasks-runner/run-command");
9
8
  const workspace_root_1 = require("../../utils/workspace-root");
10
9
  function getCloudOptions(directory = workspace_root_1.workspaceRoot) {
10
+ // Required lazily: this module is reachable from the @nx/devkit/internal
11
+ // barrel that plugin workers load, and run-command eagerly pulls in the whole
12
+ // task-execution subsystem.
13
+ const { getRunnerOptions, } = require('../../tasks-runner/run-command');
11
14
  const nxJson = (0, nx_json_1.readNxJson)(directory);
12
15
  // TODO: The default is not always cloud? But it's not handled at the moment
13
- return (0, run_command_1.getRunnerOptions)('default', nxJson, {}, true);
16
+ return getRunnerOptions('default', nxJson, {}, true);
14
17
  }
15
18
  function getCloudUrl() {
16
19
  return removeTrailingSlash(process.env.NX_CLOUD_API || process.env.NRWL_API || `https://cloud.nx.app`);
@@ -5,7 +5,6 @@ exports.getNxCloudAppOnBoardingUrl = getNxCloudAppOnBoardingUrl;
5
5
  exports.readNxCloudToken = readNxCloudToken;
6
6
  const is_workspace_claimed_1 = require("./is-workspace-claimed");
7
7
  const url_shorten_1 = require("./url-shorten");
8
- const run_command_1 = require("../../tasks-runner/run-command");
9
8
  const nx_json_1 = require("../../generators/utils/nx-json");
10
9
  async function createNxCloudOnboardingURLForWelcomeApp(tree, token) {
11
10
  token = token || readNxCloudToken(tree);
@@ -22,7 +21,11 @@ async function getNxCloudAppOnBoardingUrl(token) {
22
21
  return onboardingUrl;
23
22
  }
24
23
  function readNxCloudToken(tree) {
24
+ // Required lazily: this module is re-exported from the @nx/devkit/internal
25
+ // barrel that plugin workers load, and run-command eagerly pulls in the whole
26
+ // task-execution subsystem.
27
+ const { getRunnerOptions, } = require('../../tasks-runner/run-command');
25
28
  const nxJson = (0, nx_json_1.readNxJson)(tree);
26
- const { accessToken, nxCloudId } = (0, run_command_1.getRunnerOptions)('default', nxJson, {}, true);
29
+ const { accessToken, nxCloudId } = getRunnerOptions('default', nxJson, {}, true);
27
30
  return accessToken || nxCloudId;
28
31
  }
@@ -12,6 +12,16 @@ const project_graph_1 = require("../../../config/project-graph");
12
12
  const file_hasher_1 = require("../../../hasher/file-hasher");
13
13
  const project_graph_builder_1 = require("../../../project-graph/project-graph-builder");
14
14
  const json_1 = require("../../../utils/json");
15
+ // Highest bun.lock version this parser has been verified against:
16
+ // - 1: workspace packages are no longer listed with their dependencies
17
+ // - 2: identical content, stricter validation in Bun's own reader
18
+ // - 3: values in "overrides" may be objects holding rules scoped to a parent
19
+ // package (`"parent": { "child": "1.0.0" }`) and keys may carry a version
20
+ // selector (`"parent@1"`, `"child@<2"`). This parser never reads
21
+ // "overrides"; the resolved packages are listed in "packages" as before.
22
+ // Newer versions are parsed on a best-effort basis instead of being rejected
23
+ // outright.
24
+ const MAX_KNOWN_LOCKFILE_VERSION = 3;
15
25
  const DEPENDENCY_TYPES = [
16
26
  'dependencies',
17
27
  'devDependencies',
@@ -356,9 +366,12 @@ function parseLockFile(lockFileContent, lockFileHash) {
356
366
  if (typeof result.lockfileVersion !== 'number') {
357
367
  throw new Error(`Lockfile version must be a number, got ${typeof result.lockfileVersion}`);
358
368
  }
359
- const supportedVersions = [0, 1];
360
- if (!supportedVersions.includes(result.lockfileVersion)) {
361
- throw new Error(`Unsupported lockfile version ${result.lockfileVersion}. Supported versions: ${supportedVersions.join(', ')}`);
369
+ if (!Number.isInteger(result.lockfileVersion) ||
370
+ result.lockfileVersion < 0) {
371
+ throw new Error(`Unsupported lockfile version ${result.lockfileVersion}. Lockfile version must be a non-negative integer`);
372
+ }
373
+ if (result.lockfileVersion > MAX_KNOWN_LOCKFILE_VERSION) {
374
+ console.warn(`bun.lock has lockfileVersion ${result.lockfileVersion}, which is newer than the version ${MAX_KNOWN_LOCKFILE_VERSION} this version of Nx was tested with. Attempting to parse it anyway.`);
362
375
  }
363
376
  }
364
377
  if (!result.packages || typeof result.packages !== 'object') {
@@ -616,7 +629,7 @@ function processPackageForDependencies(packageKey, packageData, lockFile, index,
616
629
  const deps = packageDependencies[depType];
617
630
  if (!deps || typeof deps !== 'object')
618
631
  continue;
619
- const depDependencies = processDependencyEntries(deps, sourceNodeName, index, ctx, workspacePackages, lockFile.manifests);
632
+ const depDependencies = processDependencyEntries(deps, packageKey, sourceNodeName, lockFile, index, ctx, workspacePackages);
620
633
  dependencies.push(...depDependencies);
621
634
  }
622
635
  return dependencies;
@@ -634,12 +647,12 @@ function extractPackageDependencies(packageData) {
634
647
  }
635
648
  return undefined;
636
649
  }
637
- function processDependencyEntries(deps, sourceNodeName, index, ctx, workspacePackages, manifests) {
650
+ function processDependencyEntries(deps, sourcePackageKey, sourceNodeName, lockFile, index, ctx, workspacePackages) {
638
651
  const dependencies = [];
639
652
  const depsEntries = Object.entries(deps);
640
653
  for (const [packageName, versionSpec] of depsEntries) {
641
654
  try {
642
- const dependency = processSingleDependency(packageName, versionSpec, sourceNodeName, index, ctx, workspacePackages, manifests);
655
+ const dependency = processSingleDependency(packageName, versionSpec, sourcePackageKey, sourceNodeName, lockFile, index, ctx, workspacePackages);
643
656
  if (dependency) {
644
657
  dependencies.push(dependency);
645
658
  }
@@ -650,7 +663,7 @@ function processDependencyEntries(deps, sourceNodeName, index, ctx, workspacePac
650
663
  }
651
664
  return dependencies;
652
665
  }
653
- function processSingleDependency(packageName, versionSpec, sourceNodeName, index, ctx, workspacePackages, manifests) {
666
+ function processSingleDependency(packageName, versionSpec, sourcePackageKey, sourceNodeName, lockFile, index, ctx, workspacePackages) {
654
667
  if (typeof packageName !== 'string' || typeof versionSpec !== 'string') {
655
668
  return null;
656
669
  }
@@ -663,14 +676,22 @@ function processSingleDependency(packageName, versionSpec, sourceNodeName, index
663
676
  }
664
677
  let targetPackageName = packageName;
665
678
  let targetVersion = versionSpec;
666
- const aliasTarget = resolveAliasTarget(versionSpec);
667
- if (aliasTarget) {
679
+ // A "<parent>/<name>" entry records what this parent actually resolved the
680
+ // dependency to. Overrides scoped to a parent (bun.lock v3) can pick a
681
+ // version outside the declared range, so it takes precedence over matching
682
+ // the range against the versions in the lockfile.
683
+ const nestedVersion = resolveNestedVersion(sourcePackageKey, packageName, lockFile);
684
+ const aliasTarget = nestedVersion ? null : resolveAliasTarget(versionSpec);
685
+ if (nestedVersion) {
686
+ targetVersion = nestedVersion;
687
+ }
688
+ else if (aliasTarget) {
668
689
  targetPackageName = aliasTarget.packageName;
669
690
  targetVersion = aliasTarget.version;
670
691
  }
671
692
  else {
672
693
  // O(1) lookup using index instead of O(n) scan
673
- const resolvedVersion = findResolvedVersion(packageName, versionSpec, index, manifests);
694
+ const resolvedVersion = findResolvedVersion(packageName, versionSpec, index, lockFile.manifests);
674
695
  if (!resolvedVersion) {
675
696
  return null;
676
697
  }
@@ -693,6 +714,18 @@ function processSingleDependency(packageName, versionSpec, sourceNodeName, index
693
714
  return null;
694
715
  }
695
716
  }
717
+ function resolveNestedVersion(sourcePackageKey, packageName, lockFile) {
718
+ const nestedEntry = lockFile.packages[`${sourcePackageKey}/${packageName}`];
719
+ if (!Array.isArray(nestedEntry) || typeof nestedEntry[0] !== 'string') {
720
+ return null;
721
+ }
722
+ const { name, version } = getCachedSpecInfo(nestedEntry[0]);
723
+ // aliases ("alias@npm:real@1.0.0") are resolved to the real package by the caller
724
+ if (name !== packageName || !version || version.startsWith('npm:')) {
725
+ return null;
726
+ }
727
+ return version;
728
+ }
696
729
  function resolveTargetNodeName(targetPackageName, targetVersion, ctx) {
697
730
  const hoistedNodeName = `npm:${targetPackageName}`;
698
731
  const versionedNodeName = `npm:${targetPackageName}@${targetVersion}`;