@nx/js 16.9.0-beta.1 → 16.9.0-beta.3

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 (29) hide show
  1. package/executors.json +6 -0
  2. package/generators.json +6 -0
  3. package/package.json +9 -5
  4. package/src/executors/node/node.impl.js +3 -3
  5. package/src/executors/release-publish/format-bytes.d.ts +1 -0
  6. package/src/executors/release-publish/format-bytes.js +28 -0
  7. package/src/executors/release-publish/log-tar.d.ts +1 -0
  8. package/src/executors/release-publish/log-tar.js +68 -0
  9. package/src/executors/release-publish/release-publish.impl.d.ts +5 -0
  10. package/src/executors/release-publish/release-publish.impl.js +95 -0
  11. package/src/executors/release-publish/schema.d.ts +6 -0
  12. package/src/executors/release-publish/schema.json +26 -0
  13. package/src/executors/tsc/lib/batch/build-task-info-per-tsconfig-map.js +1 -1
  14. package/src/executors/tsc/lib/batch/normalize-tasks-options.js +1 -1
  15. package/src/executors/tsc/lib/get-tsconfig.js +5 -7
  16. package/src/executors/tsc/tsc.batch-impl.js +1 -1
  17. package/src/generators/release-version/release-version.d.ts +4 -0
  18. package/src/generators/release-version/release-version.js +131 -0
  19. package/src/generators/release-version/schema.d.ts +1 -0
  20. package/src/generators/release-version/schema.json +45 -0
  21. package/src/generators/release-version/test-utils/create-workspace-with-package-dependencies.d.ts +16 -0
  22. package/src/generators/release-version/test-utils/create-workspace-with-package-dependencies.js +40 -0
  23. package/src/generators/release-version/utils/package.d.ts +12 -0
  24. package/src/generators/release-version/utils/package.js +34 -0
  25. package/src/generators/release-version/utils/resolve-local-package-dependencies.d.ts +6 -0
  26. package/src/generators/release-version/utils/resolve-local-package-dependencies.js +68 -0
  27. package/src/generators/release-version/utils/resolve-version-spec.d.ts +1 -0
  28. package/src/generators/release-version/utils/resolve-version-spec.js +26 -0
  29. package/src/utils/package-json/update-package-json.js +1 -1
package/executors.json CHANGED
@@ -17,6 +17,12 @@
17
17
  "schema": "./src/executors/node/schema.json",
18
18
  "description": "Execute a Node application."
19
19
  },
20
+ "release-publish": {
21
+ "implementation": "./src/executors/release-publish/release-publish.impl",
22
+ "schema": "./src/executors/release-publish/schema.json",
23
+ "description": "DO NOT INVOKE DIRECTLY WITH `nx run`. Use `nx release publish` instead.",
24
+ "hidden": true
25
+ },
20
26
  "verdaccio": {
21
27
  "implementation": "./src/executors/verdaccio/verdaccio.impl",
22
28
  "schema": "./src/executors/verdaccio/schema.json",
package/generators.json CHANGED
@@ -59,6 +59,12 @@
59
59
  "x-type": "library",
60
60
  "description": "Convert a TypeScript library to compile with SWC."
61
61
  },
62
+ "release-version": {
63
+ "factory": "./src/generators/release-version/release-version#releaseVersionGenerator",
64
+ "schema": "./src/generators/release-version/schema.json",
65
+ "description": "DO NOT INVOKE DIRECTLY WITH `nx generate`. Use `nx release version` instead.",
66
+ "hidden": true
67
+ },
62
68
  "setup-verdaccio": {
63
69
  "factory": "./src/generators/setup-verdaccio/generator#setupVerdaccio",
64
70
  "schema": "./src/generators/setup-verdaccio/schema.json",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "16.9.0-beta.1",
3
+ "version": "16.9.0-beta.3",
4
4
  "private": false,
5
5
  "description": "The JS plugin for Nx contains executors and generators that provide the best experience for developing JavaScript and TypeScript projects. ",
6
6
  "repository": {
@@ -39,20 +39,24 @@
39
39
  "@babel/preset-env": "^7.22.9",
40
40
  "@babel/preset-typescript": "^7.22.5",
41
41
  "@babel/runtime": "^7.22.6",
42
- "@nrwl/js": "16.9.0-beta.1",
43
- "@nx/devkit": "16.9.0-beta.1",
44
- "@nx/workspace": "16.9.0-beta.1",
42
+ "@nrwl/js": "16.9.0-beta.3",
43
+ "@nx/devkit": "16.9.0-beta.3",
44
+ "@nx/workspace": "16.9.0-beta.3",
45
45
  "@phenomnomnominal/tsquery": "~5.0.1",
46
46
  "babel-plugin-const-enum": "^1.0.1",
47
47
  "babel-plugin-macros": "^2.8.0",
48
48
  "babel-plugin-transform-typescript-metadata": "^0.3.1",
49
49
  "chalk": "^4.1.0",
50
+ "columnify": "^1.6.0",
50
51
  "detect-port": "^1.5.1",
51
52
  "fast-glob": "3.2.7",
52
53
  "fs-extra": "^11.1.0",
53
54
  "ignore": "^5.0.4",
54
55
  "js-tokens": "^4.0.0",
55
56
  "minimatch": "3.0.5",
57
+ "npm-package-arg": "11.0.1",
58
+ "npm-run-path": "^4.0.1",
59
+ "ora": "5.3.0",
56
60
  "semver": "7.5.3",
57
61
  "source-map-support": "0.5.19",
58
62
  "ts-node": "10.9.1",
@@ -71,5 +75,5 @@
71
75
  "access": "public"
72
76
  },
73
77
  "type": "commonjs",
74
- "gitHead": "5056d6cefb2949ba865019e8b71e633fba28c091"
78
+ "gitHead": "d5692528c089a75c76b21d2d1235d8cc5dbb1d26"
75
79
  }
@@ -23,7 +23,7 @@ function debounce(fn, wait) {
23
23
  async function* nodeExecutor(options, context) {
24
24
  process.env.NODE_ENV ??= context?.configurationName ?? 'development';
25
25
  const project = context.projectGraph.nodes[context.projectName];
26
- const buildTarget = (0, devkit_1.parseTargetString)(options.buildTarget, context.projectGraph);
26
+ const buildTarget = (0, devkit_1.parseTargetString)(options.buildTarget, context);
27
27
  if (!project.data.targets[buildTarget.target]) {
28
28
  throw new Error(`Cannot find build target ${chalk.bold(options.buildTarget)} for project ${chalk.bold(context.projectName)}`);
29
29
  }
@@ -229,7 +229,7 @@ function getExecArgv(options) {
229
229
  return args;
230
230
  }
231
231
  function calculateResolveMappings(context, options) {
232
- const parsed = (0, devkit_1.parseTargetString)(options.buildTarget, context.projectGraph);
232
+ const parsed = (0, devkit_1.parseTargetString)(options.buildTarget, context);
233
233
  const { dependencies } = (0, buildable_libs_utils_1.calculateProjectBuildableDependencies)(context.taskGraph, context.projectGraph, context.root, parsed.project, parsed.target, parsed.configuration);
234
234
  return dependencies.reduce((m, c) => {
235
235
  if (c.node.type !== 'npm' && c.outputs[0] != null) {
@@ -240,7 +240,7 @@ function calculateResolveMappings(context, options) {
240
240
  }
241
241
  function runWaitUntilTargets(options, context) {
242
242
  return Promise.all(options.waitUntilTargets.map(async (waitUntilTarget) => {
243
- const target = (0, devkit_1.parseTargetString)(waitUntilTarget, context.projectGraph);
243
+ const target = (0, devkit_1.parseTargetString)(waitUntilTarget, context);
244
244
  const output = await (0, devkit_1.runExecutor)(target, {}, context);
245
245
  return new Promise(async (resolve) => {
246
246
  let event = await output.next();
@@ -0,0 +1 @@
1
+ export declare const formatBytes: (bytes: any, space?: boolean) => string;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ // Taken from https://github.com/npm/cli/blob/c736b622b8504b07f5a19f631ade42dd40063269/lib/utils/format-bytes.js
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.formatBytes = void 0;
5
+ // Convert bytes to printable output, for file reporting in tarballs
6
+ // Only supports up to GB because that's way larger than anything the registry
7
+ // supports anyways.
8
+ const formatBytes = (bytes, space = true) => {
9
+ let spacer = '';
10
+ if (space) {
11
+ spacer = ' ';
12
+ }
13
+ if (bytes < 1000) {
14
+ // B
15
+ return `${bytes}${spacer}B`;
16
+ }
17
+ if (bytes < 1000000) {
18
+ // kB
19
+ return `${(bytes / 1000).toFixed(1)}${spacer}kB`;
20
+ }
21
+ if (bytes < 1000000000) {
22
+ // MB
23
+ return `${(bytes / 1000000).toFixed(1)}${spacer}MB`;
24
+ }
25
+ // GB
26
+ return `${(bytes / 1000000000).toFixed(1)}${spacer}GB`;
27
+ };
28
+ exports.formatBytes = formatBytes;
@@ -0,0 +1 @@
1
+ export declare const logTar: (tarball: any, opts?: {}) => void;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logTar = void 0;
4
+ // Adapted from https://github.com/npm/cli/blob/c736b622b8504b07f5a19f631ade42dd40063269/lib/utils/tar.js
5
+ const chalk = require("chalk");
6
+ const columnify = require("columnify");
7
+ const format_bytes_1 = require("./format-bytes");
8
+ const logTar = (tarball, opts = {}) => {
9
+ // @ts-ignore
10
+ const { unicode = true } = opts;
11
+ console.log('');
12
+ console.log(`${unicode ? '📦 ' : 'package:'} ${tarball.name}@${tarball.version}`);
13
+ console.log(chalk.magenta('=== Tarball Contents ==='));
14
+ if (tarball.files.length) {
15
+ console.log('');
16
+ const columnData = columnify(tarball.files
17
+ .map((f) => {
18
+ const bytes = (0, format_bytes_1.formatBytes)(f.size, false);
19
+ return /^node_modules\//.test(f.path)
20
+ ? null
21
+ : { path: f.path, size: `${bytes}` };
22
+ })
23
+ .filter((f) => f), {
24
+ include: ['size', 'path'],
25
+ showHeaders: false,
26
+ });
27
+ columnData.split('\n').forEach((line) => {
28
+ console.log(line);
29
+ });
30
+ }
31
+ if (tarball.bundled.length) {
32
+ console.log(chalk.magenta('=== Bundled Dependencies ==='));
33
+ tarball.bundled.forEach((name) => console.log('', name));
34
+ }
35
+ console.log(chalk.magenta('=== Tarball Details ==='));
36
+ console.log(columnify([
37
+ { name: 'name:', value: tarball.name },
38
+ { name: 'version:', value: tarball.version },
39
+ tarball.filename && { name: 'filename:', value: tarball.filename },
40
+ { name: 'package size:', value: (0, format_bytes_1.formatBytes)(tarball.size) },
41
+ { name: 'unpacked size:', value: (0, format_bytes_1.formatBytes)(tarball.unpackedSize) },
42
+ { name: 'shasum:', value: tarball.shasum },
43
+ {
44
+ name: 'integrity:',
45
+ value: tarball.integrity.toString().slice(0, 20) +
46
+ '[...]' +
47
+ tarball.integrity.toString().slice(80),
48
+ },
49
+ tarball.bundled.length && {
50
+ name: 'bundled deps:',
51
+ value: tarball.bundled.length,
52
+ },
53
+ tarball.bundled.length && {
54
+ name: 'bundled files:',
55
+ value: tarball.entryCount - tarball.files.length,
56
+ },
57
+ tarball.bundled.length && {
58
+ name: 'own files:',
59
+ value: tarball.files.length,
60
+ },
61
+ { name: 'total files:', value: tarball.entryCount },
62
+ ].filter((x) => x), {
63
+ include: ['name', 'value'],
64
+ showHeaders: false,
65
+ }));
66
+ console.log('', '');
67
+ };
68
+ exports.logTar = logTar;
@@ -0,0 +1,5 @@
1
+ import { ExecutorContext } from '@nx/devkit';
2
+ import { PublishExecutorSchema } from './schema';
3
+ export default function runExecutor(options: PublishExecutorSchema, context: ExecutorContext): Promise<{
4
+ success: boolean;
5
+ }>;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const devkit_1 = require("@nx/devkit");
4
+ const child_process_1 = require("child_process");
5
+ const npm_run_path_1 = require("npm-run-path");
6
+ const log_tar_1 = require("./log-tar");
7
+ const chalk = require("chalk");
8
+ const LARGE_BUFFER = 1024 * 1000000;
9
+ function processEnv(color) {
10
+ const env = {
11
+ ...process.env,
12
+ ...(0, npm_run_path_1.env)(),
13
+ };
14
+ if (color) {
15
+ env.FORCE_COLOR = `${color}`;
16
+ }
17
+ return env;
18
+ }
19
+ async function runExecutor(options, context) {
20
+ const projectConfig = context.projectsConfigurations.projects[context.projectName];
21
+ const packageRoot = (0, devkit_1.joinPathFragments)(context.root, options.packageRoot ?? projectConfig.root);
22
+ const npmPublishCommandSegments = [`npm publish --json`];
23
+ if (options.registry) {
24
+ npmPublishCommandSegments.push(`--registry=${options.registry}`);
25
+ }
26
+ if (options.tag) {
27
+ npmPublishCommandSegments.push(`--tag=${options.tag}`);
28
+ }
29
+ if (options.dryRun) {
30
+ npmPublishCommandSegments.push(`--dry-run`);
31
+ }
32
+ // Resolve values using the `npm config` command so that things like environment variables and `publishConfig`s are accounted for
33
+ const registry = options.registry ?? (0, child_process_1.execSync)(`npm config get registry`).toString().trim();
34
+ const tag = options.tag ?? (0, child_process_1.execSync)(`npm config get tag`).toString().trim();
35
+ try {
36
+ const output = (0, child_process_1.execSync)(npmPublishCommandSegments.join(' '), {
37
+ maxBuffer: LARGE_BUFFER,
38
+ env: processEnv(true),
39
+ cwd: packageRoot,
40
+ stdio: ['ignore', 'pipe', 'pipe'],
41
+ });
42
+ const stdoutData = JSON.parse(output.toString());
43
+ // If npm workspaces are in use, the publish output will nest the data under the package name, so we normalize it first
44
+ const normalizedStdoutData = stdoutData[context.projectName] ?? stdoutData;
45
+ (0, log_tar_1.logTar)(normalizedStdoutData);
46
+ if (options.dryRun) {
47
+ console.log(`Would publish to ${registry} with tag "${tag}", but ${chalk.keyword('orange')('[dry-run]')} was set`);
48
+ }
49
+ else {
50
+ console.log(`Published to ${registry} with tag "${tag}"`);
51
+ }
52
+ return {
53
+ success: true,
54
+ };
55
+ }
56
+ catch (err) {
57
+ try {
58
+ const projectPackageJson = (0, devkit_1.readJsonFile)((0, devkit_1.joinPathFragments)(packageRoot, 'package.json'));
59
+ const name = projectPackageJson.name;
60
+ const currentVersion = projectPackageJson.version;
61
+ const stdoutData = JSON.parse(err.stdout?.toString() || '{}');
62
+ if (stdoutData.error?.code === 'EPUBLISHCONFLICT' ||
63
+ (stdoutData.error?.code === 'E403' &&
64
+ stdoutData.error?.body?.error?.includes('You cannot publish over the previously published versions'))) {
65
+ // If package and project name match, make it terser
66
+ let packageTxt = name === context.projectName
67
+ ? `package "${name}"`
68
+ : `package "${name}" from project "${context.projectName}"`;
69
+ console.warn(`Skipping ${packageTxt}, as v${currentVersion} has already been published to ${registry} with tag "${tag}"`);
70
+ return {
71
+ success: true,
72
+ };
73
+ }
74
+ console.error('npm publish error:');
75
+ if (stdoutData.error.summary) {
76
+ console.error(stdoutData.error.summary);
77
+ }
78
+ if (stdoutData.error.detail) {
79
+ console.error(stdoutData.error.detail);
80
+ }
81
+ return {
82
+ success: false,
83
+ };
84
+ }
85
+ catch (err) {
86
+ // npm v9 onwards seems to guarantee stdout will be well formed JSON when --json is used, so maybe we need to
87
+ // specify that as minimum supported version? (comes with node 18 and 20 by default)
88
+ console.error('Something unexpected went wrong when processing the npm publish output\n', err);
89
+ return {
90
+ success: false,
91
+ };
92
+ }
93
+ }
94
+ }
95
+ exports.default = runExecutor;
@@ -0,0 +1,6 @@
1
+ export interface PublishExecutorSchema {
2
+ packageRoot?: string;
3
+ registry?: string;
4
+ tag?: string;
5
+ dryRun?: boolean;
6
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "version": 2,
4
+ "title": "Implementation details of `nx release publish`",
5
+ "description": "DO NOT INVOKE DIRECTLY WITH `nx run`. Use `nx release publish` instead.",
6
+ "type": "object",
7
+ "properties": {
8
+ "packageRoot": {
9
+ "type": "string",
10
+ "description": "The root directory of the directory (containing a manifest file at its root) to publish. Defaults to the project root."
11
+ },
12
+ "registry": {
13
+ "type": "string",
14
+ "description": "The registry to publish the package to."
15
+ },
16
+ "tag": {
17
+ "type": "string",
18
+ "description": "The distribution tag to apply to the published package."
19
+ },
20
+ "dryRun": {
21
+ "type": "boolean",
22
+ "description": "Whether to run the command without actually publishing the package to the registry."
23
+ }
24
+ },
25
+ "required": []
26
+ }
@@ -33,7 +33,7 @@ function processTasksAndPopulateTsConfigTaskInfoMap(tsConfigTaskInfoMap, tasksOp
33
33
  }
34
34
  }
35
35
  function createTaskInfo(taskName, taskOptions, context, tsConfig) {
36
- const target = (0, devkit_1.parseTargetString)(taskName, context.projectGraph);
36
+ const target = (0, devkit_1.parseTargetString)(taskName, context);
37
37
  const taskContext = {
38
38
  ...context,
39
39
  // batch executors don't get these in the context, we provide them
@@ -5,7 +5,7 @@ const devkit_1 = require("@nx/devkit");
5
5
  const normalize_options_1 = require("../normalize-options");
6
6
  function normalizeTasksOptions(inputs, context) {
7
7
  return Object.entries(inputs).reduce((tasksOptions, [taskName, options]) => {
8
- const { project } = (0, devkit_1.parseTargetString)(taskName, context.projectGraph);
8
+ const { project } = (0, devkit_1.parseTargetString)(taskName, context);
9
9
  const { sourceRoot, root } = context.projectsConfigurations.projects[project];
10
10
  tasksOptions[taskName] = (0, normalize_options_1.normalizeOptions)(options, context.root, sourceRoot, root);
11
11
  return tasksOptions;
@@ -14,7 +14,7 @@ function getProcessedTaskTsConfigs(tasks, tasksOptions, context) {
14
14
  exports.getProcessedTaskTsConfigs = getProcessedTaskTsConfigs;
15
15
  const projectTsConfigCache = new Map();
16
16
  function generateTaskProjectTsConfig(task, tasksOptions, context, taskInMemoryTsConfigMap) {
17
- const { project } = (0, devkit_1.parseTargetString)(task, context.projectGraph);
17
+ const { project } = (0, devkit_1.parseTargetString)(task, context);
18
18
  if (projectTsConfigCache.has(project)) {
19
19
  const { tsConfig, tsConfigPath } = projectTsConfigCache.get(project);
20
20
  taskInMemoryTsConfigMap[task] = tsConfig;
@@ -48,13 +48,11 @@ function generateTaskProjectTsConfig(task, tasksOptions, context, taskInMemoryTs
48
48
  return tsConfigPath;
49
49
  }
50
50
  function getDependencyTasksInOtherProjects(task, project, context) {
51
- return context.taskGraph.dependencies[task].filter((t) => t !== task &&
52
- (0, devkit_1.parseTargetString)(t, context.projectGraph).project !== project);
51
+ return context.taskGraph.dependencies[task].filter((t) => t !== task && (0, devkit_1.parseTargetString)(t, context).project !== project);
53
52
  }
54
53
  function getDependencyTasksInSameProject(task, context) {
55
- const { project: taskProject } = (0, devkit_1.parseTargetString)(task, context.projectGraph);
56
- return Object.keys(context.taskGraph.tasks).filter((t) => t !== task &&
57
- (0, devkit_1.parseTargetString)(t, context.projectGraph).project === taskProject);
54
+ const { project: taskProject } = (0, devkit_1.parseTargetString)(task, context);
55
+ return Object.keys(context.taskGraph.tasks).filter((t) => t !== task && (0, devkit_1.parseTargetString)(t, context).project === taskProject);
58
56
  }
59
57
  function getInMemoryTsConfig(tsConfig, taskOptions, projectReferences) {
60
58
  const originalTsConfig = (0, devkit_1.readJsonFile)(tsConfig, {
@@ -82,7 +80,7 @@ function getInMemoryTsConfig(tsConfig, taskOptions, projectReferences) {
82
80
  };
83
81
  }
84
82
  function hasTscExecutor(task, context) {
85
- const { project, target } = (0, devkit_1.parseTargetString)(task, context.projectGraph);
83
+ const { project, target } = (0, devkit_1.parseTargetString)(task, context);
86
84
  return (context.projectGraph.nodes[project].data.targets[target].executor ===
87
85
  '@nx/js:tsc');
88
86
  }
@@ -170,7 +170,7 @@ function createTypescriptCompilationContext(tsConfigTaskInfoMap, taskInMemoryTsC
170
170
  Object.entries(taskInMemoryTsConfigMap).forEach(([task, tsConfig]) => {
171
171
  if (!tsCompilationContext[tsConfig.path]) {
172
172
  tsCompilationContext[tsConfig.path] = {
173
- project: (0, devkit_1.parseTargetString)(task, context.projectGraph).project,
173
+ project: (0, devkit_1.parseTargetString)(task, context).project,
174
174
  transformers: [],
175
175
  tsConfig: tsConfig,
176
176
  };
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { ReleaseVersionGeneratorSchema } from './schema';
3
+ export declare function releaseVersionGenerator(tree: Tree, options: ReleaseVersionGeneratorSchema): Promise<void>;
4
+ export default releaseVersionGenerator;
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.releaseVersionGenerator = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const chalk = require("chalk");
6
+ const child_process_1 = require("child_process");
7
+ const version_1 = require("nx/src/command-line/release/version");
8
+ const utils_1 = require("nx/src/tasks-runner/utils");
9
+ const ora = require("ora");
10
+ const path_1 = require("path");
11
+ const resolve_local_package_dependencies_1 = require("./utils/resolve-local-package-dependencies");
12
+ async function releaseVersionGenerator(tree, options) {
13
+ const projects = options.projects;
14
+ // Resolve any custom package roots for each project upfront as they will need to be reused during dependency resolution
15
+ const projectNameToPackageRootMap = new Map();
16
+ for (const project of projects) {
17
+ projectNameToPackageRootMap.set(project.name,
18
+ // Default to the project root if no custom packageRoot
19
+ !options.packageRoot
20
+ ? project.data.root
21
+ : (0, utils_1.interpolate)(options.packageRoot, {
22
+ workspaceRoot: '',
23
+ projectRoot: project.data.root,
24
+ projectName: project.name,
25
+ }));
26
+ }
27
+ let currentVersion;
28
+ for (const project of projects) {
29
+ const projectName = project.name;
30
+ const packageRoot = projectNameToPackageRootMap.get(projectName);
31
+ const packageJsonPath = (0, devkit_1.joinPathFragments)(packageRoot, 'package.json');
32
+ const workspaceRelativePackageJsonPath = (0, path_1.relative)(devkit_1.workspaceRoot, packageJsonPath);
33
+ const color = getColor(projectName);
34
+ const log = (msg) => {
35
+ console.log(color.instance.bold(projectName) + ' ' + msg);
36
+ };
37
+ if (!tree.exists(packageJsonPath)) {
38
+ throw new Error(`The project "${projectName}" does not have a package.json available at ${workspaceRelativePackageJsonPath}.
39
+
40
+ To fix this you will either need to add a package.json file at that location, or configure "release" within your nx.json to exclude "${projectName}" from the current release group, or amend the packageRoot configuration to point to where the package.json should be.`);
41
+ }
42
+ devkit_1.output.logSingleLine(`Running release version for project: ${color.instance.bold(project.name)}`);
43
+ const projectPackageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
44
+ log(`🔍 Reading data for package "${projectPackageJson.name}" from ${workspaceRelativePackageJsonPath}`);
45
+ const { name: packageName, version: currentVersionFromDisk } = projectPackageJson;
46
+ switch (options.currentVersionResolver) {
47
+ case 'registry': {
48
+ const metadata = options.currentVersionResolverMetadata;
49
+ const registry = metadata?.registry ?? 'https://registry.npmjs.org';
50
+ const tag = metadata?.tag ?? 'latest';
51
+ // If the currentVersionResolver is set to registry, we only want to make the request once for the whole batch of projects
52
+ if (!currentVersion) {
53
+ const spinner = ora(`${Array.from(new Array(projectName.length + 3)).join(' ')}Resolving the current version for tag "${tag}" on ${registry}`);
54
+ spinner.color =
55
+ color.spinnerColor;
56
+ spinner.start();
57
+ // Must be non-blocking async to allow spinner to render
58
+ currentVersion = await new Promise((resolve, reject) => {
59
+ (0, child_process_1.exec)(`npm view ${packageName} version --registry=${registry} --tag=${tag}`, (error, stdout, stderr) => {
60
+ if (error) {
61
+ return reject(error);
62
+ }
63
+ if (stderr) {
64
+ return reject(stderr);
65
+ }
66
+ return resolve(stdout.trim());
67
+ });
68
+ });
69
+ spinner.stop();
70
+ log(`📄 Resolved the current version as ${currentVersion} for tag "${tag}" from registry ${registry}`);
71
+ }
72
+ else {
73
+ log(`📄 Using the current version ${currentVersion} already resolved from the registry ${registry}`);
74
+ }
75
+ break;
76
+ }
77
+ case 'disk':
78
+ currentVersion = currentVersionFromDisk;
79
+ log(`📄 Resolved the current version as ${currentVersion} from ${packageJsonPath}`);
80
+ break;
81
+ default:
82
+ throw new Error(`Invalid value for options.currentVersionResolver: ${options.currentVersionResolver}`);
83
+ }
84
+ // Resolve any local package dependencies for this project (before applying the new version)
85
+ const localPackageDependencies = (0, resolve_local_package_dependencies_1.resolveLocalPackageDependencies)(tree, options.projectGraph, projects, projectNameToPackageRootMap);
86
+ const newVersion = (0, version_1.deriveNewSemverVersion)(currentVersion, options.specifier, options.preid);
87
+ (0, devkit_1.writeJson)(tree, packageJsonPath, {
88
+ ...projectPackageJson,
89
+ version: newVersion,
90
+ });
91
+ log(`✍️ New version ${newVersion} written to ${workspaceRelativePackageJsonPath}`);
92
+ const dependentProjects = Object.values(localPackageDependencies)
93
+ .filter((localPackageDependencies) => {
94
+ return localPackageDependencies.some((localPackageDependency) => localPackageDependency.target === project.name);
95
+ })
96
+ .flat();
97
+ if (dependentProjects.length > 0) {
98
+ log(`✍️ Applying new version ${newVersion} to ${dependentProjects.length} ${dependentProjects.length > 1
99
+ ? 'packages which depend'
100
+ : 'package which depends'} on ${project.name}`);
101
+ }
102
+ for (const dependentProject of dependentProjects) {
103
+ (0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(projectNameToPackageRootMap.get(dependentProject.source), 'package.json'), (json) => {
104
+ json[dependentProject.dependencyCollection][packageName] = newVersion;
105
+ return json;
106
+ });
107
+ }
108
+ }
109
+ }
110
+ exports.releaseVersionGenerator = releaseVersionGenerator;
111
+ exports.default = releaseVersionGenerator;
112
+ const colors = [
113
+ { instance: chalk.green, spinnerColor: 'green' },
114
+ { instance: chalk.greenBright, spinnerColor: 'green' },
115
+ { instance: chalk.red, spinnerColor: 'red' },
116
+ { instance: chalk.redBright, spinnerColor: 'red' },
117
+ { instance: chalk.cyan, spinnerColor: 'cyan' },
118
+ { instance: chalk.cyanBright, spinnerColor: 'cyan' },
119
+ { instance: chalk.yellow, spinnerColor: 'yellow' },
120
+ { instance: chalk.yellowBright, spinnerColor: 'yellow' },
121
+ { instance: chalk.magenta, spinnerColor: 'magenta' },
122
+ { instance: chalk.magentaBright, spinnerColor: 'magenta' },
123
+ ];
124
+ function getColor(projectName) {
125
+ let code = 0;
126
+ for (let i = 0; i < projectName.length; ++i) {
127
+ code += projectName.charCodeAt(i);
128
+ }
129
+ const colorIndex = code % colors.length;
130
+ return colors[colorIndex];
131
+ }
@@ -0,0 +1 @@
1
+ export { ReleaseVersionGeneratorSchema } from 'nx/src/command-line/release/version';
@@ -0,0 +1,45 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "NxJSReleaseVersionGenerator",
4
+ "cli": "nx",
5
+ "title": "Implementation details of `nx release version`",
6
+ "description": "DO NOT INVOKE DIRECTLY WITH `nx generate`. Use `nx release version` instead.",
7
+ "type": "object",
8
+ "properties": {
9
+ "projects": {
10
+ "type": "array",
11
+ "description": "The ProjectGraphProjectNodes being versioned in the current execution.",
12
+ "items": {
13
+ "type": "object"
14
+ }
15
+ },
16
+ "projectGraph": {
17
+ "type": "object",
18
+ "description": "ProjectGraph instance"
19
+ },
20
+ "specifier": {
21
+ "type": "string",
22
+ "description": "Exact version or semver keyword to apply to the selected release group. NOTE: This should be set on the release group level, not the project level."
23
+ },
24
+ "preid": {
25
+ "type": "string",
26
+ "description": "The optional prerelease identifier to apply to the version, in the case that specifier has been set to prerelease."
27
+ },
28
+ "packageRoot": {
29
+ "type": "string",
30
+ "description": "The root directory of the directory (containing a manifest file at its root) to publish. Defaults to the project root"
31
+ },
32
+ "currentVersionResolver": {
33
+ "type": "string",
34
+ "default": "disk",
35
+ "description": "Which approach to use to determine the current version of the project.",
36
+ "enum": ["registry", "disk"]
37
+ },
38
+ "currentVersionResolverMetadata": {
39
+ "type": "object",
40
+ "description": "Additional metadata to pass to the current version resolver.",
41
+ "default": {}
42
+ }
43
+ },
44
+ "required": ["projects", "projectGraph", "specifier"]
45
+ }
@@ -0,0 +1,16 @@
1
+ import { ProjectGraph, Tree } from '@nx/devkit';
2
+ interface ProjectAndPackageData {
3
+ [projectName: string]: {
4
+ projectRoot: string;
5
+ packageName: string;
6
+ version: string;
7
+ packageJsonPath: string;
8
+ localDependencies: {
9
+ projectName: string;
10
+ dependencyCollection: 'dependencies' | 'devDependencies' | 'optionalDependencies';
11
+ version: string;
12
+ }[];
13
+ };
14
+ }
15
+ export declare function createWorkspaceWithPackageDependencies(tree: Tree, projectAndPackageData: ProjectAndPackageData): ProjectGraph;
16
+ export {};
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWorkspaceWithPackageDependencies = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ function createWorkspaceWithPackageDependencies(tree, projectAndPackageData) {
6
+ const projectGraph = {
7
+ nodes: {},
8
+ dependencies: {},
9
+ };
10
+ for (const [projectName, data] of Object.entries(projectAndPackageData)) {
11
+ const packageJsonContents = {
12
+ name: data.packageName,
13
+ version: data.version,
14
+ };
15
+ for (const dependency of data.localDependencies) {
16
+ const dependencyPackageName = projectAndPackageData[dependency.projectName].packageName;
17
+ packageJsonContents[dependency.dependencyCollection] = {
18
+ ...packageJsonContents[dependency.dependencyCollection],
19
+ [dependencyPackageName]: dependency.version,
20
+ };
21
+ }
22
+ // add the project and its nx project level dependencies to the projectGraph
23
+ projectGraph.nodes[projectName] = {
24
+ name: projectName,
25
+ type: 'lib',
26
+ data: {
27
+ root: data.projectRoot,
28
+ },
29
+ };
30
+ projectGraph.dependencies[projectName] = data.localDependencies.map((dependency) => ({
31
+ source: projectName,
32
+ target: dependency.projectName,
33
+ type: 'static',
34
+ }));
35
+ // create the package.json in the tree
36
+ (0, devkit_1.writeJson)(tree, data.packageJsonPath, packageJsonContents);
37
+ }
38
+ return projectGraph;
39
+ }
40
+ exports.createWorkspaceWithPackageDependencies = createWorkspaceWithPackageDependencies;
@@ -0,0 +1,12 @@
1
+ import { PackageJson } from 'nx/src/utils/package-json';
2
+ export declare class Package {
3
+ private packageJson;
4
+ name: string;
5
+ version: string;
6
+ location: string;
7
+ constructor(packageJson: PackageJson, workspaceRoot: string, workspaceRelativeLocation: string);
8
+ getLocalDependency(depName: string): {
9
+ collection: 'dependencies' | 'devDependencies' | 'optionalDependencies';
10
+ spec: string;
11
+ } | null;
12
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Package = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ class Package {
6
+ constructor(packageJson, workspaceRoot, workspaceRelativeLocation) {
7
+ this.packageJson = packageJson;
8
+ this.name = packageJson.name;
9
+ this.version = packageJson.version;
10
+ this.location = (0, devkit_1.joinPathFragments)(workspaceRoot, workspaceRelativeLocation);
11
+ }
12
+ getLocalDependency(depName) {
13
+ if (this.packageJson.dependencies?.[depName]) {
14
+ return {
15
+ collection: 'dependencies',
16
+ spec: this.packageJson.dependencies[depName],
17
+ };
18
+ }
19
+ if (this.packageJson.devDependencies?.[depName]) {
20
+ return {
21
+ collection: 'devDependencies',
22
+ spec: this.packageJson.devDependencies[depName],
23
+ };
24
+ }
25
+ if (this.packageJson.optionalDependencies?.[depName]) {
26
+ return {
27
+ collection: 'optionalDependencies',
28
+ spec: this.packageJson.optionalDependencies[depName],
29
+ };
30
+ }
31
+ return null;
32
+ }
33
+ }
34
+ exports.Package = Package;
@@ -0,0 +1,6 @@
1
+ import { ProjectGraph, ProjectGraphDependency, ProjectGraphProjectNode, Tree } from '@nx/devkit';
2
+ interface LocalPackageDependency extends ProjectGraphDependency {
3
+ dependencyCollection: 'dependencies' | 'devDependencies' | 'optionalDependencies';
4
+ }
5
+ export declare function resolveLocalPackageDependencies(tree: Tree, projectGraph: ProjectGraph, projects: ProjectGraphProjectNode[], projectNameToPackageRootMap: Map<string, string>): Record<string, LocalPackageDependency[]>;
6
+ export {};
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveLocalPackageDependencies = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const semver_1 = require("semver");
6
+ const package_1 = require("./package");
7
+ const resolve_version_spec_1 = require("./resolve-version-spec");
8
+ function resolveLocalPackageDependencies(tree, projectGraph, projects, projectNameToPackageRootMap) {
9
+ const localPackageDependencies = {};
10
+ const projectNodeToPackageMap = new Map();
11
+ // Iterate through the projects being released and resolve any relevant package.json data
12
+ for (const projectNode of projects) {
13
+ // Resolve the package.json path for the project, taking into account any custom packageRoot settings
14
+ const packageRoot = projectNameToPackageRootMap.get(projectNode.name);
15
+ if (!packageRoot) {
16
+ continue;
17
+ }
18
+ const packageJsonPath = (0, devkit_1.joinPathFragments)(packageRoot, 'package.json');
19
+ if (!tree.exists(packageJsonPath)) {
20
+ continue;
21
+ }
22
+ const packageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
23
+ const pkg = new package_1.Package(packageJson, devkit_1.workspaceRoot, packageRoot);
24
+ projectNodeToPackageMap.set(projectNode, pkg);
25
+ }
26
+ // populate local npm package dependencies
27
+ for (const projectDeps of Object.values(projectGraph.dependencies)) {
28
+ const workspaceDeps = projectDeps.filter((dep) => !isExternalNpmDependency(dep.target) &&
29
+ !isExternalNpmDependency(dep.source));
30
+ for (const dep of workspaceDeps) {
31
+ const source = projectGraph.nodes[dep.source];
32
+ const target = projectGraph.nodes[dep.target];
33
+ if (!source ||
34
+ !projectNodeToPackageMap.has(source) ||
35
+ !target ||
36
+ !projectNodeToPackageMap.has(target)) {
37
+ // only relevant for dependencies between two workspace projects with Package objects
38
+ continue;
39
+ }
40
+ const sourcePackage = projectNodeToPackageMap.get(source);
41
+ const targetPackage = projectNodeToPackageMap.get(target);
42
+ const sourceNpmDependency = sourcePackage.getLocalDependency(targetPackage.name);
43
+ if (!sourceNpmDependency) {
44
+ continue;
45
+ }
46
+ const targetVersionSpec = (0, resolve_version_spec_1.resolveVersionSpec)(targetPackage.name, targetPackage.version, sourceNpmDependency.spec, sourcePackage.location);
47
+ const targetMatchesRequirement =
48
+ // For file: and workspace: protocols the targetVersionSpec could be a path, so we check if it matches the target's location
49
+ targetVersionSpec === targetPackage.location ||
50
+ (0, semver_1.satisfies)(targetPackage.version, targetVersionSpec);
51
+ if (targetMatchesRequirement) {
52
+ // track only local package dependencies that are satisfied by the target's version
53
+ localPackageDependencies[dep.source] = [
54
+ ...(localPackageDependencies[dep.source] || []),
55
+ {
56
+ ...dep,
57
+ dependencyCollection: sourceNpmDependency.collection,
58
+ },
59
+ ];
60
+ }
61
+ }
62
+ }
63
+ return localPackageDependencies;
64
+ }
65
+ exports.resolveLocalPackageDependencies = resolveLocalPackageDependencies;
66
+ function isExternalNpmDependency(dep) {
67
+ return dep.startsWith('npm:');
68
+ }
@@ -0,0 +1 @@
1
+ export declare function resolveVersionSpec(name: string, version: string, spec: string, location?: string): string;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveVersionSpec = void 0;
4
+ const npa = require("npm-package-arg");
5
+ function resolveVersionSpec(name, version, spec, location) {
6
+ // yarn classic uses link instead of file, normalize to match what npm expects
7
+ spec = spec.replace(/^link:/, 'file:');
8
+ // Support workspace: protocol for pnpm and yarn 2+ (https://pnpm.io/workspaces#workspace-protocol-workspace)
9
+ const isWorkspaceSpec = /^workspace:/.test(spec);
10
+ if (isWorkspaceSpec) {
11
+ spec = spec.replace(/^workspace:/, '');
12
+ // replace aliases (https://pnpm.io/workspaces#referencing-workspace-packages-through-aliases)
13
+ if (spec === '*' || spec === '^' || spec === '~') {
14
+ if (version) {
15
+ const prefix = spec === '*' ? '' : spec;
16
+ spec = `${prefix}${version}`;
17
+ }
18
+ else {
19
+ spec = '*';
20
+ }
21
+ }
22
+ }
23
+ const npaResult = npa.resolve(name, spec, location);
24
+ return npaResult.fetchSpec;
25
+ }
26
+ exports.resolveVersionSpec = resolveVersionSpec;
@@ -15,7 +15,7 @@ const get_main_file_dir_1 = require("../get-main-file-dir");
15
15
  function updatePackageJson(options, context, target, dependencies, fileMap = null) {
16
16
  let packageJson;
17
17
  if (fileMap == null) {
18
- fileMap = (0, nx_deps_cache_1.readProjectFileMapCache)()?.projectFileMap || {};
18
+ fileMap = (0, nx_deps_cache_1.readFileMapCache)()?.fileMap?.projectFileMap || {};
19
19
  }
20
20
  if (options.updateBuildableProjectDepsInPackageJson) {
21
21
  packageJson = (0, create_package_json_1.createPackageJson)(context.projectName, context.projectGraph, {