nx 22.7.6 → 22.7.8

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.
@@ -12,6 +12,7 @@ const generate_files_1 = require("../../generators/utils/generate-files");
12
12
  const json_1 = require("../../generators/utils/json");
13
13
  const native_1 = require("../../native");
14
14
  const package_json_1 = require("../../utils/package-json");
15
+ const package_manager_1 = require("../../utils/package-manager");
15
16
  const ignore_1 = require("../../utils/ignore");
16
17
  const provenance_1 = require("../../utils/provenance");
17
18
  const workspace_root_1 = require("../../utils/workspace-root");
@@ -48,7 +49,7 @@ async function setupAiAgentsGenerator(tree, options, inner = false) {
48
49
  }
49
50
  try {
50
51
  await (0, provenance_1.ensurePackageHasProvenance)('nx', normalizedOptions.packageVersion);
51
- const { tempDir, cleanup } = (0, package_json_1.installPackageToTmp)('nx', normalizedOptions.packageVersion);
52
+ const { tempDir, cleanup } = (0, package_json_1.installPackageToTmp)('nx', normalizedOptions.packageVersion, (0, package_manager_1.detectPackageManager)(tree.root));
52
53
  let modulePath = (0, path_1.join)(tempDir, 'node_modules', 'nx', 'src/ai/set-up-ai-agents/set-up-ai-agents.js');
53
54
  const module = await (0, handle_import_1.handleImport)(modulePath);
54
55
  const setupAiAgentsGeneratorResult = await module.setupAiAgentsGenerator(tree, normalizedOptions, true);
@@ -44,7 +44,7 @@ async function configureAiAgentsHandler(args, inner = false) {
44
44
  let cleanup;
45
45
  try {
46
46
  await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
47
- const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest');
47
+ const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest', (0, package_manager_1.detectPackageManager)(workspace_root_1.workspaceRoot));
48
48
  cleanup = packageInstallResults.cleanup;
49
49
  let modulePath = require.resolve('nx/src/command-line/configure-ai-agents/configure-ai-agents.js', { paths: [packageInstallResults.tempDir] });
50
50
  const module = await (0, handle_import_1.handleImport)(modulePath);
@@ -42,7 +42,7 @@ async function initHandler(options, inner = false) {
42
42
  let cleanup;
43
43
  try {
44
44
  await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
45
- const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest');
45
+ const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest', (0, package_manager_2.detectPackageManager)(process.cwd()));
46
46
  cleanup = packageInstallResults.cleanup;
47
47
  let modulePath = require.resolve('nx/src/command-line/init/init-v2.js', {
48
48
  paths: [packageInstallResults.tempDir],
@@ -17,6 +17,7 @@ exports.stopMigration = stopMigration;
17
17
  const child_process_1 = require("child_process");
18
18
  const fs_1 = require("fs");
19
19
  const path_1 = require("path");
20
+ const git_revision_1 = require("../../utils/git-revision");
20
21
  const migrate_1 = require("./migrate");
21
22
  let currentMigrationProcess = null;
22
23
  let currentMigrationId = null;
@@ -47,6 +48,9 @@ function finishMigrationProcess(workspacePath, squashCommits, commitMessage) {
47
48
  const migrationsJsonPath = (0, path_1.join)(workspacePath, 'migrations.json');
48
49
  const parsedMigrationsJson = JSON.parse((0, fs_1.readFileSync)(migrationsJsonPath, 'utf-8'));
49
50
  const initialGitRef = parsedMigrationsJson['nx-console'].initialGitRef;
51
+ if (squashCommits && initialGitRef) {
52
+ (0, git_revision_1.assertValidGitSha)(initialGitRef.ref);
53
+ }
50
54
  if ((0, fs_1.existsSync)(migrationsJsonPath)) {
51
55
  (0, fs_1.rmSync)(migrationsJsonPath);
52
56
  }
@@ -55,24 +59,24 @@ function finishMigrationProcess(workspacePath, squashCommits, commitMessage) {
55
59
  encoding: 'utf-8',
56
60
  windowsHide: true,
57
61
  });
58
- (0, child_process_1.execSync)(`git commit -m "${commitMessage}" --no-verify`, {
59
- cwd: workspacePath,
60
- encoding: 'utf-8',
61
- windowsHide: true,
62
- });
62
+ commit(workspacePath, commitMessage);
63
63
  if (squashCommits && initialGitRef) {
64
- (0, child_process_1.execSync)(`git reset --soft ${initialGitRef.ref}`, {
65
- cwd: workspacePath,
66
- encoding: 'utf-8',
67
- windowsHide: true,
68
- });
69
- (0, child_process_1.execSync)(`git commit -m "${commitMessage}" --no-verify`, {
64
+ (0, child_process_1.execFileSync)('git', ['reset', '--soft', initialGitRef.ref], {
70
65
  cwd: workspacePath,
71
66
  encoding: 'utf-8',
72
67
  windowsHide: true,
73
68
  });
69
+ commit(workspacePath, commitMessage);
74
70
  }
75
71
  }
72
+ function commit(workspacePath, commitMessage) {
73
+ (0, child_process_1.execSync)('git commit --no-verify -F -', {
74
+ cwd: workspacePath,
75
+ encoding: 'utf-8',
76
+ windowsHide: true,
77
+ input: commitMessage,
78
+ });
79
+ }
76
80
  async function runSingleMigration(workspacePath, migration, configuration) {
77
81
  try {
78
82
  // Set current migration tracking
@@ -312,7 +316,8 @@ function undoMigration(workspacePath, id) {
312
316
  const existing = migrationsJsonMetadata.completedMigrations[id];
313
317
  if (existing.type !== 'successful')
314
318
  throw new Error(`undoMigration called on unsuccessful migration: ${id}`);
315
- (0, child_process_1.execSync)(`git reset --hard ${existing.ref}^`, {
319
+ (0, git_revision_1.assertValidGitSha)(existing.ref);
320
+ (0, child_process_1.execFileSync)('git', ['reset', '--hard', `${existing.ref}^`], {
316
321
  cwd: workspacePath,
317
322
  encoding: 'utf-8',
318
323
  windowsHide: true,
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getLatestNxTmpPath = getLatestNxTmpPath;
4
4
  exports.cleanupLatestNx = cleanupLatestNx;
5
5
  const devkit_internals_1 = require("../../devkit-internals");
6
+ const package_manager_1 = require("../../utils/package-manager");
6
7
  const provenance_1 = require("../../utils/provenance");
8
+ const workspace_root_1 = require("../../utils/workspace-root");
7
9
  const logger_1 = require("../logger");
8
10
  // Module-level state - persists across invocations within daemon lifecycle
9
11
  let latestNxTmpPath = null;
@@ -26,7 +28,7 @@ async function getLatestNxTmpPath() {
26
28
  try {
27
29
  logger_1.serverLogger.log('[LATEST-NX]: Pulling latest Nx...');
28
30
  await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
29
- const result = await (0, devkit_internals_1.installPackageToTmpAsync)('nx', 'latest');
31
+ const result = await (0, devkit_internals_1.installPackageToTmpAsync)('nx', 'latest', (0, package_manager_1.detectPackageManager)(workspace_root_1.workspaceRoot));
30
32
  latestNxTmpPath = result.tempDir;
31
33
  cleanupFn = result.cleanup;
32
34
  logger_1.serverLogger.log('[LATEST-NX]: Successfully pulled latest Nx to', latestNxTmpPath);
Binary file
@@ -10,6 +10,7 @@ const child_process_1 = require("child_process");
10
10
  const fs_1 = require("fs");
11
11
  const path_1 = require("path");
12
12
  const fileutils_1 = require("../utils/fileutils");
13
+ const git_revision_1 = require("../utils/git-revision");
13
14
  const ignore_1 = require("../utils/ignore");
14
15
  const json_diff_1 = require("../utils/json-diff");
15
16
  const workspace_root_1 = require("../utils/workspace-root");
@@ -77,6 +78,9 @@ function calculateFileChanges(files, nxArgs, readFileAtRevision = defaultReadFil
77
78
  }
78
79
  exports.TEN_MEGABYTES = 1024 * 10000;
79
80
  function defaultReadFileAtRevision(file, revision) {
81
+ if (revision) {
82
+ (0, git_revision_1.assertValidGitRevision)(revision);
83
+ }
80
84
  try {
81
85
  const fileFullPath = `${workspace_root_1.workspaceRoot}${path_1.sep}${file}`;
82
86
  const gitRepositoryPath = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
@@ -89,7 +93,7 @@ function defaultReadFileAtRevision(file, revision) {
89
93
  .join('/');
90
94
  return !revision
91
95
  ? (0, fs_1.readFileSync)(file, 'utf-8')
92
- : (0, child_process_1.execSync)(`git show ${revision}:${filePathInGitRepository}`, {
96
+ : (0, child_process_1.execFileSync)('git', ['show', `${revision}:${filePathInGitRepository}`], {
93
97
  maxBuffer: exports.TEN_MEGABYTES,
94
98
  stdio: ['pipe', 'pipe', 'ignore'],
95
99
  windowsHide: true,
@@ -251,6 +251,12 @@ class DbCache {
251
251
  logger_1.logger.warn('The HTTP remote cache is not yet supported in the wasm build of Nx.');
252
252
  return null;
253
253
  }
254
+ // The cache request runs in Rust (reqwest), which honors
255
+ // NODE_TLS_REJECT_UNAUTHORIZED but bypasses Node's TLS stack, so Node's own
256
+ // insecure-TLS warning never fires for it. Re-emit Node's warning here.
257
+ if (process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0') {
258
+ process.emitWarning("Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.");
259
+ }
254
260
  return new native_1.HttpRemoteCache();
255
261
  }
256
262
  return null;
@@ -11,6 +11,7 @@ const yargs_parser_1 = tslib_1.__importDefault(require("yargs-parser"));
11
11
  const file_utils_1 = require("../project-graph/file-utils");
12
12
  const output_1 = require("./output");
13
13
  const child_process_1 = require("child_process");
14
+ const git_revision_1 = require("./git-revision");
14
15
  const workspace_root_1 = require("./workspace-root");
15
16
  const shared_options_1 = require("../command-line/yargs-utils/shared-options");
16
17
  function createOverrides(__overrides_unparsed__ = []) {
@@ -192,30 +193,27 @@ function parseFiles(options) {
192
193
  }
193
194
  }
194
195
  function getUncommittedFiles() {
195
- return parseGitOutput(`git diff --name-only --no-renames --relative HEAD .`);
196
+ return parseGitOutput([
197
+ 'diff',
198
+ '--name-only',
199
+ '--no-renames',
200
+ '--relative',
201
+ 'HEAD',
202
+ '.',
203
+ ]);
196
204
  }
197
205
  function getUntrackedFiles() {
198
- return parseGitOutput(`git ls-files --others --exclude-standard`);
206
+ return parseGitOutput(['ls-files', '--others', '--exclude-standard']);
199
207
  }
200
208
  function getMergeBase(base, head = 'HEAD') {
209
+ (0, git_revision_1.assertValidGitRevision)(base);
210
+ (0, git_revision_1.assertValidGitRevision)(head);
201
211
  try {
202
- return (0, child_process_1.execSync)(`git merge-base "${base}" "${head}"`, {
203
- maxBuffer: file_utils_1.TEN_MEGABYTES,
204
- cwd: workspace_root_1.workspaceRoot,
205
- stdio: 'pipe',
206
- windowsHide: true,
207
- })
208
- .toString()
209
- .trim();
212
+ return runGit(['merge-base', base, head]).toString().trim();
210
213
  }
211
214
  catch {
212
215
  try {
213
- return (0, child_process_1.execSync)(`git merge-base --fork-point "${base}" "${head}"`, {
214
- maxBuffer: file_utils_1.TEN_MEGABYTES,
215
- cwd: workspace_root_1.workspaceRoot,
216
- stdio: 'pipe',
217
- windowsHide: true,
218
- })
216
+ return runGit(['merge-base', '--fork-point', base, head])
219
217
  .toString()
220
218
  .trim();
221
219
  }
@@ -225,15 +223,27 @@ function getMergeBase(base, head = 'HEAD') {
225
223
  }
226
224
  }
227
225
  function getFilesUsingBaseAndHead(base, head) {
228
- return parseGitOutput(`git diff --name-only --no-renames --relative "${base}" "${head}"`);
226
+ (0, git_revision_1.assertValidGitRevision)(base);
227
+ (0, git_revision_1.assertValidGitRevision)(head);
228
+ return parseGitOutput([
229
+ 'diff',
230
+ '--name-only',
231
+ '--no-renames',
232
+ '--relative',
233
+ base,
234
+ head,
235
+ ]);
229
236
  }
230
- function parseGitOutput(command) {
231
- return (0, child_process_1.execSync)(command, {
237
+ function runGit(args) {
238
+ return (0, child_process_1.execFileSync)('git', args, {
232
239
  maxBuffer: file_utils_1.TEN_MEGABYTES,
233
240
  cwd: workspace_root_1.workspaceRoot,
234
241
  stdio: 'pipe',
235
242
  windowsHide: true,
236
- })
243
+ });
244
+ }
245
+ function parseGitOutput(args) {
246
+ return runGit(args)
237
247
  .toString('utf-8')
238
248
  .split('\n')
239
249
  .map((a) => a.trim())
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Git parses any argument beginning with `-` as an option rather than a
3
+ * revision, so a flag-shaped revision could smuggle options such as
4
+ * `--upload-pack` into the git commands Nx runs.
5
+ */
6
+ export declare function assertValidGitRevision(revision: string): void;
7
+ /**
8
+ * For refs Nx itself recorded from `git rev-parse` and later read back off
9
+ * disk, where anything other than a commit sha means the value was tampered
10
+ * with rather than that the user picked an unusual revision.
11
+ */
12
+ export declare function assertValidGitSha(sha: string): void;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertValidGitRevision = assertValidGitRevision;
4
+ exports.assertValidGitSha = assertValidGitSha;
5
+ /**
6
+ * Git parses any argument beginning with `-` as an option rather than a
7
+ * revision, so a flag-shaped revision could smuggle options such as
8
+ * `--upload-pack` into the git commands Nx runs.
9
+ */
10
+ function assertValidGitRevision(revision) {
11
+ if (revision.startsWith('-')) {
12
+ throw new Error(`Invalid git revision: "${revision}". Git revisions cannot start with "-".`);
13
+ }
14
+ }
15
+ const COMMIT_SHA = /^[0-9a-f]{7,40}$/i;
16
+ /**
17
+ * For refs Nx itself recorded from `git rev-parse` and later read back off
18
+ * disk, where anything other than a commit sha means the value was tampered
19
+ * with rather than that the user picked an unusual revision.
20
+ */
21
+ function assertValidGitSha(sha) {
22
+ if (!COMMIT_SHA.test(sha)) {
23
+ throw new Error(`Invalid git commit sha: "${sha}". Expected a hexadecimal commit sha.`);
24
+ }
25
+ }
@@ -28,9 +28,7 @@ export declare class GitRepository {
28
28
  hasFilterRepoInstalled(): Promise<boolean>;
29
29
  filterRepo(source: string, destination: string): Promise<void>;
30
30
  filterBranch(source: string, destination: string, branchName: string): Promise<void>;
31
- private execAsync;
32
- private quotePath;
33
- private quoteArg;
31
+ private execGit;
34
32
  }
35
33
  export interface VcsRemoteInfo {
36
34
  domain: string;
@@ -9,9 +9,9 @@ exports.getLatestCommitSha = getLatestCommitSha;
9
9
  const child_process_1 = require("child_process");
10
10
  const path_1 = require("path");
11
11
  const logger_1 = require("./logger");
12
- function execAsync(command, execOptions) {
12
+ function execFileAsync(file, args, execOptions) {
13
13
  return new Promise((res, rej) => {
14
- (0, child_process_1.exec)(command, { ...execOptions, windowsHide: true }, (err, stdout, stderr) => {
14
+ (0, child_process_1.execFile)(file, args, { ...execOptions, windowsHide: true }, (err, stdout) => {
15
15
  if (err) {
16
16
  return rej(err);
17
17
  }
@@ -22,7 +22,14 @@ function execAsync(command, execOptions) {
22
22
  async function cloneFromUpstream(url, destination, { originName, depth } = {
23
23
  originName: 'origin',
24
24
  }) {
25
- await execAsync(`git clone ${url} ${destination} ${depth ? `--depth ${depth}` : ''} --origin ${originName}`, {
25
+ await execFileAsync('git', [
26
+ 'clone',
27
+ url,
28
+ destination,
29
+ ...(depth ? ['--depth', `${depth}`] : []),
30
+ '--origin',
31
+ originName,
32
+ ], {
26
33
  cwd: (0, path_1.dirname)(destination),
27
34
  maxBuffer: 10 * 1024 * 1024,
28
35
  });
@@ -34,7 +41,7 @@ class GitRepository {
34
41
  this.root = this.getGitRootPath(this.directory);
35
42
  }
36
43
  getGitRootPath(cwd) {
37
- return (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
44
+ return (0, child_process_1.execFileSync)('git', ['rev-parse', '--show-toplevel'], {
38
45
  cwd,
39
46
  windowsHide: true,
40
47
  })
@@ -42,17 +49,22 @@ class GitRepository {
42
49
  .trim();
43
50
  }
44
51
  async hasUncommittedChanges() {
45
- const data = await this.execAsync(`git status --porcelain`);
52
+ const data = await this.execGit(['status', '--porcelain']);
46
53
  return data.trim() !== '';
47
54
  }
48
55
  async addFetchRemote(remoteName, branch) {
49
- return await this.execAsync(`git config --add remote.${remoteName}.fetch "+refs/heads/${branch}:refs/remotes/${remoteName}/${branch}"`);
56
+ return await this.execGit([
57
+ 'config',
58
+ '--add',
59
+ `remote.${remoteName}.fetch`,
60
+ `+refs/heads/${branch}:refs/remotes/${remoteName}/${branch}`,
61
+ ]);
50
62
  }
51
63
  async showStat() {
52
- return await this.execAsync(`git show --stat`);
64
+ return await this.execGit(['show', '--stat']);
53
65
  }
54
66
  async listBranches() {
55
- return (await this.execAsync(`git ls-remote --heads --quiet`))
67
+ return (await this.execGit(['ls-remote', '--heads', '--quiet']))
56
68
  .trim()
57
69
  .split('\n')
58
70
  .map((s) => s
@@ -63,45 +75,58 @@ class GitRepository {
63
75
  async getGitFiles(path) {
64
76
  // Use -z to return file names exactly as they are stored in git, separated by NULL (\x00) character.
65
77
  // This avoids problems with special characters in file names.
66
- return (await this.execAsync(`git ls-files -z ${path}`))
78
+ return (await this.execGit(['ls-files', '-z', '--', path]))
67
79
  .trim()
68
80
  .split('\x00')
69
81
  .map((s) => s.trim())
70
82
  .filter(Boolean);
71
83
  }
72
84
  async reset(ref) {
73
- return await this.execAsync(`git reset ${ref} --hard`);
85
+ return await this.execGit(['reset', '--hard', ref]);
74
86
  }
75
87
  async mergeUnrelatedHistories(ref, message) {
76
- return await this.execAsync(`git merge ${ref} -X ours --allow-unrelated-histories -m "${message}"`);
88
+ return await this.execGit([
89
+ 'merge',
90
+ ref,
91
+ '-X',
92
+ 'ours',
93
+ '--allow-unrelated-histories',
94
+ '-m',
95
+ message,
96
+ ]);
77
97
  }
78
98
  async fetch(remote, ref) {
79
- return await this.execAsync(`git fetch ${remote}${ref ? ` ${ref}` : ''}`);
99
+ return await this.execGit(['fetch', remote, ...(ref ? [ref] : [])]);
80
100
  }
81
101
  async checkout(branch, opts) {
82
- return await this.execAsync(`git checkout ${opts.new ? '-b ' : ' '}${branch}${opts.base ? ' ' + opts.base : ''}`);
102
+ return await this.execGit([
103
+ 'checkout',
104
+ ...(opts.new ? ['-b'] : []),
105
+ branch,
106
+ ...(opts.base ? [opts.base] : []),
107
+ ]);
83
108
  }
84
109
  async move(path, destination) {
85
- return await this.execAsync(`git mv ${this.quotePath(path)} ${this.quotePath(destination)}`);
110
+ return await this.execGit(['mv', '--', path, destination]);
86
111
  }
87
112
  async push(ref, remoteName) {
88
- return await this.execAsync(`git push -u -f ${remoteName} ${ref}`);
113
+ return await this.execGit(['push', '-u', '-f', remoteName, ref]);
89
114
  }
90
115
  async commit(message) {
91
- return await this.execAsync(`git commit -am "${message}"`);
116
+ return await this.execGit(['commit', '-am', message]);
92
117
  }
93
118
  async amendCommit() {
94
- return await this.execAsync(`git commit --amend -a --no-edit`);
119
+ return await this.execGit(['commit', '--amend', '-a', '--no-edit']);
95
120
  }
96
121
  async deleteGitRemote(name) {
97
- return await this.execAsync(`git remote rm ${name}`);
122
+ return await this.execGit(['remote', 'rm', name]);
98
123
  }
99
124
  async addGitRemote(name, url) {
100
- return await this.execAsync(`git remote add ${name} ${url}`);
125
+ return await this.execGit(['remote', 'add', name, url]);
101
126
  }
102
127
  async hasFilterRepoInstalled() {
103
128
  try {
104
- await this.execAsync(`git filter-repo --help`);
129
+ await this.execGit(['filter-repo', '--help']);
105
130
  return true;
106
131
  }
107
132
  catch {
@@ -114,35 +139,57 @@ class GitRepository {
114
139
  // NOTE: filter-repo requires POSIX path to work
115
140
  const sourcePosixPath = source.split(path_1.sep).join(path_1.posix.sep);
116
141
  const destinationPosixPath = destination.split(path_1.sep).join(path_1.posix.sep);
117
- await this.execAsync(`git filter-repo -f ${source !== '' ? `--path ${this.quotePath(sourcePosixPath)}` : ''} ${source !== destination
118
- ? `--path-rename ${this.quotePath(sourcePosixPath, true)}:${this.quotePath(destinationPosixPath, true)}`
119
- : ''}`);
142
+ const sourcePath = ensureTrailingSlash(sourcePosixPath);
143
+ const destinationPath = ensureTrailingSlash(destinationPosixPath);
144
+ await this.execGit([
145
+ 'filter-repo',
146
+ '-f',
147
+ ...(source !== '' ? ['--path', sourcePosixPath] : []),
148
+ ...(source !== destination
149
+ ? ['--path-rename', `${sourcePath}:${destinationPath}`]
150
+ : []),
151
+ ]);
120
152
  }
121
153
  async filterBranch(source, destination, branchName) {
122
154
  // We need non-ASCII file names to not be quoted, or else filter-branch will exclude them.
123
- await this.execAsync(`git config core.quotepath false`);
155
+ await this.execGit(['config', 'core.quotepath', 'false']);
124
156
  // NOTE: filter-repo requires POSIX path to work
125
157
  const sourcePosixPath = source.split(path_1.sep).join(path_1.posix.sep);
126
158
  const destinationPosixPath = destination.split(path_1.sep).join(path_1.posix.sep);
127
159
  // First, if the source is not a root project, then only include commits relevant to the subdirectory.
128
160
  if (source !== '') {
129
- const indexFilterCommand = this.quoteArg(`node ${(0, path_1.join)(__dirname, 'git-utils.index-filter.js')}`);
130
- await this.execAsync(`git filter-branch -f --index-filter ${indexFilterCommand} --prune-empty -- ${branchName}`, {
161
+ const indexFilterCommand = `node ${quoteForShell((0, path_1.join)(__dirname, 'git-utils.index-filter.js'))}`;
162
+ await this.execGit([
163
+ 'filter-branch',
164
+ '-f',
165
+ '--index-filter',
166
+ indexFilterCommand,
167
+ '--prune-empty',
168
+ '--',
169
+ branchName,
170
+ ], {
131
171
  NX_IMPORT_SOURCE: sourcePosixPath,
132
172
  NX_IMPORT_DESTINATION: destinationPosixPath,
133
173
  });
134
174
  }
135
175
  // Then, move files to their new location if necessary.
136
176
  if (source === '' || source !== destination) {
137
- const treeFilterCommand = this.quoteArg(`node ${(0, path_1.join)(__dirname, 'git-utils.tree-filter.js')}`);
138
- await this.execAsync(`git filter-branch -f --tree-filter ${treeFilterCommand} -- ${branchName}`, {
177
+ const treeFilterCommand = `node ${quoteForShell((0, path_1.join)(__dirname, 'git-utils.tree-filter.js'))}`;
178
+ await this.execGit([
179
+ 'filter-branch',
180
+ '-f',
181
+ '--tree-filter',
182
+ treeFilterCommand,
183
+ '--',
184
+ branchName,
185
+ ], {
139
186
  NX_IMPORT_SOURCE: sourcePosixPath,
140
187
  NX_IMPORT_DESTINATION: destinationPosixPath,
141
188
  });
142
189
  }
143
190
  }
144
- execAsync(command, env) {
145
- return execAsync(command, {
191
+ execGit(args, env) {
192
+ return execFileAsync('git', args, {
146
193
  cwd: this.root,
147
194
  maxBuffer: 10 * 1024 * 1024,
148
195
  env: {
@@ -151,25 +198,14 @@ class GitRepository {
151
198
  },
152
199
  });
153
200
  }
154
- quotePath(path, ensureTrailingSlash) {
155
- return this.quoteArg(ensureTrailingSlash && path !== '' && !path.endsWith('/')
156
- ? `${path}/`
157
- : path);
158
- }
159
- quoteArg(arg) {
160
- return process.platform === 'win32'
161
- ? // Windows/CMD only understands double-quotes, single-quotes are treated as part of the file name
162
- // Bash and other shells will substitute `$` in file names with a variable value.
163
- `"${arg
164
- // Need to keep two slashes for Windows or else the path will be invalid.
165
- // e.g. 'C:\Users\bob\projects\repo' is invalid, but 'C:\\Users\\bob\\projects\\repo' is valid
166
- .replaceAll('\\', '\\\\')}"`
167
- : // e.g. `git mv "$$file.txt" "libs/a/$$file.txt"` will not work since `$$` is swapped with the PID of the last process.
168
- // Using single-quotes prevents this substitution.
169
- `'${arg}'`;
170
- }
171
201
  }
172
202
  exports.GitRepository = GitRepository;
203
+ function ensureTrailingSlash(path) {
204
+ return path !== '' && !path.endsWith('/') ? `${path}/` : path;
205
+ }
206
+ function quoteForShell(arg) {
207
+ return `'${arg.replaceAll("'", "'\"'\"'")}'`;
208
+ }
173
209
  function parseVcsRemoteUrl(url) {
174
210
  // Remove whitespace and handle common URL formats
175
211
  const cleanUrl = url.trim();
@@ -1,7 +1,7 @@
1
1
  import { NxJsonConfiguration } from '../config/nx-json';
2
2
  import { ProjectConfiguration, ProjectMetadata, TargetConfiguration } from '../config/workspace-json-project-json';
3
3
  import type { Tree } from '../generators/tree';
4
- import { PackageManagerCommands } from './package-manager';
4
+ import { PackageManager, PackageManagerCommands } from './package-manager';
5
5
  export interface NxProjectPackageJsonConfiguration extends Partial<ProjectConfiguration> {
6
6
  includedScripts?: string[];
7
7
  }
@@ -124,11 +124,11 @@ export declare function readModulePackageJson(moduleSpecifier: string, requirePa
124
124
  packageJson: PackageJson;
125
125
  path: string;
126
126
  };
127
- export declare function installPackageToTmp(pkg: string, requiredVersion: string): {
127
+ export declare function installPackageToTmp(pkg: string, requiredVersion: string, packageManager: PackageManager): {
128
128
  tempDir: string;
129
129
  cleanup: () => void;
130
130
  };
131
- export declare function installPackageToTmpAsync(pkg: string, requiredVersion: string): Promise<{
131
+ export declare function installPackageToTmpAsync(pkg: string, requiredVersion: string, packageManager: PackageManager): Promise<{
132
132
  tempDir: string;
133
133
  cleanup: () => void;
134
134
  }>;
@@ -204,22 +204,40 @@ function readModulePackageJson(moduleSpecifier, requirePaths = (0, installation_
204
204
  * Prepares all necessary information for installing a package to a temporary directory.
205
205
  * This is used by both sync and async installation functions.
206
206
  */
207
- function preparePackageInstallation(pkg, requiredVersion) {
207
+ function preparePackageInstallation(pkg, requiredVersion, packageManager) {
208
208
  const { dir: tempDir, cleanup } = (0, package_manager_1.createTempNpmDirectory)?.() ?? {
209
209
  dir: (0, tmp_1.dirSync)().name,
210
210
  cleanup: () => { },
211
211
  };
212
212
  console.log(`Fetching ${pkg}...`);
213
- const packageManager = (0, package_manager_1.detectPackageManager)();
214
213
  const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';
215
214
  generatePackageManagerFiles(tempDir, packageManager);
216
- const preInstallCommand = (0, package_manager_1.getPackageManagerCommand)(packageManager).preInstall;
215
+ // For pnpm, `addDev` is `pnpm add -Dw` when the workspace has a
216
+ // pnpm-workspace.yaml. `createTempNpmDirectory` copies a sanitized copy of
217
+ // it into the temp dir, so the `-w` here resolves to the temp dir.
217
218
  const pmCommands = (0, package_manager_1.getPackageManagerCommand)(packageManager);
218
- let addCommand = pmCommands.addDev;
219
- if (packageManager === 'pnpm') {
220
- addCommand = 'pnpm add -D'; // we need to ensure that we are not using workspace command
221
- }
222
- const installCommand = `${addCommand} ${pkg}@${requiredVersion} ${pmCommands.ignoreScriptsFlag ?? ''}`;
219
+ const preInstallCommand = pmCommands.preInstall;
220
+ // Keep peer dependencies out of the temp install. `ensurePackage` puts the
221
+ // workspace's `node_modules` on `NODE_PATH`, so a loaded package resolves its
222
+ // peers from the workspace instead of pulling its own (possibly incompatible)
223
+ // copies into the temp dir.
224
+ //
225
+ // npm needs `--legacy-peer-deps` rather than `--omit=peer`: npm marks a package
226
+ // as a peer if anything in the tree peer-depends on it, so `--omit=peer` also
227
+ // prunes packages that are real dependencies. Bun's `--omit=peer` does not.
228
+ const skipPeerDependenciesFlags = {
229
+ npm: '--legacy-peer-deps',
230
+ bun: '--omit=peer',
231
+ pnpm: '--config.auto-install-peers=false',
232
+ };
233
+ const installCommand = [
234
+ pmCommands.addDev,
235
+ `${pkg}@${requiredVersion}`,
236
+ skipPeerDependenciesFlags[packageManager],
237
+ pmCommands.ignoreScriptsFlag,
238
+ ]
239
+ .filter(Boolean)
240
+ .join(' ');
223
241
  const execOptions = {
224
242
  cwd: tempDir,
225
243
  stdio: isVerbose ? 'inherit' : 'ignore',
@@ -239,8 +257,8 @@ function preparePackageInstallation(pkg, requiredVersion) {
239
257
  execOptions,
240
258
  };
241
259
  }
242
- function installPackageToTmp(pkg, requiredVersion) {
243
- const { tempDir, cleanup, preInstallCommand, installCommand, execOptions } = preparePackageInstallation(pkg, requiredVersion);
260
+ function installPackageToTmp(pkg, requiredVersion, packageManager) {
261
+ const { tempDir, cleanup, preInstallCommand, installCommand, execOptions } = preparePackageInstallation(pkg, requiredVersion, packageManager);
244
262
  if (preInstallCommand) {
245
263
  // ensure package.json and repo in tmp folder is set to a proper package manager state
246
264
  (0, child_process_1.execSync)(preInstallCommand, execOptions);
@@ -251,8 +269,8 @@ function installPackageToTmp(pkg, requiredVersion) {
251
269
  cleanup,
252
270
  };
253
271
  }
254
- async function installPackageToTmpAsync(pkg, requiredVersion) {
255
- const { tempDir, cleanup, preInstallCommand, installCommand, execOptions } = preparePackageInstallation(pkg, requiredVersion);
272
+ async function installPackageToTmpAsync(pkg, requiredVersion, packageManager) {
273
+ const { tempDir, cleanup, preInstallCommand, installCommand, execOptions } = preparePackageInstallation(pkg, requiredVersion, packageManager);
256
274
  try {
257
275
  if (preInstallCommand) {
258
276
  // ensure package.json and repo in tmp folder is set to a proper package manager state
@@ -75,6 +75,19 @@ export declare function modifyYarnRcYmlToFitNewDirectory(contents: string): stri
75
75
  * @returns Updated string contents of the yarnrc.yml file
76
76
  */
77
77
  export declare function modifyYarnRcToFitNewDirectory(contents: string): string;
78
+ /**
79
+ * We copy pnpm-workspace.yaml to the temporary directory so the workspace's
80
+ * registry, auth and release-age settings still apply, and so `pnpm add -w`
81
+ * recognizes the directory as a workspace root. `packages` (member globs) and
82
+ * `patchedDependencies` (relative patch paths) only resolve in the real
83
+ * workspace, so they are dropped.
84
+ *
85
+ * Exported for testing - not meant to be used outside of this file.
86
+ *
87
+ * @param contents The string contents of the pnpm-workspace.yaml file
88
+ * @returns Updated string contents of the pnpm-workspace.yaml file
89
+ */
90
+ export declare function modifyPnpmWorkspaceYamlToFitNewDirectory(contents: string): string;
78
91
  export declare function copyPackageManagerConfigurationFiles(root: string, destination: string): void;
79
92
  /**
80
93
  * Creates a temporary directory where you can run package manager commands safely.
@@ -8,6 +8,7 @@ exports.parseVersionFromPackageManagerField = parseVersionFromPackageManagerFiel
8
8
  exports.findFileInPackageJsonDirectory = findFileInPackageJsonDirectory;
9
9
  exports.modifyYarnRcYmlToFitNewDirectory = modifyYarnRcYmlToFitNewDirectory;
10
10
  exports.modifyYarnRcToFitNewDirectory = modifyYarnRcToFitNewDirectory;
11
+ exports.modifyPnpmWorkspaceYamlToFitNewDirectory = modifyPnpmWorkspaceYamlToFitNewDirectory;
11
12
  exports.copyPackageManagerConfigurationFiles = copyPackageManagerConfigurationFiles;
12
13
  exports.createTempNpmDirectory = createTempNpmDirectory;
13
14
  exports.resolvePackageVersionUsingRegistry = resolvePackageVersionUsingRegistry;
@@ -322,12 +323,33 @@ function modifyYarnRcToFitNewDirectory(contents) {
322
323
  }
323
324
  return lines.join('\n');
324
325
  }
326
+ /**
327
+ * We copy pnpm-workspace.yaml to the temporary directory so the workspace's
328
+ * registry, auth and release-age settings still apply, and so `pnpm add -w`
329
+ * recognizes the directory as a workspace root. `packages` (member globs) and
330
+ * `patchedDependencies` (relative patch paths) only resolve in the real
331
+ * workspace, so they are dropped.
332
+ *
333
+ * Exported for testing - not meant to be used outside of this file.
334
+ *
335
+ * @param contents The string contents of the pnpm-workspace.yaml file
336
+ * @returns Updated string contents of the pnpm-workspace.yaml file
337
+ */
338
+ function modifyPnpmWorkspaceYamlToFitNewDirectory(contents) {
339
+ const doc = (0, yaml_1.parseDocument)(contents);
340
+ if (doc.contents) {
341
+ doc.delete('packages');
342
+ doc.delete('patchedDependencies');
343
+ }
344
+ return doc.toString();
345
+ }
325
346
  function copyPackageManagerConfigurationFiles(root, destination) {
326
347
  for (const packageManagerConfigFile of [
327
348
  '.npmrc',
328
349
  '.yarnrc',
329
350
  '.yarnrc.yml',
330
351
  'bunfig.toml',
352
+ 'pnpm-workspace.yaml',
331
353
  ]) {
332
354
  // f is an absolute path, including the {workspaceRoot}.
333
355
  const f = findFileInPackageJsonDirectory(packageManagerConfigFile, root);
@@ -355,6 +377,11 @@ function copyPackageManagerConfigurationFiles(root, destination) {
355
377
  (0, fs_1.copyFileSync)(f, destinationPath);
356
378
  break;
357
379
  }
380
+ case 'pnpm-workspace.yaml': {
381
+ const updated = modifyPnpmWorkspaceYamlToFitNewDirectory((0, fileutils_1.readFileIfExisting)(f));
382
+ (0, fs_1.writeFileSync)(destinationPath, updated);
383
+ break;
384
+ }
358
385
  }
359
386
  }
360
387
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "22.7.6",
3
+ "version": "22.7.8",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
@@ -55,16 +55,17 @@
55
55
  "@tybys/wasm-util": "0.9.0",
56
56
  "@yarnpkg/lockfile": "1.1.0",
57
57
  "@zkochan/js-yaml": "0.0.7",
58
+ "agent-base": "6.0.2",
58
59
  "ansi-colors": "4.1.3",
59
60
  "ansi-regex": "5.0.1",
60
61
  "ansi-styles": "4.3.0",
61
62
  "argparse": "2.0.1",
62
63
  "asynckit": "0.4.0",
63
- "axios": "1.16.0",
64
+ "axios": "1.18.1",
64
65
  "balanced-match": "4.0.3",
65
66
  "base64-js": "1.5.1",
66
67
  "bl": "4.1.0",
67
- "brace-expansion": "5.0.6",
68
+ "brace-expansion": "5.0.8",
68
69
  "buffer": "5.7.1",
69
70
  "call-bind-apply-helpers": "1.0.2",
70
71
  "chalk": "4.1.2",
@@ -75,6 +76,7 @@
75
76
  "color-convert": "2.0.1",
76
77
  "color-name": "1.1.4",
77
78
  "combined-stream": "1.0.8",
79
+ "debug": "4.4.3",
78
80
  "defaults": "1.0.4",
79
81
  "define-lazy-prop": "2.0.0",
80
82
  "delayed-stream": "1.0.0",
@@ -105,6 +107,7 @@
105
107
  "has-symbols": "1.1.0",
106
108
  "has-tostringtag": "1.0.2",
107
109
  "hasown": "2.0.4",
110
+ "https-proxy-agent": "5.0.1",
108
111
  "ieee754": "1.2.1",
109
112
  "ignore": "7.0.5",
110
113
  "inherits": "2.0.4",
@@ -123,6 +126,7 @@
123
126
  "mimic-fn": "2.1.0",
124
127
  "minimatch": "10.2.5",
125
128
  "minimist": "1.2.8",
129
+ "ms": "2.1.3",
126
130
  "npm-run-path": "4.0.1",
127
131
  "once": "1.4.0",
128
132
  "onetime": "5.1.2",
@@ -171,16 +175,16 @@
171
175
  }
172
176
  },
173
177
  "optionalDependencies": {
174
- "@nx/nx-darwin-arm64": "22.7.6",
175
- "@nx/nx-darwin-x64": "22.7.6",
176
- "@nx/nx-freebsd-x64": "22.7.6",
177
- "@nx/nx-linux-arm-gnueabihf": "22.7.6",
178
- "@nx/nx-linux-arm64-gnu": "22.7.6",
179
- "@nx/nx-linux-arm64-musl": "22.7.6",
180
- "@nx/nx-linux-x64-gnu": "22.7.6",
181
- "@nx/nx-linux-x64-musl": "22.7.6",
182
- "@nx/nx-win32-arm64-msvc": "22.7.6",
183
- "@nx/nx-win32-x64-msvc": "22.7.6"
178
+ "@nx/nx-darwin-arm64": "22.7.8",
179
+ "@nx/nx-darwin-x64": "22.7.8",
180
+ "@nx/nx-freebsd-x64": "22.7.8",
181
+ "@nx/nx-linux-arm-gnueabihf": "22.7.8",
182
+ "@nx/nx-linux-arm64-gnu": "22.7.8",
183
+ "@nx/nx-linux-arm64-musl": "22.7.8",
184
+ "@nx/nx-linux-x64-gnu": "22.7.8",
185
+ "@nx/nx-linux-x64-musl": "22.7.8",
186
+ "@nx/nx-win32-arm64-msvc": "22.7.8",
187
+ "@nx/nx-win32-x64-msvc": "22.7.8"
184
188
  },
185
189
  "nx-migrations": {
186
190
  "migrations": "./migrations.json",