@nx/devkit 17.0.3 → 17.0.5

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 (33) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +9 -4
  3. package/nx.js +2 -2
  4. package/package.json +5 -4
  5. package/public-api.d.ts +1 -1
  6. package/src/executors/parse-target-string.js +3 -2
  7. package/src/executors/read-target-options.js +8 -5
  8. package/src/generators/add-build-target-defaults.d.ts +2 -0
  9. package/src/generators/add-build-target-defaults.js +18 -0
  10. package/src/generators/artifact-name-and-directory-utils.js +16 -5
  11. package/src/generators/format-files.js +1 -1
  12. package/src/generators/project-name-and-root-utils.js +156 -85
  13. package/src/generators/to-js.d.ts +7 -1
  14. package/src/generators/to-js.js +5 -4
  15. package/src/utils/calculate-hash-for-create-nodes.d.ts +2 -0
  16. package/src/utils/calculate-hash-for-create-nodes.js +16 -0
  17. package/src/utils/config-utils.d.ts +4 -0
  18. package/src/utils/config-utils.js +75 -0
  19. package/src/utils/convert-nx-executor.js +3 -3
  20. package/src/utils/get-named-inputs.d.ts +8 -0
  21. package/src/utils/get-named-inputs.js +26 -0
  22. package/src/utils/get-workspace-layout.d.ts +3 -3
  23. package/src/utils/invoke-nx-generator.js +1 -1
  24. package/src/utils/log-show-project-command.d.ts +1 -0
  25. package/src/utils/log-show-project-command.js +14 -0
  26. package/src/utils/package-json.d.ts +2 -1
  27. package/src/utils/package-json.js +24 -8
  28. package/src/utils/replace-project-configuration-with-plugin.d.ts +2 -0
  29. package/src/utils/replace-project-configuration-with-plugin.js +137 -0
  30. package/src/utils/update-package-scripts.d.ts +2 -0
  31. package/src/utils/update-package-scripts.js +183 -0
  32. package/src/utils/versions.d.ts +1 -1
  33. package/src/utils/versions.js +1 -1
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2017-2023 Narwhal Technologies Inc.
3
+ Copyright (c) 2017-2024 Narwhal Technologies Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -1,4 +1,9 @@
1
- <p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
1
+ <p style="text-align: center;">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
4
+ <img alt="Nx - Smart Monorepos · Fast CI" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
5
+ </picture>
6
+ </p>
2
7
 
3
8
  <div style="text-align: center;">
4
9
 
@@ -15,9 +20,9 @@
15
20
 
16
21
  <hr>
17
22
 
18
- # Nx: Smart, Fast and Extensible Build System
23
+ # Nx: Smart Monorepos · Fast CI
19
24
 
20
- Nx is a next generation build system with first class monorepo support and powerful integrations.
25
+ Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.
21
26
 
22
27
  This package contains a set of utilities for creating Nx plugins.
23
28
 
@@ -59,5 +64,5 @@ npx nx@latest init
59
64
  - [Blog Posts About Nx](https://blog.nrwl.io/nx/home)
60
65
 
61
66
  <p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
62
- width="100%" alt="Nx - Smart, Fast and Extensible Build System"></a></p>
67
+ width="100%" alt="Nx - Smart Monorepos · Fast CI"></a></p>
63
68
 
package/nx.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.requireNx = void 0;
4
- // After Nx v18, this can be removed and replaced with either:
4
+ // After Nx v19, this can be removed and replaced with either:
5
5
  // - import {} from 'nx/src/devkit-exports'
6
6
  // - import {} from 'nx/src/devkit-internals'
7
7
  function requireNx() {
@@ -9,7 +9,7 @@ function requireNx() {
9
9
  try {
10
10
  result = {
11
11
  ...result,
12
- // Remove in Nx v18, devkit should not support Nx v16.0.2 at that point.
12
+ // Remove in Nx v19, devkit should not support Nx v16.0.2 at that point.
13
13
  ...require('nx/src/devkit-internals'),
14
14
  };
15
15
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@nx/devkit",
3
- "version": "17.0.3",
3
+ "version": "17.0.5",
4
4
  "private": false,
5
- "description": "The Nx Devkit is used to customize Nx for different technologies and use cases. It contains many utility functions for reading and writing files, updating configuration, working with Abstract Syntax Trees(ASTs), and more.",
5
+ "description": "The Nx Devkit is used to customize Nx for different technologies and use cases. It contains many utility functions for reading and writing files, updating configuration, working with Abstract Syntax Trees(ASTs), and more. Learn more about [extending Nx by leveraging the Nx Devkit](https://nx.dev/extending-nx/intro/getting-started) on our docs.",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/nrwl/nx.git",
@@ -33,8 +33,9 @@
33
33
  "ignore": "^5.0.4",
34
34
  "tmp": "~0.2.1",
35
35
  "tslib": "^2.3.0",
36
- "semver": "7.5.3",
37
- "@nrwl/devkit": "17.0.3"
36
+ "semver": "^7.5.3",
37
+ "yargs-parser": "21.1.1",
38
+ "@nrwl/devkit": "17.0.5"
38
39
  },
39
40
  "peerDependencies": {
40
41
  "nx": ">= 16 <= 18"
package/public-api.d.ts CHANGED
@@ -18,7 +18,7 @@ export { generateFiles } from './src/generators/generate-files';
18
18
  /**
19
19
  * @category Generators
20
20
  */
21
- export { toJS } from './src/generators/to-js';
21
+ export { toJS, ToJSOptions } from './src/generators/to-js';
22
22
  /**
23
23
  * @category Generators
24
24
  */
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.targetToTargetString = exports.parseTargetString = void 0;
4
4
  const nx_1 = require("../../nx");
5
5
  let { readCachedProjectGraph, splitTarget, splitByColons } = (0, nx_1.requireNx)();
6
- // TODO: Remove this in Nx 18 when Nx 16.7.0 is no longer supported
6
+ // TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
7
7
  splitTarget = splitTarget ?? require('nx/src/utils/split-target').splitTarget;
8
8
  splitByColons =
9
9
  splitByColons ?? ((s) => s.split(':'));
@@ -22,7 +22,8 @@ function parseTargetString(targetString, projectGraphOrCtx) {
22
22
  const [maybeProject] = splitByColons(targetString);
23
23
  if (!projectGraph.nodes[maybeProject] &&
24
24
  projectGraphOrCtx &&
25
- 'projectName' in projectGraphOrCtx) {
25
+ 'projectName' in projectGraphOrCtx &&
26
+ maybeProject !== projectGraphOrCtx.projectName) {
26
27
  targetString = `${projectGraphOrCtx.projectName}:${targetString}`;
27
28
  }
28
29
  const [project, target, configuration] = splitTarget(targetString, projectGraph);
@@ -4,7 +4,7 @@ exports.readTargetOptions = void 0;
4
4
  const nx_1 = require("../../nx");
5
5
  const path_1 = require("path");
6
6
  let { Workspaces, getExecutorInformation, calculateDefaultProjectName, combineOptionsForExecutor, } = (0, nx_1.requireNx)();
7
- // TODO: Remove this in Nx 18 when Nx 16.7.0 is no longer supported
7
+ // TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
8
8
  combineOptionsForExecutor =
9
9
  combineOptionsForExecutor ??
10
10
  require('nx/src/utils/params').combineOptionsForExecutor;
@@ -19,16 +19,19 @@ function readTargetOptions({ project, target, configuration }, context) {
19
19
  throw new Error(`Unable to find project ${project}`);
20
20
  }
21
21
  const targetConfiguration = projectConfiguration.targets[target];
22
- // TODO(v18): remove Workspaces.
22
+ if (!targetConfiguration) {
23
+ throw new Error(`Unable to find target ${target} for project ${project}`);
24
+ }
25
+ // TODO(v19): remove Workspaces.
23
26
  const ws = new Workspaces(context.root);
24
27
  const [nodeModule, executorName] = targetConfiguration.executor.split(':');
25
28
  const { schema } = getExecutorInformation
26
- ? getExecutorInformation(nodeModule, executorName, context.root)
27
- : // TODO(v18): remove readExecutor. This is to be backwards compatible with Nx 16.5 and below.
29
+ ? getExecutorInformation(nodeModule, executorName, context.root, context.projectsConfigurations?.projects ?? context.workspace.projects)
30
+ : // TODO(v19): remove readExecutor. This is to be backwards compatible with Nx 16.5 and below.
28
31
  ws.readExecutor(nodeModule, executorName);
29
32
  const defaultProject = calculateDefaultProjectName
30
33
  ? calculateDefaultProjectName(context.cwd, context.root, { version: 2, projects: context.projectsConfigurations.projects }, context.nxJsonConfiguration)
31
- : // TODO(v18): remove calculateDefaultProjectName. This is to be backwards compatible with Nx 16.5 and below.
34
+ : // TODO(v19): remove calculateDefaultProjectName. This is to be backwards compatible with Nx 16.5 and below.
32
35
  ws.calculateDefaultProjectName(context.cwd, { version: 2, projects: context.projectsConfigurations.projects }, context.nxJsonConfiguration);
33
36
  return combineOptionsForExecutor({}, configuration ?? targetConfiguration.defaultConfiguration ?? '', targetConfiguration, schema, defaultProject, (0, path_1.relative)(context.root, context.cwd));
34
37
  }
@@ -0,0 +1,2 @@
1
+ import type { Tree } from 'nx/src/devkit-exports';
2
+ export declare function addBuildTargetDefaults(tree: Tree, executorName: string, buildTargetName?: string): void;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addBuildTargetDefaults = void 0;
4
+ const nx_1 = require("../../nx");
5
+ const { readNxJson, updateNxJson } = (0, nx_1.requireNx)();
6
+ function addBuildTargetDefaults(tree, executorName, buildTargetName = 'build') {
7
+ const nxJson = readNxJson(tree);
8
+ nxJson.targetDefaults ??= {};
9
+ nxJson.targetDefaults[executorName] ??= {
10
+ cache: true,
11
+ dependsOn: [`^${buildTargetName}`],
12
+ inputs: nxJson.namedInputs && 'production' in nxJson.namedInputs
13
+ ? ['production', '^production']
14
+ : ['default', '^default'],
15
+ };
16
+ updateNxJson(tree, nxJson);
17
+ }
18
+ exports.addBuildTargetDefaults = addBuildTargetDefaults;
@@ -48,13 +48,13 @@ async function determineFormat(formats, options) {
48
48
  name: derivedSelectedValue,
49
49
  },
50
50
  ],
51
- initial: 'as-provided',
51
+ initial: 0,
52
52
  }).then(({ format }) => format === asProvidedSelectedValue ? 'as-provided' : 'derived');
53
53
  return result;
54
54
  }
55
55
  function logDeprecationMessage(options, formats) {
56
56
  logger.warn(`
57
- In Nx 18, generating a ${options.artifactType} will no longer support providing a project and deriving the directory.
57
+ In Nx 19, generating a ${options.artifactType} will no longer support providing a project and deriving the directory.
58
58
  Please provide the exact directory in the future.
59
59
  Example: nx g ${options.callingGenerator} ${formats['derived'].artifactName} --directory ${formats['derived'].directory}
60
60
  NOTE: The example above assumes the command is being run from the workspace root. If the command is being run from a subdirectory, the directory option should be adjusted accordingly.
@@ -116,9 +116,20 @@ function getNameAndDirectoryOptionFormats(tree, options) {
116
116
  }
117
117
  function getAsProvidedOptions(tree, options) {
118
118
  const relativeCwd = getRelativeCwd();
119
- const asProvidedDirectory = options.directory
120
- ? joinPathFragments(relativeCwd, options.directory)
121
- : relativeCwd;
119
+ let asProvidedDirectory;
120
+ if (options.directory) {
121
+ // append the directory to the current working directory if it doesn't start with it
122
+ if (options.directory === relativeCwd ||
123
+ options.directory.startsWith(`${relativeCwd}/`)) {
124
+ asProvidedDirectory = options.directory;
125
+ }
126
+ else {
127
+ asProvidedDirectory = joinPathFragments(relativeCwd, options.directory);
128
+ }
129
+ }
130
+ else {
131
+ asProvidedDirectory = relativeCwd;
132
+ }
122
133
  const asProvidedProject = findProjectFromPath(tree, asProvidedDirectory);
123
134
  const asProvidedFileName = options.fileName ??
124
135
  (options.suffix ? `${options.name}.${options.suffix}` : options.name);
@@ -4,7 +4,7 @@ exports.formatFiles = void 0;
4
4
  const path = require("path");
5
5
  const nx_1 = require("../../nx");
6
6
  let { updateJson, readJson, sortObjectByKeys } = (0, nx_1.requireNx)();
7
- // TODO: Remove this in Nx 18 when Nx 16.7.0 is no longer supported
7
+ // TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
8
8
  sortObjectByKeys =
9
9
  sortObjectByKeys ?? require('nx/src/utils/object-sort').sortObjectByKeys;
10
10
  /**
@@ -6,15 +6,18 @@ const path_1 = require("path");
6
6
  const nx_1 = require("../../nx");
7
7
  const get_workspace_layout_1 = require("../utils/get-workspace-layout");
8
8
  const names_1 = require("../utils/names");
9
- const { joinPathFragments, normalizePath, logger, readJson, stripIndents, workspaceRoot, } = (0, nx_1.requireNx)();
10
- const deprecationWarning = stripIndents `
11
- In Nx 18, generating projects will no longer derive the name and root.
12
- Please provide the exact project name and root in the future.`;
9
+ const { joinPathFragments, logger, normalizePath, output, readJson, stripIndents, workspaceRoot, } = (0, nx_1.requireNx)();
13
10
  async function determineProjectNameAndRootOptions(tree, options) {
11
+ if (!options.projectNameAndRootFormat &&
12
+ (process.env.NX_INTERACTIVE !== 'true' || !isTTY())) {
13
+ options.projectNameAndRootFormat = 'derived';
14
+ }
14
15
  validateName(options.name, options.projectNameAndRootFormat);
15
16
  const formats = getProjectNameAndRootFormats(tree, options);
16
- const format = options.projectNameAndRootFormat ??
17
- (await determineFormat(tree, formats, options.callingGenerator));
17
+ const format = options.projectNameAndRootFormat ?? (await determineFormat(formats));
18
+ if (format === 'derived' && options.callingGenerator) {
19
+ logDeprecationMessage(options.callingGenerator, formats);
20
+ }
18
21
  return {
19
22
  ...formats[format],
20
23
  projectNameAndRootFormat: format,
@@ -41,16 +44,17 @@ function validateName(name, projectNameAndRootFormat) {
41
44
  throw new Error(`The project name should match the pattern "${pattern}". The provided value "${name}" does not match.`);
42
45
  }
43
46
  }
44
- function getExample(callingGenerator, formats) {
45
- return `Example: nx g ${callingGenerator} ${formats['as-provided'].projectName} --directory ${formats['as-provided'].projectRoot}`;
47
+ function logDeprecationMessage(callingGenerator, formats) {
48
+ logger.warn(stripIndents `
49
+ In Nx 19, generating projects will no longer derive the name and root.
50
+ Please provide the exact project name and root in the future.
51
+ Example: nx g ${callingGenerator} ${formats['derived'].projectName} --directory ${formats['derived'].projectRoot}
52
+ `);
46
53
  }
47
- async function determineFormat(tree, formats, callingGenerator) {
54
+ async function determineFormat(formats) {
48
55
  if (!formats.derived) {
49
56
  return 'as-provided';
50
57
  }
51
- if (process.env.NX_INTERACTIVE !== 'true' || !isTTY()) {
52
- return 'derived';
53
- }
54
58
  const asProvidedDescription = `As provided:
55
59
  Name: ${formats['as-provided'].projectName}
56
60
  Root: ${formats['as-provided'].projectRoot}`;
@@ -76,115 +80,156 @@ async function determineFormat(tree, formats, callingGenerator) {
76
80
  name: derivedSelectedValue,
77
81
  },
78
82
  ],
79
- initial: 'as-provided',
83
+ initial: 0,
80
84
  }).then(({ format }) => format === asProvidedSelectedValue ? 'as-provided' : 'derived');
81
- if (result === 'derived' && callingGenerator) {
82
- const example = getExample(callingGenerator, formats);
83
- logger.warn(deprecationWarning + '\n' + example);
84
- }
85
85
  return result;
86
86
  }
87
87
  function getProjectNameAndRootFormats(tree, options) {
88
88
  const directory = options.directory
89
89
  ? normalizePath(options.directory.replace(/^\.?\//, ''))
90
90
  : undefined;
91
- const asProvidedProjectName = options.name;
92
- let asProvidedProjectDirectory;
93
- const relativeCwd = normalizePath((0, path_1.relative)(workspaceRoot, getCwd())).replace(/\/$/, '');
94
- if (directory) {
91
+ const { name: asProvidedParsedName, directory: asProvidedParsedDirectory } = parseNameForAsProvided(options.name);
92
+ if (asProvidedParsedDirectory && directory) {
93
+ throw new Error(`You can't specify both a directory (${options.directory}) and a name with a directory path (${options.name}). ` +
94
+ `Please specify either a directory or a name with a directory path.`);
95
+ }
96
+ const asProvidedOptions = getAsProvidedOptions(tree, {
97
+ ...options,
98
+ directory: directory ?? asProvidedParsedDirectory,
99
+ name: asProvidedParsedName,
100
+ });
101
+ if (options.projectNameAndRootFormat === 'as-provided') {
102
+ return {
103
+ 'as-provided': asProvidedOptions,
104
+ derived: undefined,
105
+ };
106
+ }
107
+ if (asProvidedOptions.projectName.startsWith('@')) {
108
+ if (!options.projectNameAndRootFormat) {
109
+ output.warn({
110
+ title: `The provided name "${options.name}" contains a scoped project name and this is not supported by the "${options.callingGenerator}" when using the "derived" format.`,
111
+ bodyLines: [
112
+ `The generator will try to generate the project "${asProvidedOptions.projectName}" using the "as-provided" format at "${asProvidedOptions.projectRoot}".`,
113
+ ],
114
+ });
115
+ return {
116
+ 'as-provided': asProvidedOptions,
117
+ derived: undefined,
118
+ };
119
+ }
120
+ throw new Error(`The provided name "${options.name}" contains a scoped project name and this is not supported by the "${options.callingGenerator}" when using the "derived" format. ` +
121
+ `Please provide a name without "@" or use the "as-provided" format.`);
122
+ }
123
+ const { name: derivedParsedName, directory: derivedParsedDirectory } = parseNameForDerived(options.name);
124
+ const derivedOptions = getDerivedOptions(tree, {
125
+ ...options,
126
+ directory: directory ?? derivedParsedDirectory,
127
+ name: derivedParsedName,
128
+ });
129
+ return {
130
+ 'as-provided': asProvidedOptions,
131
+ derived: derivedOptions,
132
+ };
133
+ }
134
+ function getAsProvidedOptions(tree, options) {
135
+ let projectSimpleName;
136
+ let projectFileName;
137
+ if (options.name.startsWith('@')) {
138
+ const [_scope, ...rest] = options.name.split('/');
139
+ projectFileName = rest.join('-');
140
+ projectSimpleName = rest.pop();
141
+ }
142
+ else {
143
+ projectSimpleName = options.name;
144
+ projectFileName = options.name;
145
+ }
146
+ let projectRoot;
147
+ const relativeCwd = getRelativeCwd();
148
+ if (options.directory) {
95
149
  // append the directory to the current working directory if it doesn't start with it
96
- if (directory === relativeCwd || directory.startsWith(`${relativeCwd}/`)) {
97
- asProvidedProjectDirectory = directory;
150
+ if (options.directory === relativeCwd ||
151
+ options.directory.startsWith(`${relativeCwd}/`)) {
152
+ projectRoot = options.directory;
98
153
  }
99
154
  else {
100
- asProvidedProjectDirectory = joinPathFragments(relativeCwd, directory);
155
+ projectRoot = joinPathFragments(relativeCwd, options.directory);
101
156
  }
102
157
  }
103
158
  else if (options.rootProject) {
104
- asProvidedProjectDirectory = '.';
159
+ projectRoot = '.';
105
160
  }
106
161
  else {
107
- asProvidedProjectDirectory = relativeCwd;
162
+ projectRoot = relativeCwd;
108
163
  // append the project name to the current working directory if it doesn't end with it
109
- if (!relativeCwd.endsWith(asProvidedProjectName) &&
110
- !relativeCwd.endsWith(options.name)) {
111
- asProvidedProjectDirectory = joinPathFragments(relativeCwd, asProvidedProjectName);
164
+ if (!relativeCwd.endsWith(options.name)) {
165
+ projectRoot = joinPathFragments(relativeCwd, options.name);
112
166
  }
113
167
  }
114
- if (asProvidedProjectName.startsWith('@')) {
115
- const nameWithoutScope = asProvidedProjectName.split('/')[1];
116
- return {
117
- 'as-provided': {
118
- projectName: asProvidedProjectName,
119
- names: {
120
- projectSimpleName: nameWithoutScope,
121
- projectFileName: nameWithoutScope,
122
- },
123
- importPath: options.importPath ?? asProvidedProjectName,
124
- projectRoot: asProvidedProjectDirectory,
125
- },
126
- };
127
- }
128
- let asProvidedImportPath;
129
- let npmScope;
168
+ let importPath = undefined;
130
169
  if (options.projectType === 'library') {
131
- asProvidedImportPath = options.importPath;
132
- if (!asProvidedImportPath) {
133
- npmScope = getNpmScope(tree);
134
- asProvidedImportPath =
135
- asProvidedProjectDirectory === '.'
136
- ? readJson(tree, 'package.json').name ??
137
- getImportPath(npmScope, asProvidedProjectName)
138
- : getImportPath(npmScope, asProvidedProjectName);
170
+ importPath = options.importPath;
171
+ if (!importPath) {
172
+ if (options.name.startsWith('@')) {
173
+ importPath = options.name;
174
+ }
175
+ else {
176
+ const npmScope = getNpmScope(tree);
177
+ importPath =
178
+ projectRoot === '.'
179
+ ? readJson(tree, 'package.json').name ??
180
+ getImportPath(npmScope, options.name)
181
+ : getImportPath(npmScope, options.name);
182
+ }
139
183
  }
140
184
  }
185
+ return {
186
+ projectName: options.name,
187
+ names: {
188
+ projectSimpleName,
189
+ projectFileName,
190
+ },
191
+ importPath,
192
+ projectRoot,
193
+ };
194
+ }
195
+ function getDerivedOptions(tree, options) {
141
196
  const name = (0, names_1.names)(options.name).fileName;
142
- let { projectDirectory, layoutDirectory } = getDirectories(tree, directory, options.projectType);
143
- const derivedProjectDirectoryWithoutLayout = projectDirectory
197
+ let { projectDirectory, layoutDirectory } = getDirectories(tree, options.directory, options.projectType);
198
+ const projectDirectoryWithoutLayout = projectDirectory
144
199
  ? `${(0, names_1.names)(projectDirectory).fileName}/${name}`
145
200
  : options.rootProject
146
201
  ? '.'
147
202
  : name;
148
203
  // the project name uses the directory without the layout directory
149
- const derivedProjectName = derivedProjectDirectoryWithoutLayout === '.'
204
+ const projectName = projectDirectoryWithoutLayout === '.'
150
205
  ? name
151
- : derivedProjectDirectoryWithoutLayout.replace(/\//g, '-');
152
- const derivedSimpleProjectName = name;
153
- let derivedProjectDirectory = derivedProjectDirectoryWithoutLayout;
154
- if (derivedProjectDirectoryWithoutLayout !== '.') {
206
+ : projectDirectoryWithoutLayout.replace(/\//g, '-');
207
+ const projectSimpleName = name;
208
+ let projectRoot = projectDirectoryWithoutLayout;
209
+ if (projectDirectoryWithoutLayout !== '.') {
155
210
  // prepend the layout directory
156
- derivedProjectDirectory = joinPathFragments(layoutDirectory, derivedProjectDirectory);
211
+ projectRoot = joinPathFragments(layoutDirectory, projectRoot);
157
212
  }
158
- let derivedImportPath;
213
+ let importPath;
159
214
  if (options.projectType === 'library') {
160
- derivedImportPath = options.importPath;
161
- if (!derivedImportPath) {
162
- derivedImportPath =
163
- derivedProjectDirectory === '.'
215
+ importPath = options.importPath;
216
+ if (!importPath) {
217
+ const npmScope = getNpmScope(tree);
218
+ importPath =
219
+ projectRoot === '.'
164
220
  ? readJson(tree, 'package.json').name ??
165
- getImportPath(npmScope, derivedProjectName)
166
- : getImportPath(npmScope, derivedProjectDirectoryWithoutLayout);
221
+ getImportPath(npmScope, projectName)
222
+ : getImportPath(npmScope, projectDirectoryWithoutLayout);
167
223
  }
168
224
  }
169
225
  return {
170
- 'as-provided': {
171
- projectName: asProvidedProjectName,
172
- names: {
173
- projectSimpleName: asProvidedProjectName,
174
- projectFileName: asProvidedProjectName,
175
- },
176
- importPath: asProvidedImportPath,
177
- projectRoot: asProvidedProjectDirectory,
178
- },
179
- derived: {
180
- projectName: derivedProjectName,
181
- names: {
182
- projectSimpleName: derivedSimpleProjectName,
183
- projectFileName: derivedProjectName,
184
- },
185
- importPath: derivedImportPath,
186
- projectRoot: derivedProjectDirectory,
226
+ projectName,
227
+ names: {
228
+ projectSimpleName,
229
+ projectFileName: projectName,
187
230
  },
231
+ importPath,
232
+ projectRoot,
188
233
  };
189
234
  }
190
235
  function getDirectories(tree, directory, projectType) {
@@ -218,6 +263,9 @@ function getCwd() {
218
263
  ? process.env.INIT_CWD
219
264
  : process.cwd();
220
265
  }
266
+ function getRelativeCwd() {
267
+ return normalizePath((0, path_1.relative)(workspaceRoot, getCwd())).replace(/\/$/, '');
268
+ }
221
269
  /**
222
270
  * Function for setting cwd during testing
223
271
  */
@@ -225,3 +273,26 @@ function setCwd(path) {
225
273
  process.env.INIT_CWD = (0, path_1.join)(workspaceRoot, path);
226
274
  }
227
275
  exports.setCwd = setCwd;
276
+ function parseNameForAsProvided(rawName) {
277
+ const directory = normalizePath(rawName);
278
+ if (rawName.includes('@')) {
279
+ const index = directory.lastIndexOf('@');
280
+ if (index === 0) {
281
+ return { name: rawName, directory: undefined };
282
+ }
283
+ const name = directory.substring(index);
284
+ return { name, directory };
285
+ }
286
+ if (rawName.includes('/')) {
287
+ const index = directory.lastIndexOf('/');
288
+ const name = directory.substring(index + 1);
289
+ return { name, directory };
290
+ }
291
+ return { name: rawName, directory: undefined };
292
+ }
293
+ function parseNameForDerived(rawName) {
294
+ const parsedName = normalizePath(rawName).split('/');
295
+ const name = parsedName.pop();
296
+ const directory = parsedName.length ? parsedName.join('/') : undefined;
297
+ return { name, directory };
298
+ }
@@ -1,5 +1,11 @@
1
1
  import type { Tree } from 'nx/src/generators/tree';
2
+ import type { ScriptTarget, ModuleKind } from 'typescript';
3
+ export type ToJSOptions = {
4
+ target?: ScriptTarget;
5
+ module?: ModuleKind;
6
+ extension: '.js' | '.mjs' | '.cjs';
7
+ };
2
8
  /**
3
9
  * Rename and transpile any new typescript files created to javascript files
4
10
  */
5
- export declare function toJS(tree: Tree): void;
11
+ export declare function toJS(tree: Tree, options?: ToJSOptions): void;
@@ -6,17 +6,18 @@ const package_json_1 = require("../utils/package-json");
6
6
  /**
7
7
  * Rename and transpile any new typescript files created to javascript files
8
8
  */
9
- function toJS(tree) {
10
- const { JsxEmit, ScriptTarget, transpile } = (0, package_json_1.ensurePackage)('typescript', versions_1.typescriptVersion);
9
+ function toJS(tree, options) {
10
+ const { JsxEmit, ScriptTarget, transpile, ModuleKind } = (0, package_json_1.ensurePackage)('typescript', versions_1.typescriptVersion);
11
11
  for (const c of tree.listChanges()) {
12
12
  if ((c.path.endsWith('.ts') || c.path.endsWith('tsx')) &&
13
13
  c.type === 'CREATE') {
14
14
  tree.write(c.path, transpile(c.content.toString('utf-8'), {
15
15
  allowJs: true,
16
16
  jsx: JsxEmit.Preserve,
17
- target: ScriptTarget.ESNext,
17
+ target: options?.target ?? ScriptTarget.ESNext,
18
+ module: options?.module ?? ModuleKind.ESNext,
18
19
  }));
19
- tree.rename(c.path, c.path.replace(/\.tsx?$/, '.js'));
20
+ tree.rename(c.path, c.path.replace(/\.tsx?$/, options?.extension ?? '.js'));
20
21
  }
21
22
  }
22
23
  }
@@ -0,0 +1,2 @@
1
+ import type { CreateNodesContext } from 'nx/src/devkit-exports';
2
+ export declare function calculateHashForCreateNodes(projectRoot: string, options: object, context: CreateNodesContext, additionalGlobs?: string[]): string;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateHashForCreateNodes = void 0;
4
+ const nx_1 = require("../../nx");
5
+ const path_1 = require("path");
6
+ const { hashWithWorkspaceContext, hashArray, hashObject } = (0, nx_1.requireNx)();
7
+ function calculateHashForCreateNodes(projectRoot, options, context, additionalGlobs = []) {
8
+ return hashArray([
9
+ hashWithWorkspaceContext(context.workspaceRoot, [
10
+ (0, path_1.join)(projectRoot, '**/*'),
11
+ ...additionalGlobs,
12
+ ]),
13
+ hashObject(options),
14
+ ]);
15
+ }
16
+ exports.calculateHashForCreateNodes = calculateHashForCreateNodes;
@@ -0,0 +1,4 @@
1
+ export declare let dynamicImport: Function;
2
+ export declare function loadConfigFile<T extends object = any>(configFilePath: string): Promise<T>;
3
+ export declare function getRootTsConfigPath(): string | null;
4
+ export declare function getRootTsConfigFileName(): string | null;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRootTsConfigFileName = exports.getRootTsConfigPath = exports.loadConfigFile = exports.dynamicImport = void 0;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ const nx_1 = require("../../nx");
7
+ const { workspaceRoot, registerTsProject } = (0, nx_1.requireNx)();
8
+ exports.dynamicImport = new Function('modulePath', 'return import(modulePath);');
9
+ async function loadConfigFile(configFilePath) {
10
+ {
11
+ let module;
12
+ if ((0, path_1.extname)(configFilePath) === '.ts') {
13
+ const siblingFiles = (0, fs_1.readdirSync)((0, path_1.dirname)(configFilePath));
14
+ const tsConfigPath = siblingFiles.includes('tsconfig.json')
15
+ ? (0, path_1.join)((0, path_1.dirname)(configFilePath), 'tsconfig.json')
16
+ : getRootTsConfigPath();
17
+ if (tsConfigPath) {
18
+ const unregisterTsProject = registerTsProject(tsConfigPath);
19
+ try {
20
+ module = await load(configFilePath);
21
+ }
22
+ finally {
23
+ unregisterTsProject();
24
+ }
25
+ }
26
+ else {
27
+ module = await load(configFilePath);
28
+ }
29
+ }
30
+ else {
31
+ module = await load(configFilePath);
32
+ }
33
+ return module.default ?? module;
34
+ }
35
+ }
36
+ exports.loadConfigFile = loadConfigFile;
37
+ function getRootTsConfigPath() {
38
+ const tsConfigFileName = getRootTsConfigFileName();
39
+ return tsConfigFileName ? (0, path_1.join)(workspaceRoot, tsConfigFileName) : null;
40
+ }
41
+ exports.getRootTsConfigPath = getRootTsConfigPath;
42
+ function getRootTsConfigFileName() {
43
+ for (const tsConfigName of ['tsconfig.base.json', 'tsconfig.json']) {
44
+ const pathExists = (0, fs_1.existsSync)((0, path_1.join)(workspaceRoot, tsConfigName));
45
+ if (pathExists) {
46
+ return tsConfigName;
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+ exports.getRootTsConfigFileName = getRootTsConfigFileName;
52
+ /**
53
+ * Load the module after ensuring that the require cache is cleared.
54
+ */
55
+ async function load(path) {
56
+ // Clear cache if the path is in the cache
57
+ if (require.cache[path]) {
58
+ for (const k of Object.keys(require.cache)) {
59
+ delete require.cache[k];
60
+ }
61
+ }
62
+ try {
63
+ // Try using `require` first, which works for CJS modules.
64
+ // Modules are CJS unless it is named `.mjs` or `package.json` sets type to "module".
65
+ return require(path);
66
+ }
67
+ catch (e) {
68
+ if (e.code === 'ERR_REQUIRE_ESM') {
69
+ // If `require` fails to load ESM, try dynamic `import()`.
70
+ return await (0, exports.dynamicImport)(`${path}?t=${Date.now()}`);
71
+ }
72
+ // Re-throw all other errors
73
+ throw e;
74
+ }
75
+ }
@@ -14,15 +14,15 @@ function convertNxExecutor(executor) {
14
14
  const workspaces = new Workspaces(builderContext.workspaceRoot);
15
15
  const nxJsonConfiguration = readNxJsonFromDisk
16
16
  ? readNxJsonFromDisk(builderContext.workspaceRoot)
17
- : // TODO(v18): remove readNxJson. This is to be backwards compatible with Nx 16.5 and below.
17
+ : // TODO(v19): remove readNxJson. This is to be backwards compatible with Nx 16.5 and below.
18
18
  workspaces.readNxJson();
19
19
  const promise = async () => {
20
20
  const projectsConfigurations = retrieveProjectConfigurationsWithAngularProjects
21
21
  ? {
22
22
  version: 2,
23
- projects: await retrieveProjectConfigurationsWithAngularProjects(builderContext.workspaceRoot, nxJsonConfiguration).then((p) => p.projectNodes),
23
+ projects: await retrieveProjectConfigurationsWithAngularProjects(builderContext.workspaceRoot, nxJsonConfiguration).then((p) => p.projectNodes ?? p.projects),
24
24
  }
25
- : // TODO(v18): remove retrieveProjectConfigurations. This is to be backwards compatible with Nx 16.5 and below.
25
+ : // TODO(v19): remove retrieveProjectConfigurations. This is to be backwards compatible with Nx 16.5 and below.
26
26
  workspaces.readProjectsConfigurations({
27
27
  _includeProjectsFromAngularJson: true,
28
28
  });
@@ -0,0 +1,8 @@
1
+ import type { CreateNodesContext } from 'nx/src/devkit-exports';
2
+ import type { InputDefinition } from 'nx/src/config/workspace-json-project-json';
3
+ /**
4
+ * Get the named inputs available for a directory
5
+ */
6
+ export declare function getNamedInputs(directory: string, context: CreateNodesContext): {
7
+ [inputName: string]: (string | InputDefinition)[];
8
+ };
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getNamedInputs = void 0;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ const nx_1 = require("../../nx");
7
+ const { readJsonFile } = (0, nx_1.requireNx)();
8
+ /**
9
+ * Get the named inputs available for a directory
10
+ */
11
+ function getNamedInputs(directory, context) {
12
+ const projectJsonPath = (0, path_1.join)(directory, 'project.json');
13
+ const projectJson = (0, fs_1.existsSync)(projectJsonPath)
14
+ ? readJsonFile(projectJsonPath)
15
+ : null;
16
+ const packageJsonPath = (0, path_1.join)(directory, 'package.json');
17
+ const packageJson = (0, fs_1.existsSync)(packageJsonPath)
18
+ ? readJsonFile(packageJsonPath)
19
+ : null;
20
+ return {
21
+ ...context.nxJsonConfiguration.namedInputs,
22
+ ...packageJson?.nx?.namedInputs,
23
+ ...projectJson?.namedInputs,
24
+ };
25
+ }
26
+ exports.getNamedInputs = getNamedInputs;
@@ -18,7 +18,7 @@ export declare function getWorkspaceLayout(tree: Tree): {
18
18
  /**
19
19
  * Experimental
20
20
  */
21
- export declare function extractLayoutDirectory(directory: string): {
22
- layoutDirectory: string;
23
- projectDirectory: string;
21
+ export declare function extractLayoutDirectory(directory?: string): {
22
+ layoutDirectory: string | null;
23
+ projectDirectory?: string;
24
24
  };
@@ -4,7 +4,7 @@ exports.convertNxGenerator = void 0;
4
4
  const path_1 = require("path");
5
5
  const nx_1 = require("../../nx");
6
6
  let { logger, stripIndent } = (0, nx_1.requireNx)();
7
- // TODO: Remove this in Nx 18 when Nx 16.7.0 is no longer supported
7
+ // TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
8
8
  stripIndent = stripIndent ?? require('nx/src/utils/logger').stripIndent;
9
9
  class RunCallbackTask {
10
10
  constructor(callback) {
@@ -0,0 +1 @@
1
+ export declare function logShowProjectCommand(projectName: string): void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logShowProjectCommand = void 0;
4
+ const nx_1 = require("../../nx");
5
+ function logShowProjectCommand(projectName) {
6
+ const { output } = (0, nx_1.requireNx)();
7
+ output.log({
8
+ title: `👀 View Details of ${projectName}`,
9
+ bodyLines: [
10
+ `Run "nx show project ${projectName} --web" to view details about this project.`,
11
+ ],
12
+ });
13
+ }
14
+ exports.logShowProjectCommand = logShowProjectCommand;
@@ -13,9 +13,10 @@ import type { GeneratorCallback } from 'nx/src/config/misc-interfaces';
13
13
  * @param dependencies Dependencies to be added to the dependencies section of package.json
14
14
  * @param devDependencies Dependencies to be added to the devDependencies section of package.json
15
15
  * @param packageJsonPath Path to package.json
16
+ * @param keepExistingVersions If true, prevents existing dependencies from being bumped to newer versions
16
17
  * @returns Callback to install dependencies only if necessary, no-op otherwise
17
18
  */
18
- export declare function addDependenciesToPackageJson(tree: Tree, dependencies: Record<string, string>, devDependencies: Record<string, string>, packageJsonPath?: string): GeneratorCallback;
19
+ export declare function addDependenciesToPackageJson(tree: Tree, dependencies: Record<string, string>, devDependencies: Record<string, string>, packageJsonPath?: string, keepExistingVersions?: boolean): GeneratorCallback;
19
20
  /**
20
21
  * Remove Dependencies and Dev Dependencies from package.json
21
22
  *
@@ -86,9 +86,10 @@ function updateExistingDependenciesVersion(dependencies, existingDependencies =
86
86
  * @param dependencies Dependencies to be added to the dependencies section of package.json
87
87
  * @param devDependencies Dependencies to be added to the devDependencies section of package.json
88
88
  * @param packageJsonPath Path to package.json
89
+ * @param keepExistingVersions If true, prevents existing dependencies from being bumped to newer versions
89
90
  * @returns Callback to install dependencies only if necessary, no-op otherwise
90
91
  */
91
- function addDependenciesToPackageJson(tree, dependencies, devDependencies, packageJsonPath = 'package.json') {
92
+ function addDependenciesToPackageJson(tree, dependencies, devDependencies, packageJsonPath = 'package.json', keepExistingVersions) {
92
93
  const currentPackageJson = readJson(tree, packageJsonPath);
93
94
  /** Dependencies to install that are not met in dev dependencies */
94
95
  let filteredDependencies = filterExistingDependencies(dependencies, currentPackageJson.devDependencies);
@@ -96,6 +97,7 @@ function addDependenciesToPackageJson(tree, dependencies, devDependencies, packa
96
97
  let filteredDevDependencies = filterExistingDependencies(devDependencies, currentPackageJson.dependencies);
97
98
  // filtered dependencies should consist of:
98
99
  // - dependencies of the same type that are not present
100
+ // by default, filtered dependencies also include these (unless keepExistingVersions is true):
99
101
  // - dependencies of the same type that have greater version
100
102
  // - specified dependencies of the other type that have greater version and are already installed as current type
101
103
  filteredDependencies = {
@@ -106,8 +108,14 @@ function addDependenciesToPackageJson(tree, dependencies, devDependencies, packa
106
108
  ...updateExistingDependenciesVersion(filteredDevDependencies, currentPackageJson.devDependencies),
107
109
  ...updateExistingAltDependenciesVersion(dependencies, currentPackageJson.devDependencies),
108
110
  };
109
- filteredDependencies = removeLowerVersions(filteredDependencies, currentPackageJson.dependencies);
110
- filteredDevDependencies = removeLowerVersions(filteredDevDependencies, currentPackageJson.devDependencies);
111
+ if (keepExistingVersions) {
112
+ filteredDependencies = removeExistingDependencies(filteredDependencies, currentPackageJson.dependencies);
113
+ filteredDevDependencies = removeExistingDependencies(filteredDevDependencies, currentPackageJson.devDependencies);
114
+ }
115
+ else {
116
+ filteredDependencies = removeLowerVersions(filteredDependencies, currentPackageJson.dependencies);
117
+ filteredDevDependencies = removeLowerVersions(filteredDevDependencies, currentPackageJson.devDependencies);
118
+ }
111
119
  if (requiresAddingOfPackages(currentPackageJson, filteredDependencies, filteredDevDependencies)) {
112
120
  updateJson(tree, packageJsonPath, (json) => {
113
121
  json.dependencies = {
@@ -134,11 +142,19 @@ exports.addDependenciesToPackageJson = addDependenciesToPackageJson;
134
142
  **/
135
143
  function removeLowerVersions(incomingDeps, existingDeps) {
136
144
  return Object.keys(incomingDeps).reduce((acc, d) => {
137
- if (existingDeps?.[d] &&
138
- !isIncomingVersionGreater(incomingDeps[d], existingDeps[d])) {
139
- return acc;
145
+ if (!existingDeps?.[d] ||
146
+ isIncomingVersionGreater(incomingDeps[d], existingDeps[d])) {
147
+ acc[d] = incomingDeps[d];
148
+ }
149
+ return acc;
150
+ }, {});
151
+ }
152
+ function removeExistingDependencies(incomingDeps, existingDeps) {
153
+ return Object.keys(incomingDeps).reduce((acc, d) => {
154
+ if (!existingDeps?.[d]) {
155
+ acc[d] = incomingDeps[d];
140
156
  }
141
- return { ...acc, [d]: incomingDeps[d] };
157
+ return acc;
142
158
  }, {});
143
159
  }
144
160
  /**
@@ -273,7 +289,7 @@ function ensurePackage(pkgOrTree, requiredVersionOrPackage, maybeRequiredVersion
273
289
  }
274
290
  }
275
291
  if (process.env.NX_DRY_RUN && process.env.NX_DRY_RUN !== 'false') {
276
- throw new Error('NOTE: This generator does not support --dry-run. If you are running this in Nx Console, it should execute fine once you hit the "Run" button.\n');
292
+ throw new Error('NOTE: This generator does not support --dry-run. If you are running this in Nx Console, it should execute fine once you hit the "Generate" button.\n');
277
293
  }
278
294
  const { dir: tempDir } = createTempNpmDirectory?.() ?? {
279
295
  dir: (0, tmp_1.dirSync)().name,
@@ -0,0 +1,2 @@
1
+ import type { Tree, CreateNodes } from 'nx/src/devkit-exports';
2
+ export declare function replaceProjectConfigurationsWithPlugin<T = unknown>(tree: Tree, rootMappings: Map<string, string>, pluginPath: string, createNodes: CreateNodes<T>, pluginOptions: T): Promise<void>;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replaceProjectConfigurationsWithPlugin = void 0;
4
+ const nx_1 = require("../../nx");
5
+ const { readNxJson, updateNxJson, glob, hashObject, findProjectForPath, readProjectConfiguration, updateProjectConfiguration, } = (0, nx_1.requireNx)();
6
+ async function replaceProjectConfigurationsWithPlugin(tree, rootMappings, pluginPath, createNodes, pluginOptions) {
7
+ const nxJson = readNxJson(tree);
8
+ const hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string' ? p === pluginPath : p.plugin === pluginPath);
9
+ if (hasPlugin) {
10
+ return;
11
+ }
12
+ nxJson.plugins ??= [];
13
+ nxJson.plugins.push({
14
+ plugin: pluginPath,
15
+ options: pluginOptions,
16
+ });
17
+ updateNxJson(tree, nxJson);
18
+ const [pluginGlob, createNodesFunction] = createNodes;
19
+ const configFiles = glob(tree, [pluginGlob]);
20
+ for (const configFile of configFiles) {
21
+ try {
22
+ const projectName = findProjectForPath(configFile, rootMappings);
23
+ const projectConfig = readProjectConfiguration(tree, projectName);
24
+ const nodes = await createNodesFunction(configFile, pluginOptions, {
25
+ workspaceRoot: tree.root,
26
+ nxJsonConfiguration: readNxJson(tree),
27
+ configFiles,
28
+ });
29
+ const node = nodes.projects[Object.keys(nodes.projects)[0]];
30
+ for (const [targetName, targetConfig] of Object.entries(node.targets)) {
31
+ const targetFromProjectConfig = projectConfig.targets[targetName];
32
+ if (targetFromProjectConfig?.executor !== targetConfig.executor) {
33
+ continue;
34
+ }
35
+ const targetFromCreateNodes = node.targets[targetName];
36
+ removeConfigurationDefinedByPlugin(targetName, targetFromProjectConfig, targetFromCreateNodes, projectConfig);
37
+ }
38
+ updateProjectConfiguration(tree, projectName, projectConfig);
39
+ }
40
+ catch (e) {
41
+ console.error(e);
42
+ }
43
+ }
44
+ }
45
+ exports.replaceProjectConfigurationsWithPlugin = replaceProjectConfigurationsWithPlugin;
46
+ function removeConfigurationDefinedByPlugin(targetName, targetFromProjectConfig, targetFromCreateNodes, projectConfig) {
47
+ // Executor
48
+ delete targetFromProjectConfig.executor;
49
+ // Default Configuration
50
+ if (targetFromProjectConfig.defaultConfiguration ===
51
+ targetFromCreateNodes.defaultConfiguration) {
52
+ delete targetFromProjectConfig.defaultConfiguration;
53
+ }
54
+ // Cache
55
+ if (targetFromProjectConfig.cache === targetFromCreateNodes.cache) {
56
+ delete targetFromProjectConfig.cache;
57
+ }
58
+ // Depends On
59
+ if (targetFromProjectConfig.dependsOn &&
60
+ shouldRemoveArrayProperty(targetFromProjectConfig.dependsOn, targetFromCreateNodes.dependsOn)) {
61
+ delete targetFromProjectConfig.dependsOn;
62
+ }
63
+ // Outputs
64
+ if (targetFromProjectConfig.outputs &&
65
+ shouldRemoveArrayProperty(targetFromProjectConfig.outputs, targetFromCreateNodes.outputs)) {
66
+ delete targetFromProjectConfig.outputs;
67
+ }
68
+ // Inputs
69
+ if (targetFromProjectConfig.inputs &&
70
+ shouldRemoveArrayProperty(targetFromProjectConfig.inputs, targetFromCreateNodes.inputs)) {
71
+ delete targetFromProjectConfig.inputs;
72
+ }
73
+ // Options
74
+ for (const [optionName, optionValue] of Object.entries(targetFromProjectConfig.options ?? {})) {
75
+ if (equals(targetFromCreateNodes.options[optionName], optionValue)) {
76
+ delete targetFromProjectConfig.options[optionName];
77
+ }
78
+ }
79
+ if (Object.keys(targetFromProjectConfig.options).length === 0) {
80
+ delete targetFromProjectConfig.options;
81
+ }
82
+ // Configurations
83
+ for (const [configName, configOptions] of Object.entries(targetFromProjectConfig.configurations ?? {})) {
84
+ for (const [optionName, optionValue] of Object.entries(configOptions)) {
85
+ if (targetFromCreateNodes.configurations?.[configName]?.[optionName] ===
86
+ optionValue) {
87
+ delete targetFromProjectConfig.configurations[configName][optionName];
88
+ }
89
+ }
90
+ if (Object.keys(configOptions).length === 0) {
91
+ delete targetFromProjectConfig.configurations[configName];
92
+ }
93
+ }
94
+ if (Object.keys(targetFromProjectConfig.configurations ?? {}).length === 0) {
95
+ delete targetFromProjectConfig.configurations;
96
+ }
97
+ if (Object.keys(targetFromProjectConfig).length === 0) {
98
+ delete projectConfig.targets[targetName];
99
+ }
100
+ }
101
+ function equals(a, b) {
102
+ if (Array.isArray(a) && Array.isArray(b)) {
103
+ return a.length === b.length && a.every((v, i) => v === b[i]);
104
+ }
105
+ if (typeof a === 'object' && typeof b === 'object') {
106
+ return hashObject(a) === hashObject(b);
107
+ }
108
+ return a === b;
109
+ }
110
+ function shouldRemoveArrayProperty(arrayValuesFromProjectConfiguration, arrayValuesFromCreateNodes) {
111
+ const setOfArrayValuesFromProjectConfiguration = new Set(arrayValuesFromProjectConfiguration);
112
+ loopThroughArrayValuesFromCreateNodes: for (const arrayValueFromCreateNodes of arrayValuesFromCreateNodes) {
113
+ if (typeof arrayValueFromCreateNodes === 'string') {
114
+ if (!setOfArrayValuesFromProjectConfiguration.has(arrayValueFromCreateNodes)) {
115
+ // If the inputs from the project configuration is missing an input from createNodes it was removed
116
+ return false;
117
+ }
118
+ else {
119
+ setOfArrayValuesFromProjectConfiguration.delete(arrayValueFromCreateNodes);
120
+ }
121
+ }
122
+ else {
123
+ for (const arrayValue of setOfArrayValuesFromProjectConfiguration.values()) {
124
+ if (typeof arrayValue !== 'string' &&
125
+ hashObject(arrayValue) === hashObject(arrayValueFromCreateNodes)) {
126
+ setOfArrayValuesFromProjectConfiguration.delete(arrayValue);
127
+ // Continue the outer loop, breaking out of this loop
128
+ continue loopThroughArrayValuesFromCreateNodes;
129
+ }
130
+ }
131
+ // If an input was not matched, that means the input was removed
132
+ return false;
133
+ }
134
+ }
135
+ // If there are still inputs in the project configuration, they have added additional inputs
136
+ return setOfArrayValuesFromProjectConfiguration.size === 0;
137
+ }
@@ -0,0 +1,2 @@
1
+ import type { CreateNodes, Tree } from 'nx/src/devkit-exports';
2
+ export declare function updatePackageScripts(tree: Tree, createNodesTuple: CreateNodes): Promise<void>;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updatePackageScripts = void 0;
4
+ const path_1 = require("path");
5
+ const yargs = require("yargs-parser");
6
+ const nx_1 = require("../../nx");
7
+ const { glob, readJson, readNxJson, workspaceRoot, writeJson } = (0, nx_1.requireNx)();
8
+ async function updatePackageScripts(tree, createNodesTuple) {
9
+ const nxJson = readNxJson(tree);
10
+ const [pattern, createNodes] = createNodesTuple;
11
+ const matchingFiles = glob(tree, [pattern]);
12
+ for (const file of matchingFiles) {
13
+ const projectRoot = getProjectRootFromConfigFile(file);
14
+ await processProject(tree, projectRoot, file, createNodes, nxJson, matchingFiles);
15
+ }
16
+ }
17
+ exports.updatePackageScripts = updatePackageScripts;
18
+ async function processProject(tree, projectRoot, projectConfigurationFile, createNodesFunction, nxJsonConfiguration, configFiles) {
19
+ const packageJsonPath = `${projectRoot}/package.json`;
20
+ if (!tree.exists(packageJsonPath)) {
21
+ return;
22
+ }
23
+ const packageJson = readJson(tree, packageJsonPath);
24
+ if (!packageJson.scripts || !Object.keys(packageJson.scripts).length) {
25
+ return;
26
+ }
27
+ const result = await createNodesFunction(projectConfigurationFile, {}, {
28
+ nxJsonConfiguration,
29
+ workspaceRoot,
30
+ configFiles,
31
+ });
32
+ const targetCommands = getInferredTargetCommands(result);
33
+ if (!targetCommands.length) {
34
+ return;
35
+ }
36
+ const replacedTargets = new Set();
37
+ for (const targetCommand of targetCommands) {
38
+ const { command, target, configuration } = targetCommand;
39
+ const targetCommandRegex = new RegExp(`(?<=^|&)((?: )*(?:[^&\\r\\n\\s]+ )*)(${command})((?: [^&\\r\\n\\s]+)*(?: )*)(?=$|&)`, 'g');
40
+ for (const scriptName of Object.keys(packageJson.scripts)) {
41
+ const script = packageJson.scripts[scriptName];
42
+ // quick check for exact match within the script
43
+ if (targetCommandRegex.test(script)) {
44
+ packageJson.scripts[scriptName] = script.replace(targetCommandRegex, configuration
45
+ ? `$1nx ${target} --configuration=${configuration}$3`
46
+ : `$1nx ${target}$3`);
47
+ replacedTargets.add(target);
48
+ }
49
+ else {
50
+ /**
51
+ * Parse script and command to handle the following:
52
+ * - if command doesn't match script => don't replace
53
+ * - if command has more args => don't replace
54
+ * - if command has same args, regardless of order => replace removing args
55
+ * - if command has less args or with different value => replace leaving args
56
+ */
57
+ const parsedCommand = yargs(command, {
58
+ configuration: { 'strip-dashed': true },
59
+ });
60
+ // this assumes there are no positional args in the command, everything is a command or subcommand
61
+ const commandCommand = parsedCommand._.join(' ');
62
+ const commandRegex = new RegExp(`(?<=^|&)((?: )*(?:[^&\\r\\n\\s]+ )*)(${commandCommand})((?: [^&\\r\\n\\s]+)*( )*)(?=$|&)`, 'g');
63
+ const matches = script.match(commandRegex);
64
+ if (!matches) {
65
+ // the command doesn't match the script, don't replace
66
+ continue;
67
+ }
68
+ for (const match of matches) {
69
+ // parse the matched command within the script
70
+ const parsedScript = yargs(match, {
71
+ configuration: { 'strip-dashed': true },
72
+ });
73
+ let hasArgsWithDifferentValues = false;
74
+ let scriptHasExtraArgs = false;
75
+ let commandHasExtraArgs = false;
76
+ for (const [key, value] of Object.entries(parsedCommand)) {
77
+ if (key === '_') {
78
+ continue;
79
+ }
80
+ if (parsedScript[key] === undefined) {
81
+ commandHasExtraArgs = true;
82
+ break;
83
+ }
84
+ if (parsedScript[key] !== value) {
85
+ hasArgsWithDifferentValues = true;
86
+ }
87
+ }
88
+ if (commandHasExtraArgs) {
89
+ // the command has extra args, don't replace
90
+ continue;
91
+ }
92
+ for (const key of Object.keys(parsedScript)) {
93
+ if (key === '_') {
94
+ continue;
95
+ }
96
+ if (!parsedCommand[key]) {
97
+ scriptHasExtraArgs = true;
98
+ break;
99
+ }
100
+ }
101
+ if (!hasArgsWithDifferentValues && !scriptHasExtraArgs) {
102
+ // they are the same, replace with the command removing the args
103
+ packageJson.scripts[scriptName] = packageJson.scripts[scriptName].replace(match, match.replace(commandRegex, configuration
104
+ ? `$1nx ${target} --configuration=${configuration}$4`
105
+ : `$1nx ${target}$4`));
106
+ replacedTargets.add(target);
107
+ }
108
+ else {
109
+ // there are different args or the script has extra args, replace with the command leaving the args
110
+ packageJson.scripts[scriptName] = packageJson.scripts[scriptName].replace(match, match.replace(commandRegex, configuration
111
+ ? `$1nx ${target} --configuration=${configuration}$3`
112
+ : `$1nx ${target}$3`));
113
+ replacedTargets.add(target);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ if (process.env.NX_RUNNING_NX_INIT === 'true') {
120
+ // running `nx init` so we want to exclude everything by default
121
+ packageJson.nx ??= {};
122
+ packageJson.nx.includedScripts = [];
123
+ }
124
+ else if (replacedTargets.size) {
125
+ /**
126
+ * Running `nx add`. In this case we want to:
127
+ * - if `includedScripts` is already set: exclude scripts that match inferred targets that were used to replace a script
128
+ * - if `includedScripts` is not set: set `includedScripts` with all scripts except the ones that match an inferred target that was used to replace a script
129
+ */
130
+ const includedScripts = packageJson.nx?.includedScripts ?? Object.keys(packageJson.scripts);
131
+ const filteredScripts = includedScripts.filter((s) => !replacedTargets.has(s));
132
+ if (filteredScripts.length !== includedScripts.length) {
133
+ packageJson.nx ??= {};
134
+ packageJson.nx.includedScripts = filteredScripts;
135
+ }
136
+ }
137
+ writeJson(tree, packageJsonPath, packageJson);
138
+ }
139
+ function getInferredTargetCommands(result) {
140
+ const targetCommands = [];
141
+ for (const project of Object.values(result.projects ?? {})) {
142
+ for (const [targetName, target] of Object.entries(project.targets ?? {})) {
143
+ if (target.command) {
144
+ targetCommands.push({ command: target.command, target: targetName });
145
+ }
146
+ else if (target.executor === 'nx:run-commands' &&
147
+ target.options?.command) {
148
+ targetCommands.push({
149
+ command: target.options.command,
150
+ target: targetName,
151
+ });
152
+ }
153
+ if (!target.configurations) {
154
+ continue;
155
+ }
156
+ for (const [configurationName, configuration] of Object.entries(target.configurations)) {
157
+ if (configuration.command) {
158
+ targetCommands.push({
159
+ command: configuration.command,
160
+ target: targetName,
161
+ configuration: configurationName,
162
+ });
163
+ }
164
+ else if (target.executor === 'nx:run-commands' &&
165
+ configuration.options?.command) {
166
+ targetCommands.push({
167
+ command: configuration.options.command,
168
+ target: targetName,
169
+ configuration: configurationName,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ }
175
+ return targetCommands;
176
+ }
177
+ function getProjectRootFromConfigFile(file) {
178
+ let projectRoot = (0, path_1.dirname)(file);
179
+ if ((0, path_1.basename)(projectRoot) === '.storybook') {
180
+ projectRoot = (0, path_1.dirname)(projectRoot);
181
+ }
182
+ return projectRoot;
183
+ }
@@ -1 +1 @@
1
- export declare const typescriptVersion = "~5.1.3";
1
+ export declare const typescriptVersion = "~5.4.2";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.typescriptVersion = void 0;
4
- exports.typescriptVersion = '~5.1.3';
4
+ exports.typescriptVersion = '~5.4.2';