nx 19.6.4 → 19.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. package/package.json +12 -12
  2. package/release/changelog-renderer/index.d.ts +1 -1
  3. package/release/changelog-renderer/index.js +46 -11
  4. package/schemas/nx-schema.json +5 -0
  5. package/src/command-line/import/command-object.js +4 -0
  6. package/src/command-line/import/import.d.ts +4 -0
  7. package/src/command-line/import/import.js +147 -12
  8. package/src/command-line/import/utils/prepare-source-repo.d.ts +1 -1
  9. package/src/command-line/import/utils/prepare-source-repo.js +31 -85
  10. package/src/command-line/release/changelog.js +53 -12
  11. package/src/command-line/release/command-object.d.ts +1 -0
  12. package/src/command-line/release/command-object.js +6 -1
  13. package/src/command-line/release/config/version-plans.d.ts +14 -1
  14. package/src/command-line/release/config/version-plans.js +33 -1
  15. package/src/command-line/release/plan-check.js +8 -61
  16. package/src/command-line/release/plan.js +131 -37
  17. package/src/command-line/release/release.js +1 -1
  18. package/src/command-line/release/utils/get-touched-projects-for-group.d.ts +7 -0
  19. package/src/command-line/release/utils/get-touched-projects-for-group.js +78 -0
  20. package/src/command-line/release/utils/git.d.ts +1 -1
  21. package/src/command-line/release/utils/git.js +46 -19
  22. package/src/command-line/release/version.js +1 -1
  23. package/src/command-line/yargs-utils/shared-options.d.ts +2 -1
  24. package/src/command-line/yargs-utils/shared-options.js +5 -0
  25. package/src/core/graph/main.js +1 -1
  26. package/src/daemon/server/sync-generators.d.ts +4 -0
  27. package/src/daemon/server/sync-generators.js +172 -52
  28. package/src/migrations/update-15-0-0/prefix-outputs.js +3 -18
  29. package/src/native/index.d.ts +4 -0
  30. package/src/native/native-bindings.js +2 -0
  31. package/src/native/nx.wasi-browser.js +42 -36
  32. package/src/native/nx.wasi.cjs +40 -36
  33. package/src/native/nx.wasm32-wasi.wasm +0 -0
  34. package/src/project-graph/plugins/isolation/plugin-pool.js +1 -1
  35. package/src/tasks-runner/run-command.js +4 -1
  36. package/src/tasks-runner/utils.d.ts +1 -8
  37. package/src/tasks-runner/utils.js +9 -12
  38. package/src/utils/command-line-utils.d.ts +1 -0
  39. package/src/utils/git-utils.d.ts +7 -10
  40. package/src/utils/git-utils.js +61 -44
  41. package/src/utils/sync-generators.d.ts +2 -2
  42. package/src/utils/squash.d.ts +0 -1
  43. package/src/utils/squash.js +0 -12
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGetTouchedProjectsForGroup = createGetTouchedProjectsForGroup;
4
+ const workspace_projects_1 = require("../../../project-graph/affected/locators/workspace-projects");
5
+ const file_utils_1 = require("../../../project-graph/file-utils");
6
+ const ignore_1 = require("../../../utils/ignore");
7
+ const output_1 = require("../../../utils/output");
8
+ const config_1 = require("../config/config");
9
+ /**
10
+ * Create a function that returns the touched projects for a given release group. Only relevant when version plans are enabled.
11
+ */
12
+ function createGetTouchedProjectsForGroup(nxArgs, projectGraph, changedFiles, fileData) {
13
+ /**
14
+ * Create a minimal subset of touched projects based on the configured ignore patterns, we only need
15
+ * to recompute when the ignorePatternsForPlanCheck differs between release groups.
16
+ */
17
+ const serializedIgnorePatternsToTouchedProjects = new Map();
18
+ return async function getTouchedProjectsForGroup(releaseGroup,
19
+ // We don't access releaseGroups.projects directly, because we need to take the --projects filter into account
20
+ releaseGroupFilteredProjectNames, hasProjectsFilter) {
21
+ // The current release group doesn't leverage version plans
22
+ if (!releaseGroup.versionPlans) {
23
+ return [];
24
+ }
25
+ // Exclude patterns from .nxignore, .gitignore and explicit version plan config
26
+ let serializedIgnorePatterns = '[]';
27
+ const ignore = (0, ignore_1.getIgnoreObject)();
28
+ if (typeof releaseGroup.versionPlans !== 'boolean' &&
29
+ Array.isArray(releaseGroup.versionPlans.ignorePatternsForPlanCheck) &&
30
+ releaseGroup.versionPlans.ignorePatternsForPlanCheck.length) {
31
+ output_1.output.note({
32
+ title: `Applying configured ignore patterns to changed files${releaseGroup.name !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
33
+ ? ` for release group "${releaseGroup.name}"`
34
+ : ''}`,
35
+ bodyLines: [
36
+ ...releaseGroup.versionPlans.ignorePatternsForPlanCheck.map((pattern) => ` - ${pattern}`),
37
+ ],
38
+ });
39
+ ignore.add(releaseGroup.versionPlans.ignorePatternsForPlanCheck);
40
+ serializedIgnorePatterns = JSON.stringify(releaseGroup.versionPlans.ignorePatternsForPlanCheck);
41
+ }
42
+ let touchedProjects = {};
43
+ if (serializedIgnorePatternsToTouchedProjects.has(serializedIgnorePatterns)) {
44
+ touchedProjects = serializedIgnorePatternsToTouchedProjects.get(serializedIgnorePatterns);
45
+ }
46
+ else {
47
+ // We only care about directly touched projects, not implicitly affected ones etc
48
+ const touchedProjectsArr = await (0, workspace_projects_1.getTouchedProjects)((0, file_utils_1.calculateFileChanges)(changedFiles, fileData, nxArgs, undefined, ignore), projectGraph.nodes);
49
+ touchedProjects = touchedProjectsArr.reduce((acc, project) => ({ ...acc, [project]: true }), {});
50
+ serializedIgnorePatternsToTouchedProjects.set(serializedIgnorePatterns, touchedProjects);
51
+ }
52
+ const touchedProjectsUnderReleaseGroup = releaseGroupFilteredProjectNames.filter((project) => touchedProjects[project]);
53
+ if (touchedProjectsUnderReleaseGroup.length) {
54
+ output_1.output.log({
55
+ title: `Touched projects${hasProjectsFilter ? ` (after --projects filter applied)` : ''} based on changed files${releaseGroup.name !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
56
+ ? ` under release group "${releaseGroup.name}"`
57
+ : ''}`,
58
+ bodyLines: [
59
+ ...touchedProjectsUnderReleaseGroup.map((project) => ` - ${project}`),
60
+ '',
61
+ 'NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.',
62
+ ],
63
+ });
64
+ }
65
+ else {
66
+ output_1.output.log({
67
+ title: `No touched projects${hasProjectsFilter ? ` (after --projects filter applied)` : ''} found based on changed files${typeof releaseGroup.versionPlans !== 'boolean' &&
68
+ Array.isArray(releaseGroup.versionPlans.ignorePatternsForPlanCheck) &&
69
+ releaseGroup.versionPlans.ignorePatternsForPlanCheck.length
70
+ ? ' combined with configured ignore patterns'
71
+ : ''}${releaseGroup.name !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
72
+ ? ` under release group "${releaseGroup.name}"`
73
+ : ''}`,
74
+ });
75
+ }
76
+ return touchedProjectsUnderReleaseGroup;
77
+ };
78
+ }
@@ -62,6 +62,6 @@ export declare function parseConventionalCommitsMessage(message: string): {
62
62
  description: string;
63
63
  breaking: boolean;
64
64
  } | null;
65
- export declare function parseGitCommit(commit: RawGitCommit): GitCommit | null;
65
+ export declare function parseGitCommit(commit: RawGitCommit, isVersionPlanCommit?: boolean): GitCommit | null;
66
66
  export declare function getCommitHash(ref: string): Promise<string>;
67
67
  export declare function getFirstGitCommit(): Promise<string>;
@@ -136,7 +136,7 @@ async function gitAdd({ changedFiles, deletedFiles, dryRun, verbose, logFn, cwd,
136
136
  ignoredFiles.push(f);
137
137
  // git add will fail if trying to add an untracked file that doesn't exist
138
138
  }
139
- else if (changedTrackedFiles.has(f)) {
139
+ else if (changedTrackedFiles.has(f) || dryRun) {
140
140
  filesToAdd.push(f);
141
141
  }
142
142
  }
@@ -281,6 +281,30 @@ function parseConventionalCommitsMessage(message) {
281
281
  breaking: Boolean(match.groups.breaking),
282
282
  };
283
283
  }
284
+ function extractReferencesFromCommitMessage(message, shortHash) {
285
+ const references = [];
286
+ for (const m of message.matchAll(PullRequestRE)) {
287
+ references.push({ type: 'pull-request', value: m[1] });
288
+ }
289
+ for (const m of message.matchAll(IssueRE)) {
290
+ if (!references.some((i) => i.value === m[1])) {
291
+ references.push({ type: 'issue', value: m[1] });
292
+ }
293
+ }
294
+ references.push({ value: shortHash, type: 'hash' });
295
+ return references;
296
+ }
297
+ function getAllAuthorsForCommit(commit) {
298
+ const authors = [commit.author];
299
+ // Additional authors can be specified in the commit body (depending on the VCS provider)
300
+ for (const match of commit.body.matchAll(CoAuthoredByRegex)) {
301
+ authors.push({
302
+ name: (match.groups.name || '').trim(),
303
+ email: (match.groups.email || '').trim(),
304
+ });
305
+ }
306
+ return authors;
307
+ }
284
308
  // https://www.conventionalcommits.org/en/v1.0.0/
285
309
  // https://regex101.com/r/FSfNvA/1
286
310
  const ConventionalCommitRegex = /(?<type>[a-z]+)(\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
@@ -289,7 +313,25 @@ const PullRequestRE = /\([ a-z]*(#\d+)\s*\)/gm;
289
313
  const IssueRE = /(#\d+)/gm;
290
314
  const ChangedFileRegex = /(A|M|D|R\d*|C\d*)\t([^\t\n]*)\t?(.*)?/gm;
291
315
  const RevertHashRE = /This reverts commit (?<hash>[\da-f]{40})./gm;
292
- function parseGitCommit(commit) {
316
+ function parseGitCommit(commit, isVersionPlanCommit = false) {
317
+ // For version plans, we do not require conventional commits and therefore cannot extract data based on that format
318
+ if (isVersionPlanCommit) {
319
+ return {
320
+ ...commit,
321
+ description: commit.message,
322
+ type: '',
323
+ scope: '',
324
+ references: extractReferencesFromCommitMessage(commit.message, commit.shortHash),
325
+ // The commit message is not the source of truth for a breaking (major) change in version plans, so the value is not relevant
326
+ // TODO(v20): Make the current GitCommit interface more clearly tied to conventional commits
327
+ isBreaking: false,
328
+ authors: getAllAuthorsForCommit(commit),
329
+ // Not applicable to version plans
330
+ affectedFiles: [],
331
+ // Not applicable, a version plan cannot have been added in a commit that also reverts another commit
332
+ revertedHashes: [],
333
+ };
334
+ }
293
335
  const parsedMessage = parseConventionalCommitsMessage(commit.message);
294
336
  if (!parsedMessage) {
295
337
  return null;
@@ -298,16 +340,7 @@ function parseGitCommit(commit) {
298
340
  const isBreaking = parsedMessage.breaking || commit.body.includes('BREAKING CHANGE:');
299
341
  let description = parsedMessage.description;
300
342
  // Extract references from message
301
- const references = [];
302
- for (const m of description.matchAll(PullRequestRE)) {
303
- references.push({ type: 'pull-request', value: m[1] });
304
- }
305
- for (const m of description.matchAll(IssueRE)) {
306
- if (!references.some((i) => i.value === m[1])) {
307
- references.push({ type: 'issue', value: m[1] });
308
- }
309
- }
310
- references.push({ value: commit.shortHash, type: 'hash' });
343
+ const references = extractReferencesFromCommitMessage(description, commit.shortHash);
311
344
  // Remove references and normalize
312
345
  description = description.replace(PullRequestRE, '').trim();
313
346
  let type = parsedMessage.type;
@@ -322,13 +355,7 @@ function parseGitCommit(commit) {
322
355
  description = commit.message;
323
356
  }
324
357
  // Find all authors
325
- const authors = [commit.author];
326
- for (const match of commit.body.matchAll(CoAuthoredByRegex)) {
327
- authors.push({
328
- name: (match.groups.name || '').trim(),
329
- email: (match.groups.email || '').trim(),
330
- });
331
- }
358
+ const authors = getAllAuthorsForCommit(commit);
332
359
  // Extract file changes from commit body
333
360
  const affectedFiles = Array.from(commit.body.matchAll(ChangedFileRegex)).reduce((prev, [fullLine, changeType, file1, file2]) =>
334
361
  // file2 only exists for some change types, such as renames
@@ -82,7 +82,7 @@ function createAPI(overrideReleaseConfig) {
82
82
  }
83
83
  if (!args.specifier) {
84
84
  const rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
85
- (0, version_plans_1.setResolvedVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
85
+ await (0, version_plans_1.setResolvedVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes), args.verbose);
86
86
  }
87
87
  else {
88
88
  if (args.verbose && releaseGroups.some((g) => !!g.versionPlans)) {
@@ -3,7 +3,7 @@ interface ExcludeOptions {
3
3
  exclude: string[];
4
4
  }
5
5
  export declare const defaultYargsParserConfiguration: Partial<ParserConfigurationOptions>;
6
- export declare function withExcludeOption(yargs: Argv): Argv<ExcludeOptions>;
6
+ export declare function withExcludeOption<T>(yargs: Argv<T>): Argv<T & ExcludeOptions>;
7
7
  export interface RunOptions {
8
8
  exclude: string;
9
9
  parallel: string;
@@ -20,6 +20,7 @@ export interface RunOptions {
20
20
  batch: boolean;
21
21
  useAgents: boolean;
22
22
  excludeTaskDependencies: boolean;
23
+ skipSync: boolean;
23
24
  }
24
25
  export declare function withRunOptions<T>(yargs: Argv<T>): Argv<T & RunOptions>;
25
26
  export declare function withTargetAndConfigurationOption(yargs: Argv, demandOption?: boolean): Argv<{
@@ -79,6 +79,11 @@ function withRunOptions(yargs) {
79
79
  describe: 'Skips running dependent tasks first',
80
80
  type: 'boolean',
81
81
  default: false,
82
+ })
83
+ .option('skipSync', {
84
+ type: 'boolean',
85
+ // TODO(leo): add description and make it visible once it is stable
86
+ hidden: true,
82
87
  })
83
88
  .options('cloud', {
84
89
  type: 'boolean',