nx 23.0.0-beta.21 → 23.0.0-beta.22

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 (48) hide show
  1. package/dist/src/adapter/compat.d.ts +1 -1
  2. package/dist/src/adapter/compat.js +1 -0
  3. package/dist/src/command-line/examples.js +4 -4
  4. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +5 -0
  5. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +1 -0
  6. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +5 -0
  7. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +1 -0
  8. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +5 -0
  9. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +1 -0
  10. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +1 -0
  11. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +15 -0
  12. package/dist/src/command-line/migrate/agentic/run-step.d.ts +7 -0
  13. package/dist/src/command-line/migrate/agentic/run-step.js +3 -1
  14. package/dist/src/command-line/migrate/agentic/select.js +120 -32
  15. package/dist/src/command-line/migrate/command-object.d.ts +42 -0
  16. package/dist/src/command-line/migrate/command-object.js +37 -7
  17. package/dist/src/command-line/migrate/migrate-config.d.ts +27 -0
  18. package/dist/src/command-line/migrate/migrate-config.js +103 -0
  19. package/dist/src/command-line/migrate/migrate.d.ts +37 -2
  20. package/dist/src/command-line/migrate/migrate.js +97 -8
  21. package/dist/src/command-line/release/changelog/version-plan-filtering.d.ts +3 -1
  22. package/dist/src/command-line/release/changelog/version-plan-filtering.js +7 -3
  23. package/dist/src/command-line/release/changelog.d.ts +7 -0
  24. package/dist/src/command-line/release/changelog.js +22 -9
  25. package/dist/src/command-line/release/release.js +65 -55
  26. package/dist/src/command-line/release/utils/git.d.ts +6 -0
  27. package/dist/src/command-line/release/utils/git.js +33 -0
  28. package/dist/src/command-line/release/version/derive-specifier-from-conventional-commits.js +3 -2
  29. package/dist/src/command-line/release/version.d.ts +3 -0
  30. package/dist/src/command-line/release/version.js +13 -3
  31. package/dist/src/config/misc-interfaces.d.ts +8 -0
  32. package/dist/src/config/nx-json.d.ts +49 -0
  33. package/dist/src/core/graph/main.js +1 -1
  34. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  35. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  36. package/dist/src/plugins/js/lock-file/lock-file.d.ts +5 -0
  37. package/dist/src/plugins/js/lock-file/lock-file.js +34 -24
  38. package/dist/src/plugins/js/project-graph/affected/lock-file-changes.d.ts +2 -4
  39. package/dist/src/plugins/js/project-graph/affected/lock-file-changes.js +121 -43
  40. package/dist/src/project-graph/file-utils.d.ts +7 -0
  41. package/dist/src/project-graph/file-utils.js +78 -10
  42. package/dist/src/tasks-runner/init-tasks-runner.d.ts +2 -2
  43. package/dist/src/tasks-runner/init-tasks-runner.js +6 -6
  44. package/dist/src/tasks-runner/task-orchestrator.d.ts +2 -2
  45. package/dist/src/tasks-runner/task-orchestrator.js +6 -6
  46. package/migrations.json +18 -9
  47. package/package.json +11 -11
  48. package/schemas/nx-schema.json +41 -0
Binary file
@@ -8,6 +8,7 @@ import { RawProjectGraphDependency } from '../../../project-graph/project-graph-
8
8
  import { PackageJson } from '../../../utils/package-json';
9
9
  import { PackageManager } from '../../../utils/package-manager';
10
10
  export declare const LOCKFILES: string[];
11
+ export declare const AUTO_AFFECTED_LOCK_FILES: readonly ["yarn.lock", "package-lock.json", "pnpm-lock.yaml", "pnpm-lock.yml", "bun.lockb", "bun.lock"];
11
12
  /**
12
13
  * Parses lock file and maps dependencies and metadata to {@link LockFileGraph}
13
14
  */
@@ -15,6 +16,10 @@ export declare function getLockFileNodes(packageManager: PackageManager, content
15
16
  nodes: Record<string, ProjectGraphExternalNode>;
16
17
  keyMap: Map<string, any>;
17
18
  };
19
+ export declare function getLockFileNodesForName(lockFile: string, contents: string, lockFileHash: string, packageJson?: PackageJson): {
20
+ nodes: Record<string, ProjectGraphExternalNode>;
21
+ keyMap: Map<string, any>;
22
+ };
18
23
  /**
19
24
  * Parses lock file and maps dependencies and metadata to {@link LockFileGraph}
20
25
  */
@@ -4,8 +4,9 @@
4
4
  * It encapsulates the package manager specific logic and implementation details.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.LOCKFILES = void 0;
7
+ exports.AUTO_AFFECTED_LOCK_FILES = exports.LOCKFILES = void 0;
8
8
  exports.getLockFileNodes = getLockFileNodes;
9
+ exports.getLockFileNodesForName = getLockFileNodesForName;
9
10
  exports.getLockFileDependencies = getLockFileDependencies;
10
11
  exports.lockFileExists = lockFileExists;
11
12
  exports.getLockFileName = getLockFileName;
@@ -28,6 +29,7 @@ const yarn_parser_1 = require("./yarn-parser");
28
29
  const YARN_LOCK_FILE = 'yarn.lock';
29
30
  const NPM_LOCK_FILE = 'package-lock.json';
30
31
  const PNPM_LOCK_FILE = 'pnpm-lock.yaml';
32
+ const PNPM_LOCK_FILE_LEGACY = 'pnpm-lock.yml';
31
33
  exports.LOCKFILES = [
32
34
  YARN_LOCK_FILE,
33
35
  NPM_LOCK_FILE,
@@ -35,6 +37,14 @@ exports.LOCKFILES = [
35
37
  bun_parser_1.BUN_LOCK_FILE,
36
38
  bun_parser_1.BUN_TEXT_LOCK_FILE,
37
39
  ];
40
+ exports.AUTO_AFFECTED_LOCK_FILES = [
41
+ YARN_LOCK_FILE,
42
+ NPM_LOCK_FILE,
43
+ PNPM_LOCK_FILE,
44
+ PNPM_LOCK_FILE_LEGACY,
45
+ bun_parser_1.BUN_LOCK_FILE,
46
+ bun_parser_1.BUN_TEXT_LOCK_FILE,
47
+ ];
38
48
  const YARN_LOCK_PATH = (0, node_path_1.join)(workspace_root_1.workspaceRoot, YARN_LOCK_FILE);
39
49
  const NPM_LOCK_PATH = (0, node_path_1.join)(workspace_root_1.workspaceRoot, NPM_LOCK_FILE);
40
50
  const PNPM_LOCK_PATH = (0, node_path_1.join)(workspace_root_1.workspaceRoot, PNPM_LOCK_FILE);
@@ -45,29 +55,10 @@ const BUN_TEXT_LOCK_PATH = (0, node_path_1.join)(workspace_root_1.workspaceRoot,
45
55
  */
46
56
  function getLockFileNodes(packageManager, contents, lockFileHash, context) {
47
57
  try {
48
- if (packageManager === 'yarn') {
49
- const packageJson = (0, fileutils_1.readJsonFile)((0, node_path_1.join)(context.workspaceRoot, 'package.json'));
50
- return (0, yarn_parser_1.getYarnLockfileNodes)(contents, lockFileHash, packageJson);
51
- }
52
- if (packageManager === 'pnpm') {
53
- return (0, pnpm_parser_1.getPnpmLockfileNodes)(contents, lockFileHash);
54
- }
55
- if (packageManager === 'npm') {
56
- return (0, npm_parser_1.getNpmLockfileNodes)(contents, lockFileHash);
57
- }
58
- if (packageManager === 'bun') {
59
- const lockFilePath = getLockFilePath(packageManager);
60
- if (lockFilePath.endsWith(bun_parser_1.BUN_TEXT_LOCK_FILE)) {
61
- // Use new text-based parser
62
- const nodes = (0, bun_parser_1.getBunTextLockfileNodes)(contents, lockFileHash);
63
- return { nodes, keyMap: new Map() };
64
- }
65
- else {
66
- // Fallback to yarn parser for binary format
67
- const packageJson = (0, fileutils_1.readJsonFile)((0, node_path_1.join)(context.workspaceRoot, 'package.json'));
68
- return (0, yarn_parser_1.getYarnLockfileNodes)(contents, lockFileHash, packageJson);
69
- }
70
- }
58
+ const packageJson = packageManager === 'yarn' || packageManager === 'bun'
59
+ ? (0, fileutils_1.readJsonFile)((0, node_path_1.join)(context.workspaceRoot, 'package.json'))
60
+ : undefined;
61
+ return getLockFileNodesForName(getLockFileName(packageManager), contents, lockFileHash, packageJson);
71
62
  }
72
63
  catch (e) {
73
64
  if (!isPostInstallProcess()) {
@@ -80,6 +71,25 @@ function getLockFileNodes(packageManager, contents, lockFileHash, context) {
80
71
  }
81
72
  throw new Error(`Unknown package manager: ${packageManager}`);
82
73
  }
74
+ function getLockFileNodesForName(lockFile, contents, lockFileHash, packageJson) {
75
+ if (lockFile === YARN_LOCK_FILE || lockFile === bun_parser_1.BUN_LOCK_FILE) {
76
+ // yarn-parser only reads optional fields plus an unused `name` for the
77
+ // synthetic root workspace node, which is identical across base/head and
78
+ // therefore irrelevant for affected diffing.
79
+ return (0, yarn_parser_1.getYarnLockfileNodes)(contents, lockFileHash, packageJson ?? {});
80
+ }
81
+ if (lockFile === PNPM_LOCK_FILE || lockFile === PNPM_LOCK_FILE_LEGACY) {
82
+ return (0, pnpm_parser_1.getPnpmLockfileNodes)(contents, lockFileHash);
83
+ }
84
+ if (lockFile === NPM_LOCK_FILE) {
85
+ return (0, npm_parser_1.getNpmLockfileNodes)(contents, lockFileHash);
86
+ }
87
+ if (lockFile === bun_parser_1.BUN_TEXT_LOCK_FILE) {
88
+ const nodes = (0, bun_parser_1.getBunTextLockfileNodes)(contents, lockFileHash);
89
+ return { nodes, keyMap: new Map() };
90
+ }
91
+ throw new Error(`Unknown lock file: ${lockFile}`);
92
+ }
83
93
  /**
84
94
  * Parses lock file and maps dependencies and metadata to {@link LockFileGraph}
85
95
  */
@@ -1,5 +1,3 @@
1
1
  import { TouchedProjectLocator } from '../../../../project-graph/affected/affected-project-graph-models';
2
- import { WholeFileChange } from '../../../../project-graph/file-utils';
3
- import { JsonChange } from '../../../../utils/json-diff';
4
- export declare const PNPM_LOCK_FILES: string[];
5
- export declare const getTouchedProjectsFromLockFile: TouchedProjectLocator<WholeFileChange | JsonChange>;
2
+ import { LockFileChange, WholeFileChange } from '../../../../project-graph/file-utils';
3
+ export declare const getTouchedProjectsFromLockFile: TouchedProjectLocator<WholeFileChange | LockFileChange>;
@@ -1,26 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getTouchedProjectsFromLockFile = exports.PNPM_LOCK_FILES = void 0;
4
- const configuration_1 = require("../../../../config/configuration");
5
- const json_diff_1 = require("../../../../utils/json-diff");
3
+ exports.getTouchedProjectsFromLockFile = void 0;
4
+ const file_utils_1 = require("../../../../project-graph/file-utils");
6
5
  const config_1 = require("../../utils/config");
7
6
  const find_matching_projects_1 = require("../../../../utils/find-matching-projects");
8
- exports.PNPM_LOCK_FILES = ['pnpm-lock.yaml', 'pnpm-lock.yml'];
9
- const ALL_LOCK_FILES = [
10
- ...exports.PNPM_LOCK_FILES,
11
- 'package-lock.json',
12
- 'yarn.lock',
13
- 'bun.lockb',
14
- 'bun.lock',
15
- ];
16
- const getTouchedProjectsFromLockFile = (fileChanges, projectGraphNodes) => {
17
- const nxJson = (0, configuration_1.readNxJson)();
7
+ const file_hasher_1 = require("../../../../hasher/file-hasher");
8
+ const output_1 = require("../../../../utils/output");
9
+ const lock_file_1 = require("../../lock-file/lock-file");
10
+ const getTouchedProjectsFromLockFile = (fileChanges, projectGraphNodes, nxJson, packageJson, projectGraph) => {
18
11
  const { projectsAffectedByDependencyUpdates } = (0, config_1.jsPluginConfig)(nxJson);
19
- const changedLockFile = fileChanges.find((f) => ALL_LOCK_FILES.includes(f.file));
12
+ const changedLockFile = fileChanges.find((f) => lock_file_1.AUTO_AFFECTED_LOCK_FILES.includes(f.file));
20
13
  if (projectsAffectedByDependencyUpdates === 'auto') {
21
- const changedProjectPaths = getProjectPathsAffectedByDependencyUpdates(changedLockFile);
22
- const changedProjectNames = getProjectsNamesFromPaths(projectGraphNodes, changedProjectPaths);
23
- return changedProjectNames;
14
+ return getAutoAffected(changedLockFile, projectGraphNodes, projectGraph, packageJson);
24
15
  }
25
16
  else if (Array.isArray(projectsAffectedByDependencyUpdates)) {
26
17
  return (0, find_matching_projects_1.findMatchingProjects)(projectsAffectedByDependencyUpdates, projectGraphNodes);
@@ -32,41 +23,128 @@ const getTouchedProjectsFromLockFile = (fileChanges, projectGraphNodes) => {
32
23
  };
33
24
  exports.getTouchedProjectsFromLockFile = getTouchedProjectsFromLockFile;
34
25
  /**
35
- * For pnpm projects, check lock file for changes to importers and return the project paths that have changes.
26
+ * In auto mode, parse the lock file at the base and head revisions
27
+ * using Nx's existing lock file parsers, then diff the resulting
28
+ * external-node maps to determine which packages actually changed.
29
+ *
30
+ * Returns external node names (e.g. "npm:lodash@4.17.21") so the
31
+ * graph reversal in filterAffected can walk back to workspace projects.
36
32
  */
37
- const getProjectPathsAffectedByDependencyUpdates = (changedLockFile) => {
33
+ function getAutoAffected(changedLockFile, projectGraphNodes, projectGraph, packageJson) {
34
+ const allProjectNames = Object.values(projectGraphNodes).map((p) => p.name);
38
35
  if (!changedLockFile) {
39
36
  return [];
40
37
  }
41
- const changedProjectPaths = new Set();
42
- if (exports.PNPM_LOCK_FILES.includes(changedLockFile.file)) {
43
- for (const change of changedLockFile.getChanges()) {
44
- if ((0, json_diff_1.isJsonChange)(change) &&
45
- change.path[0] === 'importers' &&
46
- change.path[1] !== undefined) {
47
- changedProjectPaths.add(change.path[1]);
38
+ const changes = changedLockFile.getChanges();
39
+ // A WholeFileChange means we were unable to read both revisions of
40
+ // the lock file (e.g. missing base revision, git error). Fall back
41
+ // to marking all projects affected.
42
+ if (!changes.every(file_utils_1.isLockFileChange)) {
43
+ return allProjectNames;
44
+ }
45
+ const changedPackageNames = getChangedPackageNames(changedLockFile.file, changes, packageJson);
46
+ if (changedPackageNames === null) {
47
+ return allProjectNames;
48
+ }
49
+ if (changedPackageNames.size === 0) {
50
+ return [];
51
+ }
52
+ // Look up the changed packages in the project graph's external nodes
53
+ // and return the external node names. The graph reversal in
54
+ // filterAffected walks from these nodes to workspace projects.
55
+ const { touchedNodeNames, missingPackageNames } = findExternalNodesByPackageName(changedPackageNames, projectGraph.externalNodes ?? {});
56
+ if (missingPackageNames.size > 0) {
57
+ return allProjectNames;
58
+ }
59
+ return touchedNodeNames;
60
+ }
61
+ /**
62
+ * Parse the base and head revisions of the lock file with Nx's
63
+ * existing parsers and diff the resulting package -> version maps.
64
+ *
65
+ * Returns the set of changed package names, or null if parsing
66
+ * failed (in which case the caller should fall back to all projects).
67
+ */
68
+ function getChangedPackageNames(file, changes, packageJson) {
69
+ try {
70
+ const changed = new Set();
71
+ // calculateFileChanges emits a single LockFileChange per lock file, but
72
+ // the iteration keeps the contract open in case multiple ranges are ever
73
+ // emitted for the same file.
74
+ for (const change of changes) {
75
+ const baseFingerprints = collectPackageFingerprints((0, lock_file_1.getLockFileNodesForName)(file, change.baseContent, (0, file_hasher_1.hashArray)([change.baseContent]), packageJson).nodes);
76
+ const headFingerprints = collectPackageFingerprints((0, lock_file_1.getLockFileNodesForName)(file, change.headContent, (0, file_hasher_1.hashArray)([change.headContent]), packageJson).nodes);
77
+ for (const [name, fingerprints] of headFingerprints) {
78
+ const baseSet = baseFingerprints.get(name);
79
+ if (!baseSet || !setsEqual(baseSet, fingerprints)) {
80
+ changed.add(name);
81
+ }
82
+ }
83
+ for (const name of baseFingerprints.keys()) {
84
+ if (!headFingerprints.has(name)) {
85
+ changed.add(name);
86
+ }
48
87
  }
49
88
  }
89
+ return changed;
50
90
  }
51
- return Array.from(changedProjectPaths);
52
- };
53
- const getProjectsNamesFromPaths = (projectGraphNodes, projectPaths) => {
54
- if (!projectPaths.length) {
55
- return [];
91
+ catch (e) {
92
+ output_1.output.warn({
93
+ title: `Failed to parse "${file}" for projectsAffectedByDependencyUpdates "auto" mode. All projects will be marked as affected.`,
94
+ bodyLines: [e instanceof Error ? e.message : String(e)],
95
+ });
96
+ return null;
56
97
  }
57
- const lookup = new RootPathLookup(projectGraphNodes);
58
- return projectPaths
59
- .map((path) => lookup.findNodeNameByRoot(path))
60
- .filter(Boolean);
61
- };
62
- class RootPathLookup {
63
- constructor(nodes) {
64
- this.rootToNameMap = new Map();
65
- Object.entries(nodes).forEach(([name, node]) => {
66
- this.rootToNameMap.set(node.data.root, name);
98
+ }
99
+ /**
100
+ * Build a map of packageName -> set of versions present in the
101
+ * external-node record returned by a lock-file parser. We include both
102
+ * version and hash so patched/tarball/integrity-only changes still
103
+ * count as lockfile changes even when the semver stays the same.
104
+ */
105
+ function collectPackageFingerprints(nodes) {
106
+ const fingerprints = new Map();
107
+ for (const node of Object.values(nodes ?? {})) {
108
+ const name = node.data?.packageName;
109
+ if (!name)
110
+ continue;
111
+ const fingerprint = JSON.stringify({
112
+ version: node.data.version ?? '',
113
+ hash: node.data.hash ?? '',
67
114
  });
115
+ let set = fingerprints.get(name);
116
+ if (!set) {
117
+ set = new Set();
118
+ fingerprints.set(name, set);
119
+ }
120
+ set.add(fingerprint);
68
121
  }
69
- findNodeNameByRoot(root) {
70
- return this.rootToNameMap.get(root);
122
+ return fingerprints;
123
+ }
124
+ function setsEqual(a, b) {
125
+ if (a.size !== b.size)
126
+ return false;
127
+ for (const value of a) {
128
+ if (!b.has(value))
129
+ return false;
130
+ }
131
+ return true;
132
+ }
133
+ /**
134
+ * Given a set of package names, find all matching external node names
135
+ * in the project graph.
136
+ */
137
+ function findExternalNodesByPackageName(packageNames, externalNodes) {
138
+ const touchedNodeNames = [];
139
+ const matchedPackageNames = new Set();
140
+ for (const [name, node] of Object.entries(externalNodes)) {
141
+ if (packageNames.has(node.data.packageName)) {
142
+ touchedNodeNames.push(name);
143
+ matchedPackageNames.add(node.data.packageName);
144
+ }
71
145
  }
146
+ return {
147
+ touchedNodeNames,
148
+ missingPackageNames: new Set(Array.from(packageNames).filter((name) => !matchedPackageNames.has(name))),
149
+ };
72
150
  }
@@ -13,8 +13,15 @@ export declare class WholeFileChange implements Change {
13
13
  export declare class DeletedFileChange implements Change {
14
14
  type: string;
15
15
  }
16
+ export declare class LockFileChange implements Change {
17
+ baseContent: string;
18
+ headContent: string;
19
+ type: string;
20
+ constructor(baseContent: string, headContent: string);
21
+ }
16
22
  export declare function isWholeFileChange(change: Change): change is WholeFileChange;
17
23
  export declare function isDeletedFileChange(change: Change): change is DeletedFileChange;
24
+ export declare function isLockFileChange(change: Change): change is LockFileChange;
18
25
  export declare function calculateFileChanges(files: string[], nxArgs?: NxArgs, readFileAtRevision?: {
19
26
  (f: string, r: string | void): string;
20
27
  }, ignore?: ReturnType<typeof ignore>): FileChange[];
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TEN_MEGABYTES = exports.DeletedFileChange = exports.WholeFileChange = void 0;
3
+ exports.TEN_MEGABYTES = exports.LockFileChange = exports.DeletedFileChange = exports.WholeFileChange = void 0;
4
4
  exports.isWholeFileChange = isWholeFileChange;
5
5
  exports.isDeletedFileChange = isDeletedFileChange;
6
+ exports.isLockFileChange = isLockFileChange;
6
7
  exports.calculateFileChanges = calculateFileChanges;
7
8
  exports.defaultFileRead = defaultFileRead;
8
9
  exports.readPackageJson = readPackageJson;
9
10
  const child_process_1 = require("child_process");
10
11
  const fs_1 = require("fs");
12
+ const os_1 = require("os");
11
13
  const path_1 = require("path");
12
14
  const fileutils_1 = require("../utils/fileutils");
13
15
  const ignore_1 = require("../utils/ignore");
@@ -25,16 +27,36 @@ class DeletedFileChange {
25
27
  }
26
28
  }
27
29
  exports.DeletedFileChange = DeletedFileChange;
30
+ class LockFileChange {
31
+ constructor(baseContent, headContent) {
32
+ this.baseContent = baseContent;
33
+ this.headContent = headContent;
34
+ this.type = 'LockFileChange';
35
+ }
36
+ }
37
+ exports.LockFileChange = LockFileChange;
28
38
  function isWholeFileChange(change) {
29
39
  return change.type === 'WholeFileChange';
30
40
  }
31
41
  function isDeletedFileChange(change) {
32
42
  return change.type === 'WholeFileDeleted';
33
43
  }
44
+ function isLockFileChange(change) {
45
+ return change.type === 'LockFileChange';
46
+ }
47
+ const TEXT_LOCK_FILES = new Set([
48
+ 'yarn.lock',
49
+ 'package-lock.json',
50
+ 'pnpm-lock.yaml',
51
+ 'pnpm-lock.yml',
52
+ 'bun.lock',
53
+ ]);
54
+ const BINARY_LOCK_FILES = new Set(['bun.lockb']);
34
55
  function calculateFileChanges(files, nxArgs, readFileAtRevision = defaultReadFileAtRevision, ignore = (0, ignore_1.getIgnoreObject)()) {
35
56
  files = files.filter((f) => !ignore.ignores(f));
36
57
  return files.map((f) => {
37
58
  const ext = (0, path_1.extname)(f);
59
+ const basename = f.split('/').pop() ?? f;
38
60
  return {
39
61
  file: f,
40
62
  getChanges: () => {
@@ -47,6 +69,16 @@ function calculateFileChanges(files, nxArgs, readFileAtRevision = defaultReadFil
47
69
  if (nxArgs.files && nxArgs.files.includes(f)) {
48
70
  return [new WholeFileChange()];
49
71
  }
72
+ if (TEXT_LOCK_FILES.has(basename) || BINARY_LOCK_FILES.has(basename)) {
73
+ try {
74
+ const atBase = readLockFileAtRevision(f, basename, nxArgs.base, readFileAtRevision);
75
+ const atHead = readLockFileAtRevision(f, basename, nxArgs.head, readFileAtRevision);
76
+ return [new LockFileChange(atBase, atHead)];
77
+ }
78
+ catch {
79
+ return [new WholeFileChange()];
80
+ }
81
+ }
50
82
  switch (ext) {
51
83
  case '.json':
52
84
  try {
@@ -75,18 +107,17 @@ function calculateFileChanges(files, nxArgs, readFileAtRevision = defaultReadFil
75
107
  };
76
108
  });
77
109
  }
110
+ function readLockFileAtRevision(file, lockFileName, revision, readFileAtRevision) {
111
+ if (lockFileName === 'bun.lockb' &&
112
+ readFileAtRevision === defaultReadFileAtRevision) {
113
+ return defaultReadBunLockFileAtRevision(file, revision);
114
+ }
115
+ return readFileAtRevision(file, revision);
116
+ }
78
117
  exports.TEN_MEGABYTES = 1024 * 10000;
79
118
  function defaultReadFileAtRevision(file, revision) {
80
119
  try {
81
- const fileFullPath = `${workspace_root_1.workspaceRoot}${path_1.sep}${file}`;
82
- const gitRepositoryPath = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
83
- windowsHide: true,
84
- })
85
- .toString()
86
- .trim();
87
- const filePathInGitRepository = (0, path_1.relative)(gitRepositoryPath, fileFullPath)
88
- .split(path_1.sep)
89
- .join('/');
120
+ const filePathInGitRepository = getFilePathInGitRepository(file);
90
121
  return !revision
91
122
  ? (0, fs_1.readFileSync)(file, 'utf-8')
92
123
  : (0, child_process_1.execSync)(`git show ${revision}:${filePathInGitRepository}`, {
@@ -101,6 +132,43 @@ function defaultReadFileAtRevision(file, revision) {
101
132
  return '';
102
133
  }
103
134
  }
135
+ function defaultReadBunLockFileAtRevision(file, revision) {
136
+ if (!revision) {
137
+ return (0, child_process_1.execFileSync)('bun', [(0, path_1.join)(workspace_root_1.workspaceRoot, file)], {
138
+ encoding: 'utf-8',
139
+ maxBuffer: exports.TEN_MEGABYTES,
140
+ windowsHide: true,
141
+ }).trim();
142
+ }
143
+ const filePathInGitRepository = getFilePathInGitRepository(file);
144
+ const tempDirectory = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'nx-bun-lock-'));
145
+ const tempLockfilePath = (0, path_1.join)(tempDirectory, 'bun.lockb');
146
+ try {
147
+ const lockFileContents = (0, child_process_1.execFileSync)('git', ['show', `${revision}:${filePathInGitRepository}`], {
148
+ maxBuffer: exports.TEN_MEGABYTES,
149
+ stdio: ['pipe', 'pipe', 'ignore'],
150
+ windowsHide: true,
151
+ });
152
+ (0, fs_1.writeFileSync)(tempLockfilePath, lockFileContents);
153
+ return (0, child_process_1.execFileSync)('bun', [tempLockfilePath], {
154
+ encoding: 'utf-8',
155
+ maxBuffer: exports.TEN_MEGABYTES,
156
+ windowsHide: true,
157
+ }).trim();
158
+ }
159
+ finally {
160
+ (0, fs_1.rmSync)(tempDirectory, { force: true, recursive: true });
161
+ }
162
+ }
163
+ function getFilePathInGitRepository(file) {
164
+ const fileFullPath = `${workspace_root_1.workspaceRoot}${path_1.sep}${file}`;
165
+ const gitRepositoryPath = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
166
+ windowsHide: true,
167
+ })
168
+ .toString()
169
+ .trim();
170
+ return (0, path_1.relative)(gitRepositoryPath, fileFullPath).split(path_1.sep).join('/');
171
+ }
104
172
  function defaultFileRead(filePath) {
105
173
  return (0, fs_1.readFileSync)((0, path_1.join)(workspace_root_1.workspaceRoot, filePath), 'utf-8');
106
174
  }
@@ -3,5 +3,5 @@ import { Task, TaskGraph } from '../config/task-graph';
3
3
  import { LifeCycle, TaskResult } from './life-cycle';
4
4
  import type { ProjectGraph } from '../config/project-graph';
5
5
  import { RunningTask } from './running-tasks/running-task';
6
- export declare function runDiscreteTasks(tasks: Task[], projectGraph: ProjectGraph, taskGraphForHashing: TaskGraph, nxJson: NxJsonConfiguration, lifeCycle: LifeCycle): Promise<Array<Promise<TaskResult[]>>>;
7
- export declare function runContinuousTasks(tasks: Task[], projectGraph: ProjectGraph, taskGraphForHashing: TaskGraph, nxJson: NxJsonConfiguration, lifeCycle: LifeCycle): Promise<Record<string, Promise<RunningTask>>>;
6
+ export declare function runDiscreteTasks(tasks: Task[], projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, nxJson: NxJsonConfiguration, lifeCycle: LifeCycle): Promise<Array<Promise<TaskResult[]>>>;
7
+ export declare function runContinuousTasks(tasks: Task[], projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, nxJson: NxJsonConfiguration, lifeCycle: LifeCycle): Promise<Record<string, Promise<RunningTask>>>;
@@ -10,7 +10,7 @@ const task_orchestrator_1 = require("./task-orchestrator");
10
10
  const create_task_hasher_1 = require("../hasher/create-task-hasher");
11
11
  const client_1 = require("../daemon/client/client");
12
12
  const task_results_life_cycle_1 = require("./life-cycles/task-results-life-cycle");
13
- async function createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
13
+ async function createOrchestrator(tasks, projectGraph, fullTaskGraph, nxJson, lifeCycle) {
14
14
  (0, dotenv_1.loadRootEnvFiles)();
15
15
  const invokeRunnerTerminalLifecycle = new invoke_runner_terminal_output_life_cycle_1.InvokeRunnerTerminalOutputLifeCycle(tasks);
16
16
  const taskResultsLifecycle = new task_results_life_cycle_1.TaskResultsLifeCycle();
@@ -42,13 +42,13 @@ async function createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJs
42
42
  lifeCycle: compositedLifeCycle,
43
43
  };
44
44
  (0, run_command_1.setEnvVarsBasedOnArgs)(nxArgs, true);
45
- const orchestrator = new task_orchestrator_1.TaskOrchestrator(hasher, null, tasks, projectGraph, taskGraph, nxJson, nxArgs, false, client_1.daemonClient, undefined, taskGraphForHashing);
45
+ const orchestrator = new task_orchestrator_1.TaskOrchestrator(hasher, null, tasks, projectGraph, taskGraph, nxJson, nxArgs, false, client_1.daemonClient, undefined, fullTaskGraph);
46
46
  await orchestrator.init();
47
47
  orchestrator.processAllScheduledTasks();
48
48
  return orchestrator;
49
49
  }
50
- async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
51
- const orchestrator = await createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle);
50
+ async function runDiscreteTasks(tasks, projectGraph, fullTaskGraph, nxJson, lifeCycle) {
51
+ const orchestrator = await createOrchestrator(tasks, projectGraph, fullTaskGraph, nxJson, lifeCycle);
52
52
  let groupId = 0;
53
53
  let nextBatch = orchestrator.nextBatch();
54
54
  const batchResults = [];
@@ -77,8 +77,8 @@ async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson
77
77
  });
78
78
  return [...batchResults, ...taskResults];
79
79
  }
80
- async function runContinuousTasks(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
81
- const orchestrator = await createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle);
80
+ async function runContinuousTasks(tasks, projectGraph, fullTaskGraph, nxJson, lifeCycle) {
81
+ const orchestrator = await createOrchestrator(tasks, projectGraph, fullTaskGraph, nxJson, lifeCycle);
82
82
  return tasks.reduce((current, task, index) => {
83
83
  current[task.id] = orchestrator.startContinuousTask(task, index);
84
84
  return current;
@@ -21,7 +21,7 @@ export declare class TaskOrchestrator {
21
21
  private readonly bail;
22
22
  private readonly daemon;
23
23
  private readonly outputStyle;
24
- private readonly taskGraphForHashing;
24
+ private readonly fullTaskGraph;
25
25
  private taskDetails;
26
26
  private cache;
27
27
  private readonly tuiEnabled;
@@ -49,7 +49,7 @@ export declare class TaskOrchestrator {
49
49
  private discreteTaskExitHandled;
50
50
  private continuousTaskExitHandled;
51
51
  private cleanupPromise;
52
- constructor(hasher: TaskHasher, initiatingProject: string | undefined, initiatingTasks: Task[], projectGraph: ProjectGraph, taskGraph: TaskGraph, nxJson: NxJsonConfiguration, options: NxArgs & DefaultTasksRunnerOptions, bail: boolean, daemon: DaemonClient, outputStyle: string, taskGraphForHashing?: TaskGraph);
52
+ constructor(hasher: TaskHasher, initiatingProject: string | undefined, initiatingTasks: Task[], projectGraph: ProjectGraph, taskGraph: TaskGraph, nxJson: NxJsonConfiguration, options: NxArgs & DefaultTasksRunnerOptions, bail: boolean, daemon: DaemonClient, outputStyle: string, fullTaskGraph?: TaskGraph);
53
53
  init(): Promise<void>;
54
54
  run(): Promise<{
55
55
  [k: string]: TaskStatus;
@@ -39,7 +39,7 @@ function resolveBatchTaskStatus(result) {
39
39
  }
40
40
  class TaskOrchestrator {
41
41
  // endregion internal state
42
- constructor(hasher, initiatingProject, initiatingTasks, projectGraph, taskGraph, nxJson, options, bail, daemon, outputStyle, taskGraphForHashing = taskGraph) {
42
+ constructor(hasher, initiatingProject, initiatingTasks, projectGraph, taskGraph, nxJson, options, bail, daemon, outputStyle, fullTaskGraph = taskGraph) {
43
43
  this.hasher = hasher;
44
44
  this.initiatingProject = initiatingProject;
45
45
  this.initiatingTasks = initiatingTasks;
@@ -50,7 +50,7 @@ class TaskOrchestrator {
50
50
  this.bail = bail;
51
51
  this.daemon = daemon;
52
52
  this.outputStyle = outputStyle;
53
- this.taskGraphForHashing = taskGraphForHashing;
53
+ this.fullTaskGraph = fullTaskGraph;
54
54
  this.taskDetails = (0, hash_task_1.getTaskDetails)();
55
55
  this.cache = (0, cache_1.getCache)(this.options);
56
56
  this.tuiEnabled = (0, is_tui_enabled_1.isTuiEnabled)();
@@ -185,7 +185,7 @@ class TaskOrchestrator {
185
185
  for (const task of unhashed) {
186
186
  perTaskEnvs[task.id] = (0, task_env_1.getTaskSpecificEnv)(task, this.projectGraph);
187
187
  }
188
- await (0, hash_task_1.hashTasks)(this.hasher, this.projectGraph, this.taskGraphForHashing, perTaskEnvs, this.taskDetails, unhashed);
188
+ await (0, hash_task_1.hashTasks)(this.hasher, this.projectGraph, this.fullTaskGraph, perTaskEnvs, this.taskDetails, unhashed);
189
189
  }
190
190
  }
191
191
  // 2. Bulk-resolve cache hits before processTask — avoids N
@@ -257,7 +257,7 @@ class TaskOrchestrator {
257
257
  const task = this.taskGraph.tasks[taskId];
258
258
  const taskSpecificEnv = (0, task_env_1.getTaskSpecificEnv)(task, this.projectGraph);
259
259
  if (!task.hash) {
260
- await (0, hash_task_1.hashTask)(this.hasher, this.projectGraph, this.taskGraphForHashing, task, taskSpecificEnv, this.taskDetails);
260
+ await (0, hash_task_1.hashTask)(this.hasher, this.projectGraph, this.fullTaskGraph, task, taskSpecificEnv, this.taskDetails);
261
261
  }
262
262
  await this.options.lifeCycle.scheduleTask(task);
263
263
  return taskSpecificEnv;
@@ -474,7 +474,7 @@ class TaskOrchestrator {
474
474
  for (const task of tasks) {
475
475
  perTaskEnvs[task.id] = (0, task_env_1.getTaskSpecificEnv)(task, this.projectGraph);
476
476
  }
477
- await (0, hash_task_1.hashTasks)(this.hasher, this.projectGraph, this.taskGraphForHashing, perTaskEnvs, this.taskDetails, tasks);
477
+ await (0, hash_task_1.hashTasks)(this.hasher, this.projectGraph, this.fullTaskGraph, perTaskEnvs, this.taskDetails, tasks);
478
478
  }
479
479
  async applyFromCacheOrRunBatch(doNotSkipCache, batch, groupId) {
480
480
  const applyFromCacheOrRunBatchStart = perf_hooks_1.performance.mark('TaskOrchestrator-apply-from-cache-or-run-batch:start');
@@ -542,7 +542,7 @@ class TaskOrchestrator {
542
542
  async runBatch(batch, env, groupId) {
543
543
  const runBatchStart = perf_hooks_1.performance.mark('TaskOrchestrator-run-batch:start');
544
544
  try {
545
- const batchProcess = await this.forkedProcessTaskRunner.forkProcessForBatch(batch, this.projectGraph, this.taskGraph, env);
545
+ const batchProcess = await this.forkedProcessTaskRunner.forkProcessForBatch(batch, this.projectGraph, this.fullTaskGraph, env);
546
546
  // Stream output from batch process to the batch
547
547
  batchProcess.onOutput((output) => {
548
548
  this.options.lifeCycle.appendBatchOutput?.(batch.id, output);