@nx/devkit 0.0.0-pr-27957-a99d5ea → 0.0.0-pr-28229-afe192c

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/devkit",
3
- "version": "0.0.0-pr-27957-a99d5ea",
3
+ "version": "0.0.0-pr-28229-afe192c",
4
4
  "private": false,
5
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": {
@@ -29,14 +29,13 @@
29
29
  "homepage": "https://nx.dev",
30
30
  "dependencies": {
31
31
  "ejs": "^3.1.7",
32
- "enquirer": "~2.3.6",
33
32
  "ignore": "^5.0.4",
34
33
  "tmp": "~0.2.1",
35
34
  "tslib": "^2.3.0",
36
35
  "semver": "^7.5.3",
37
36
  "yargs-parser": "21.1.1",
38
37
  "minimatch": "9.0.3",
39
- "@nrwl/devkit": "0.0.0-pr-27957-a99d5ea"
38
+ "enquirer": "~2.3.6"
40
39
  },
41
40
  "peerDependencies": {
42
41
  "nx": ">= 17 <= 20"
@@ -49,4 +48,4 @@
49
48
  },
50
49
  "type": "commonjs",
51
50
  "types": "./index.d.ts"
52
- }
51
+ }
@@ -1,20 +1,10 @@
1
- import { Tree } from 'nx/src/devkit-exports';
2
- export type NameAndDirectoryFormat = 'as-provided' | 'derived';
1
+ import { type Tree } from 'nx/src/devkit-exports';
3
2
  export type ArtifactGenerationOptions = {
4
- artifactType: string;
5
- callingGenerator: string | null;
6
- name: string;
7
- directory?: string;
8
- disallowPathInNameForDerived?: boolean;
3
+ path: string;
4
+ name?: string;
9
5
  fileExtension?: 'js' | 'jsx' | 'ts' | 'tsx' | 'vue';
10
6
  fileName?: string;
11
- flat?: boolean;
12
- nameAndDirectoryFormat?: NameAndDirectoryFormat;
13
- pascalCaseDirectory?: boolean;
14
- pascalCaseFile?: boolean;
15
- project?: string;
16
7
  suffix?: string;
17
- derivedDirectory?: string;
18
8
  };
19
9
  export type NameAndDirectoryOptions = {
20
10
  /**
@@ -38,9 +28,7 @@ export type NameAndDirectoryOptions = {
38
28
  */
39
29
  project: string;
40
30
  };
41
- export declare function determineArtifactNameAndDirectoryOptions(tree: Tree, options: ArtifactGenerationOptions): Promise<NameAndDirectoryOptions & {
42
- nameAndDirectoryFormat: NameAndDirectoryFormat;
43
- }>;
31
+ export declare function determineArtifactNameAndDirectoryOptions(tree: Tree, options: ArtifactGenerationOptions): Promise<NameAndDirectoryOptions>;
44
32
  export declare function getRelativeCwd(): string;
45
33
  /**
46
34
  * Function for setting cwd during testing
@@ -3,210 +3,42 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.determineArtifactNameAndDirectoryOptions = determineArtifactNameAndDirectoryOptions;
4
4
  exports.getRelativeCwd = getRelativeCwd;
5
5
  exports.setCwd = setCwd;
6
- const enquirer_1 = require("enquirer");
7
- const path_1 = require("path");
8
- const names_1 = require("../utils/names");
9
6
  const devkit_exports_1 = require("nx/src/devkit-exports");
10
7
  const devkit_internals_1 = require("nx/src/devkit-internals");
8
+ const path_1 = require("path");
11
9
  async function determineArtifactNameAndDirectoryOptions(tree, options) {
12
- if (!options.nameAndDirectoryFormat &&
13
- (process.env.NX_INTERACTIVE !== 'true' || !isTTY())) {
14
- options.nameAndDirectoryFormat = 'derived';
15
- }
16
- const formats = getNameAndDirectoryOptionFormats(tree, options);
17
- const format = options.nameAndDirectoryFormat ?? (await determineFormat(formats, options));
18
- validateResolvedProject(tree, formats[format]?.project, options, formats[format]?.directory);
19
- if (format === 'derived' && options.callingGenerator) {
20
- logDeprecationMessage(options, formats);
21
- }
22
- return {
23
- ...formats[format],
24
- nameAndDirectoryFormat: format,
25
- };
10
+ const normalizedOptions = getNameAndDirectoryOptions(tree, options);
11
+ validateResolvedProject(normalizedOptions.project, normalizedOptions.directory);
12
+ return normalizedOptions;
26
13
  }
27
- async function determineFormat(formats, options) {
28
- if (!formats.derived) {
29
- return 'as-provided';
30
- }
31
- const asProvidedDescription = `As provided: ${formats['as-provided'].filePath}`;
32
- const asProvidedSelectedValue = formats['as-provided'].filePath;
33
- const derivedDescription = `Derived: ${formats['derived'].filePath}`;
34
- const derivedSelectedValue = formats['derived'].filePath;
35
- if (asProvidedSelectedValue === derivedSelectedValue) {
36
- return 'as-provided';
37
- }
38
- const result = await (0, enquirer_1.prompt)({
39
- type: 'select',
40
- name: 'format',
41
- message: `Where should the ${options.artifactType} be generated?`,
42
- choices: [
43
- {
44
- message: asProvidedDescription,
45
- name: asProvidedSelectedValue,
46
- },
47
- {
48
- message: derivedDescription,
49
- name: derivedSelectedValue,
50
- },
51
- ],
52
- initial: 0,
53
- }).then(({ format }) => format === asProvidedSelectedValue ? 'as-provided' : 'derived');
54
- return result;
55
- }
56
- function logDeprecationMessage(options, formats) {
57
- devkit_exports_1.logger.warn(`
58
- In Nx 20, generating a ${options.artifactType} will no longer support providing a project and deriving the directory.
59
- Please provide the exact directory in the future.
60
- Example: nx g ${options.callingGenerator} ${formats['derived'].artifactName} --directory ${formats['derived'].directory}
61
- 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.
62
- `);
63
- }
64
- function getNameAndDirectoryOptionFormats(tree, options) {
65
- const directory = options.directory
66
- ? (0, devkit_exports_1.normalizePath)(options.directory.replace(/^\.?\//, ''))
14
+ function getNameAndDirectoryOptions(tree, options) {
15
+ const path = options.path
16
+ ? (0, devkit_exports_1.normalizePath)(options.path.replace(/^\.?\//, ''))
67
17
  : undefined;
68
18
  const fileExtension = options.fileExtension ?? 'ts';
69
- const { name: extractedName, directory: extractedDirectory } = extractNameAndDirectoryFromName(options.name);
70
- if (extractedDirectory && directory) {
71
- throw new Error(`You can't specify both a directory (${options.directory}) and a name with a directory path (${options.name}). ` +
72
- `Please specify either a directory or a name with a directory path.`);
73
- }
74
- const asProvidedOptions = getAsProvidedOptions(tree, {
75
- ...options,
76
- directory: directory ?? extractedDirectory,
77
- fileExtension,
78
- name: extractedName,
79
- });
80
- if (!options.project) {
81
- validateResolvedProject(tree, asProvidedOptions.project, options, asProvidedOptions.directory);
82
- }
83
- if (options.nameAndDirectoryFormat === 'as-provided') {
84
- return {
85
- 'as-provided': asProvidedOptions,
86
- derived: undefined,
87
- };
88
- }
89
- if (options.disallowPathInNameForDerived && options.name.includes('/')) {
90
- if (!options.nameAndDirectoryFormat) {
91
- devkit_exports_1.output.warn({
92
- title: `The provided name "${options.name}" contains a path and this is not supported by the "${options.callingGenerator}" when using the "derived" format.`,
93
- bodyLines: [
94
- `The generator will try to generate the ${options.artifactType} using the "as-provided" format at "${asProvidedOptions.filePath}".`,
95
- ],
96
- });
97
- return {
98
- 'as-provided': asProvidedOptions,
99
- derived: undefined,
100
- };
101
- }
102
- throw new Error(`The provided name "${options.name}" contains a path and this is not supported by the "${options.callingGenerator}" when using the "derived" format. ` +
103
- `Please provide a name without a path or use the "as-provided" format.`);
104
- }
105
- const derivedOptions = getDerivedOptions(tree, {
106
- ...options,
107
- directory,
108
- fileExtension,
109
- name: extractedName,
110
- }, asProvidedOptions, !options.disallowPathInNameForDerived && extractedDirectory
111
- ? extractedDirectory
112
- : undefined);
113
- return {
114
- 'as-provided': asProvidedOptions,
115
- derived: derivedOptions,
116
- };
117
- }
118
- function getAsProvidedOptions(tree, options) {
19
+ let { name: extractedName, directory } = extractNameAndDirectoryFromPath(path);
119
20
  const relativeCwd = getRelativeCwd();
120
- let asProvidedDirectory;
121
- if (options.directory) {
122
- // append the directory to the current working directory if it doesn't start with it
123
- if (options.directory === relativeCwd ||
124
- options.directory.startsWith(`${relativeCwd}/`)) {
125
- asProvidedDirectory = options.directory;
126
- }
127
- else {
128
- asProvidedDirectory = (0, devkit_exports_1.joinPathFragments)(relativeCwd, options.directory);
129
- }
130
- }
131
- else {
132
- asProvidedDirectory = relativeCwd;
133
- }
134
- const asProvidedProject = findProjectFromPath(tree, asProvidedDirectory);
135
- const asProvidedFileName = options.fileName ??
136
- (options.suffix ? `${options.name}.${options.suffix}` : options.name);
137
- const asProvidedFilePath = (0, devkit_exports_1.joinPathFragments)(asProvidedDirectory, `${asProvidedFileName}.${options.fileExtension}`);
138
- return {
139
- artifactName: options.name,
140
- directory: asProvidedDirectory,
141
- fileName: asProvidedFileName,
142
- filePath: asProvidedFilePath,
143
- project: asProvidedProject,
144
- };
145
- }
146
- function getDerivedOptions(tree, options, asProvidedOptions, extractedDirectory) {
147
- const projects = (0, devkit_exports_1.getProjects)(tree);
148
- if (options.project && !projects.has(options.project)) {
149
- throw new Error(`The provided project "${options.project}" does not exist! Please provide an existing project name.`);
150
- }
151
- const projectName = options.project ?? asProvidedOptions.project;
152
- const project = projects.get(projectName);
153
- const derivedName = options.name;
154
- const baseDirectory = options.directory
155
- ? (0, names_1.names)(options.directory).fileName
156
- : (0, devkit_exports_1.joinPathFragments)(project.sourceRoot ?? (0, devkit_exports_1.joinPathFragments)(project.root, 'src'), project.projectType === 'application' ? 'app' : 'lib', extractedDirectory ?? '');
157
- const derivedDirectory = typeof options.derivedDirectory === 'string'
158
- ? (0, devkit_exports_1.joinPathFragments)(project.sourceRoot ?? project.root, options.derivedDirectory, options.flat
159
- ? ''
160
- : options.pascalCaseDirectory
161
- ? (0, names_1.names)(derivedName).className
162
- : (0, names_1.names)(derivedName).fileName)
163
- : options.flat
164
- ? (0, devkit_exports_1.normalizePath)(baseDirectory)
165
- : (0, devkit_exports_1.joinPathFragments)(baseDirectory, options.pascalCaseDirectory
166
- ? (0, names_1.names)(derivedName).className
167
- : (0, names_1.names)(derivedName).fileName);
168
- if (options.directory &&
169
- !isDirectoryUnderProjectRoot(derivedDirectory, project.root)) {
170
- if (!options.nameAndDirectoryFormat) {
171
- devkit_exports_1.output.warn({
172
- title: `The provided directory "${options.directory}" is not under the provided project root "${project.root}".`,
173
- bodyLines: [
174
- `The generator will try to generate the ${options.artifactType} using the "as-provided" format.`,
175
- `With the "as-provided" format, the "project" option is ignored and the ${options.artifactType} will be generated at "${asProvidedOptions.filePath}" (<cwd>/<provided directory>).`,
176
- ],
177
- });
178
- return undefined;
179
- }
180
- throw new Error(`The provided directory "${options.directory}" is not under the provided project root "${project.root}". ` +
181
- `Please provide a directory that is under the provided project root or use the "as-provided" format and only provide the directory.`);
182
- }
183
- let derivedFileName = options.fileName;
184
- if (!derivedFileName) {
185
- derivedFileName = options.suffix
186
- ? `${derivedName}.${options.suffix}`
187
- : derivedName;
188
- derivedFileName = options.pascalCaseFile
189
- ? (0, names_1.names)(derivedFileName).className
190
- : (0, names_1.names)(derivedFileName).fileName;
191
- }
192
- const derivedFilePath = (0, devkit_exports_1.joinPathFragments)(derivedDirectory, `${derivedFileName}.${options.fileExtension}`);
21
+ // append the directory to the current working directory if it doesn't start with it
22
+ if (directory !== relativeCwd && !directory.startsWith(`${relativeCwd}/`)) {
23
+ directory = (0, devkit_exports_1.joinPathFragments)(relativeCwd, directory);
24
+ }
25
+ const project = findProjectFromPath(tree, directory);
26
+ const name = options.fileName ??
27
+ (options.suffix ? `${extractedName}.${options.suffix}` : extractedName);
28
+ const filePath = (0, devkit_exports_1.joinPathFragments)(directory, `${name}.${fileExtension}`);
193
29
  return {
194
- artifactName: derivedName,
195
- directory: derivedDirectory,
196
- fileName: derivedFileName,
197
- filePath: derivedFilePath,
198
- project: projectName,
30
+ artifactName: options.name ?? extractedName,
31
+ directory: directory,
32
+ fileName: name,
33
+ filePath: filePath,
34
+ project: project,
199
35
  };
200
36
  }
201
- function validateResolvedProject(tree, project, options, normalizedDirectory) {
37
+ function validateResolvedProject(project, normalizedDirectory) {
202
38
  if (project) {
203
39
  return;
204
40
  }
205
- if (options.directory) {
206
- throw new Error(`The provided directory resolved relative to the current working directory "${normalizedDirectory}" does not exist under any project root. ` +
207
- `Please make sure to navigate to a location or provide a directory that exists under a project root.`);
208
- }
209
- throw new Error(`The current working directory "${getRelativeCwd() || '.'}" does not exist under any project root. ` +
41
+ throw new Error(`The provided directory resolved relative to the current working directory "${normalizedDirectory}" does not exist under any project root. ` +
210
42
  `Please make sure to navigate to a location or provide a directory that exists under a project root.`);
211
43
  }
212
44
  function findProjectFromPath(tree, path) {
@@ -218,15 +50,6 @@ function findProjectFromPath(tree, path) {
218
50
  const projectRootMappings = (0, devkit_internals_1.createProjectRootMappingsFromProjectConfigurations)(projectConfigurations);
219
51
  return (0, devkit_internals_1.findProjectForPath)(path, projectRootMappings);
220
52
  }
221
- function isDirectoryUnderProjectRoot(directory, projectRoot) {
222
- const normalizedDirectory = (0, devkit_exports_1.joinPathFragments)(devkit_exports_1.workspaceRoot, directory);
223
- const normalizedProjectRoot = (0, devkit_exports_1.joinPathFragments)(devkit_exports_1.workspaceRoot, projectRoot).replace(/\/$/, '');
224
- return (normalizedDirectory === normalizedProjectRoot ||
225
- normalizedDirectory.startsWith(`${normalizedProjectRoot}/`));
226
- }
227
- function isTTY() {
228
- return !!process.stdout.isTTY && process.env['CI'] !== 'true';
229
- }
230
53
  function getRelativeCwd() {
231
54
  return (0, devkit_exports_1.normalizePath)((0, path_1.relative)(devkit_exports_1.workspaceRoot, getCwd()));
232
55
  }
@@ -241,9 +64,11 @@ function getCwd() {
241
64
  ? process.env.INIT_CWD
242
65
  : process.cwd();
243
66
  }
244
- function extractNameAndDirectoryFromName(rawName) {
245
- const parsedName = (0, devkit_exports_1.normalizePath)(rawName).split('/');
246
- const name = parsedName.pop();
247
- const directory = parsedName.length ? parsedName.join('/') : undefined;
67
+ function extractNameAndDirectoryFromPath(path) {
68
+ // Remove trailing slash
69
+ path = path.replace(/\/$/, '');
70
+ const parsedPath = (0, devkit_exports_1.normalizePath)(path).split('/');
71
+ const name = parsedPath.pop();
72
+ const directory = parsedPath.join('/');
248
73
  return { name, directory };
249
74
  }
@@ -1,19 +1,15 @@
1
- import { ProjectType, Tree } from 'nx/src/devkit-exports';
2
- export type ProjectNameAndRootFormat = 'as-provided' | 'derived';
1
+ import { type ProjectType, type Tree } from 'nx/src/devkit-exports';
3
2
  export type ProjectGenerationOptions = {
4
- name: string;
3
+ directory: string;
4
+ name?: string;
5
5
  projectType: ProjectType;
6
- callingGenerator: string | null;
7
- directory?: string;
8
6
  importPath?: string;
9
- projectNameAndRootFormat?: ProjectNameAndRootFormat;
10
7
  rootProject?: boolean;
11
8
  };
12
9
  export type ProjectNameAndRootOptions = {
13
10
  /**
14
11
  * Normalized full project name, including scope if name was provided with
15
- * scope (e.g., `@scope/name`, only available when `projectNameAndRootFormat`
16
- * is `as-provided`).
12
+ * scope (e.g., `@scope/name`)
17
13
  */
18
14
  projectName: string;
19
15
  /**
@@ -37,10 +33,5 @@ export type ProjectNameAndRootOptions = {
37
33
  */
38
34
  importPath?: string;
39
35
  };
40
- export declare function determineProjectNameAndRootOptions(tree: Tree, options: ProjectGenerationOptions): Promise<ProjectNameAndRootOptions & {
41
- projectNameAndRootFormat: ProjectNameAndRootFormat;
42
- }>;
43
- /**
44
- * Function for setting cwd during testing
45
- */
46
- export declare function setCwd(path: string): void;
36
+ export declare function determineProjectNameAndRootOptions(tree: Tree, options: ProjectGenerationOptions): Promise<ProjectNameAndRootOptions>;
37
+ export declare function ensureProjectName(tree: Tree, options: Omit<ProjectGenerationOptions, 'projectType'>, projectType: 'application' | 'library'): Promise<void>;
@@ -1,157 +1,36 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.determineProjectNameAndRootOptions = determineProjectNameAndRootOptions;
4
- exports.setCwd = setCwd;
4
+ exports.ensureProjectName = ensureProjectName;
5
5
  const enquirer_1 = require("enquirer");
6
- const path_1 = require("path");
7
- const get_workspace_layout_1 = require("../utils/get-workspace-layout");
8
- const names_1 = require("../utils/names");
9
6
  const devkit_exports_1 = require("nx/src/devkit-exports");
7
+ const path_1 = require("path");
10
8
  async function determineProjectNameAndRootOptions(tree, options) {
11
- if (!options.projectNameAndRootFormat &&
12
- (process.env.NX_INTERACTIVE !== 'true' || !isTTY())) {
13
- options.projectNameAndRootFormat = 'derived';
14
- }
15
- validateName(options.name, options.projectNameAndRootFormat);
16
- const formats = getProjectNameAndRootFormats(tree, options);
17
- const format = options.projectNameAndRootFormat ?? (await determineFormat(formats));
18
- if (format === 'derived' && options.callingGenerator) {
19
- logDeprecationMessage(options.callingGenerator, formats);
20
- }
21
- return {
22
- ...formats[format],
23
- projectNameAndRootFormat: format,
24
- };
25
- }
26
- function validateName(name, projectNameAndRootFormat) {
27
- if (projectNameAndRootFormat === 'derived' && name.startsWith('@')) {
28
- throw new Error(`The project name "${name}" cannot start with "@" when the "projectNameAndRootFormat" is "derived".`);
29
- }
30
- /**
31
- * Matches two types of project names:
32
- *
33
- * 1. Valid npm package names (e.g., '@scope/name' or 'name').
34
- * 2. Names starting with a letter and can contain any character except whitespace and ':'.
35
- *
36
- * The second case is to support the legacy behavior (^[a-zA-Z].*$) with the difference
37
- * that it doesn't allow the ":" character. It was wrong to allow it because it would
38
- * conflict with the notation for tasks.
39
- */
40
- const pattern = '(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$';
41
- const validationRegex = new RegExp(pattern);
42
- if (!validationRegex.test(name)) {
43
- throw new Error(`The project name should match the pattern "${pattern}". The provided value "${name}" does not match.`);
44
- }
45
- }
46
- function logDeprecationMessage(callingGenerator, formats) {
47
- devkit_exports_1.logger.warn((0, devkit_exports_1.stripIndents) `
48
- In Nx 20, generating projects will no longer derive the name and root.
49
- Please provide the exact project name and root in the future.
50
- Example: nx g ${callingGenerator} ${formats['derived'].projectName} --directory ${formats['derived'].projectRoot}
51
- `);
52
- }
53
- async function determineFormat(formats) {
54
- if (!formats.derived) {
55
- return 'as-provided';
56
- }
57
- const asProvidedDescription = `As provided:
58
- Name: ${formats['as-provided'].projectName}
59
- Root: ${formats['as-provided'].projectRoot}`;
60
- const asProvidedSelectedValue = `${formats['as-provided'].projectName} @ ${formats['as-provided'].projectRoot}`;
61
- const derivedDescription = `Derived:
62
- Name: ${formats['derived'].projectName}
63
- Root: ${formats['derived'].projectRoot}`;
64
- const derivedSelectedValue = `${formats['derived'].projectName} @ ${formats['derived'].projectRoot}`;
65
- if (asProvidedSelectedValue === derivedSelectedValue) {
66
- return 'as-provided';
67
- }
68
- const result = await (0, enquirer_1.prompt)({
69
- type: 'select',
70
- name: 'format',
71
- message: 'What should be the project name and where should it be generated?',
72
- choices: [
73
- {
74
- message: asProvidedDescription,
75
- name: asProvidedSelectedValue,
76
- },
77
- {
78
- message: derivedDescription,
79
- name: derivedSelectedValue,
80
- },
81
- ],
82
- initial: 0,
83
- }).then(({ format }) => format === asProvidedSelectedValue ? 'as-provided' : 'derived');
84
- return result;
85
- }
86
- function getProjectNameAndRootFormats(tree, options) {
87
- const directory = options.directory
88
- ? (0, devkit_exports_1.normalizePath)(options.directory.replace(/^\.?\//, ''))
89
- : undefined;
90
- const { name: asProvidedParsedName, directory: asProvidedParsedDirectory } = parseNameForAsProvided(options.name);
91
- if (asProvidedParsedDirectory && directory) {
92
- throw new Error(`You can't specify both a directory (${options.directory}) and a name with a directory path (${options.name}). ` +
93
- `Please specify either a directory or a name with a directory path.`);
94
- }
95
- const asProvidedOptions = getAsProvidedOptions(tree, {
96
- ...options,
97
- directory: directory ?? asProvidedParsedDirectory,
98
- name: asProvidedParsedName,
99
- });
100
- if (options.projectNameAndRootFormat === 'as-provided') {
101
- return {
102
- 'as-provided': asProvidedOptions,
103
- derived: undefined,
104
- };
105
- }
106
- if (asProvidedOptions.projectName.startsWith('@')) {
107
- if (!options.projectNameAndRootFormat) {
108
- devkit_exports_1.output.warn({
109
- 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.`,
110
- bodyLines: [
111
- `The generator will try to generate the project "${asProvidedOptions.projectName}" using the "as-provided" format at "${asProvidedOptions.projectRoot}".`,
112
- ],
113
- });
114
- return {
115
- 'as-provided': asProvidedOptions,
116
- derived: undefined,
117
- };
118
- }
119
- 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. ` +
120
- `Please provide a name without "@" or use the "as-provided" format.`);
121
- }
122
- const { name: derivedParsedName, directory: derivedParsedDirectory } = parseNameForDerived(options.name);
123
- const derivedOptions = getDerivedOptions(tree, {
124
- ...options,
125
- directory: directory ?? derivedParsedDirectory,
126
- name: derivedParsedName,
127
- });
128
- return {
129
- 'as-provided': asProvidedOptions,
130
- derived: derivedOptions,
131
- };
132
- }
133
- function getAsProvidedOptions(tree, options) {
9
+ validateOptions(options);
10
+ const directory = (0, devkit_exports_1.normalizePath)(options.directory);
11
+ const name = options.name ??
12
+ directory.match(/(@[^@/]+(\/[^@/]+)+)/)?.[1] ??
13
+ directory.substring(directory.lastIndexOf('/') + 1);
134
14
  let projectSimpleName;
135
15
  let projectFileName;
136
- if (options.name.startsWith('@')) {
137
- const [_scope, ...rest] = options.name.split('/');
16
+ if (name.startsWith('@')) {
17
+ const [_scope, ...rest] = name.split('/');
138
18
  projectFileName = rest.join('-');
139
19
  projectSimpleName = rest.pop();
140
20
  }
141
21
  else {
142
- projectSimpleName = options.name;
143
- projectFileName = options.name;
22
+ projectSimpleName = name;
23
+ projectFileName = name;
144
24
  }
145
25
  let projectRoot;
146
26
  const relativeCwd = getRelativeCwd();
147
- if (options.directory) {
27
+ if (directory) {
148
28
  // append the directory to the current working directory if it doesn't start with it
149
- if (options.directory === relativeCwd ||
150
- options.directory.startsWith(`${relativeCwd}/`)) {
151
- projectRoot = options.directory;
29
+ if (directory === relativeCwd || directory.startsWith(`${relativeCwd}/`)) {
30
+ projectRoot = directory;
152
31
  }
153
32
  else {
154
- projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, options.directory);
33
+ projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, directory);
155
34
  }
156
35
  }
157
36
  else if (options.rootProject) {
@@ -160,29 +39,29 @@ function getAsProvidedOptions(tree, options) {
160
39
  else {
161
40
  projectRoot = relativeCwd;
162
41
  // append the project name to the current working directory if it doesn't end with it
163
- if (!relativeCwd.endsWith(options.name)) {
164
- projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, options.name);
42
+ if (!relativeCwd.endsWith(name)) {
43
+ projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, name);
165
44
  }
166
45
  }
167
46
  let importPath = undefined;
168
47
  if (options.projectType === 'library') {
169
48
  importPath = options.importPath;
170
49
  if (!importPath) {
171
- if (options.name.startsWith('@')) {
172
- importPath = options.name;
50
+ if (name.startsWith('@')) {
51
+ importPath = name;
173
52
  }
174
53
  else {
175
54
  const npmScope = getNpmScope(tree);
176
55
  importPath =
177
56
  projectRoot === '.'
178
57
  ? (0, devkit_exports_1.readJson)(tree, 'package.json').name ??
179
- getImportPath(npmScope, options.name)
180
- : getImportPath(npmScope, options.name);
58
+ getImportPath(npmScope, name)
59
+ : getImportPath(npmScope, name);
181
60
  }
182
61
  }
183
62
  }
184
63
  return {
185
- projectName: options.name,
64
+ projectName: name,
186
65
  names: {
187
66
  projectSimpleName,
188
67
  projectFileName,
@@ -191,53 +70,51 @@ function getAsProvidedOptions(tree, options) {
191
70
  projectRoot,
192
71
  };
193
72
  }
194
- function getDerivedOptions(tree, options) {
195
- const name = (0, names_1.names)(options.name).fileName;
196
- let { projectDirectory, layoutDirectory } = getDirectories(tree, options.directory, options.projectType);
197
- const projectDirectoryWithoutLayout = projectDirectory
198
- ? `${(0, names_1.names)(projectDirectory).fileName}/${name}`
199
- : options.rootProject
200
- ? '.'
201
- : name;
202
- // the project name uses the directory without the layout directory
203
- const projectName = projectDirectoryWithoutLayout === '.'
204
- ? name
205
- : projectDirectoryWithoutLayout.replace(/\//g, '-');
206
- const projectSimpleName = name;
207
- let projectRoot = projectDirectoryWithoutLayout;
208
- if (projectDirectoryWithoutLayout !== '.') {
209
- // prepend the layout directory
210
- projectRoot = (0, devkit_exports_1.joinPathFragments)(layoutDirectory, projectRoot);
211
- }
212
- let importPath;
213
- if (options.projectType === 'library') {
214
- importPath = options.importPath;
215
- if (!importPath) {
216
- const npmScope = getNpmScope(tree);
217
- importPath =
218
- projectRoot === '.'
219
- ? (0, devkit_exports_1.readJson)(tree, 'package.json').name ??
220
- getImportPath(npmScope, projectName)
221
- : getImportPath(npmScope, projectDirectoryWithoutLayout);
73
+ async function ensureProjectName(tree, options, projectType) {
74
+ if (!options.name) {
75
+ if (options.directory === '.' && getRelativeCwd() === '') {
76
+ const result = await (0, enquirer_1.prompt)({
77
+ type: 'input',
78
+ name: 'name',
79
+ message: `What do you want to name the ${projectType}?`,
80
+ }).then(({ name }) => (options.name = name));
81
+ }
82
+ const { projectName } = await determineProjectNameAndRootOptions(tree, {
83
+ ...options,
84
+ projectType,
85
+ });
86
+ options.name = projectName;
87
+ }
88
+ }
89
+ function validateOptions(options) {
90
+ if (options.directory === '.') {
91
+ /**
92
+ * Root projects must provide name option
93
+ */
94
+ if (!options.name) {
95
+ throw new Error(`Root projects must also specify name option.`);
222
96
  }
223
97
  }
224
- return {
225
- projectName,
226
- names: {
227
- projectSimpleName,
228
- projectFileName: projectName,
229
- },
230
- importPath,
231
- projectRoot,
232
- };
233
- }
234
- function getDirectories(tree, directory, projectType) {
235
- let { projectDirectory, layoutDirectory } = (0, get_workspace_layout_1.extractLayoutDirectory)(directory);
236
- if (!layoutDirectory) {
237
- const { appsDir, libsDir } = (0, get_workspace_layout_1.getWorkspaceLayout)(tree);
238
- layoutDirectory = projectType === 'application' ? appsDir : libsDir;
98
+ else {
99
+ /**
100
+ * Both directory and name (if present) must match one of two cases:
101
+ *
102
+ * 1. Valid npm package names (e.g., '@scope/name' or 'name').
103
+ * 2. Names starting with a letter and can contain any character except whitespace and ':'.
104
+ *
105
+ * The second case is to support the legacy behavior (^[a-zA-Z].*$) with the difference
106
+ * that it doesn't allow the ":" character. It was wrong to allow it because it would
107
+ * conflict with the notation for tasks.
108
+ */
109
+ const pattern = '(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$';
110
+ const validationRegex = new RegExp(pattern);
111
+ if (options.name && !validationRegex.test(options.name)) {
112
+ throw new Error(`The name should match the pattern "${pattern}". The provided value "${options.name}" does not match.`);
113
+ }
114
+ if (!validationRegex.test(options.directory)) {
115
+ throw new Error(`The directory should match the pattern "${pattern}". The provided value "${options.directory}" does not match.`);
116
+ }
239
117
  }
240
- return { projectDirectory, layoutDirectory };
241
118
  }
242
119
  function getImportPath(npmScope, name) {
243
120
  return npmScope ? `${npmScope === '@' ? '' : '@'}${npmScope}/${name}` : name;
@@ -248,9 +125,6 @@ function getNpmScope(tree) {
248
125
  : { name: null };
249
126
  return name?.startsWith('@') ? name.split('/')[0].substring(1) : undefined;
250
127
  }
251
- function isTTY() {
252
- return !!process.stdout.isTTY && process.env['CI'] !== 'true';
253
- }
254
128
  /**
255
129
  * When running a script with the package manager (e.g. `npm run`), the package manager will
256
130
  * traverse the directory tree upwards until it finds a `package.json` and will set `process.cwd()`
@@ -265,32 +139,3 @@ function getCwd() {
265
139
  function getRelativeCwd() {
266
140
  return (0, devkit_exports_1.normalizePath)((0, path_1.relative)(devkit_exports_1.workspaceRoot, getCwd())).replace(/\/$/, '');
267
141
  }
268
- /**
269
- * Function for setting cwd during testing
270
- */
271
- function setCwd(path) {
272
- process.env.INIT_CWD = (0, path_1.join)(devkit_exports_1.workspaceRoot, path);
273
- }
274
- function parseNameForAsProvided(rawName) {
275
- const directory = (0, devkit_exports_1.normalizePath)(rawName);
276
- if (rawName.includes('@')) {
277
- const index = directory.lastIndexOf('@');
278
- if (index === 0) {
279
- return { name: rawName, directory: undefined };
280
- }
281
- const name = directory.substring(index);
282
- return { name, directory };
283
- }
284
- if (rawName.includes('/')) {
285
- const index = directory.lastIndexOf('/');
286
- const name = directory.substring(index + 1);
287
- return { name, directory };
288
- }
289
- return { name: rawName, directory: undefined };
290
- }
291
- function parseNameForDerived(rawName) {
292
- const parsedName = (0, devkit_exports_1.normalizePath)(rawName).split('/');
293
- const name = parsedName.pop();
294
- const directory = parsedName.length ? parsedName.join('/') : undefined;
295
- return { name, directory };
296
- }
@@ -27,6 +27,7 @@ function installPackagesTask(tree, alwaysRun = false, cwd = '', packageManager =
27
27
  const execSyncOptions = {
28
28
  cwd: (0, path_1.join)(tree.root, cwd),
29
29
  stdio: process.env.NX_GENERATE_QUIET === 'true' ? 'ignore' : 'inherit',
30
+ windowsHide: true,
30
31
  };
31
32
  // ensure local registry from process is not interfering with the install
32
33
  // when we start the process from temp folder the local registry would override the custom registry
@@ -38,16 +38,51 @@ async function _addPluginInternal(tree, graph, pluginName, pluginFactory, option
38
38
  const nxJson = (0, devkit_exports_1.readNxJson)(tree);
39
39
  let pluginOptions;
40
40
  let projConfigs;
41
- const combinations = generateCombinations(options);
42
- optionsLoop: for (const _pluginOptions of combinations) {
43
- pluginOptions = _pluginOptions;
44
- nxJson.plugins ??= [];
45
- if (nxJson.plugins.some((p) => typeof p === 'string'
46
- ? p === pluginName
47
- : p.plugin === pluginName && !p.include)) {
48
- // Plugin has already been added
49
- return;
41
+ if (Object.keys(options).length > 0) {
42
+ const combinations = generateCombinations(options);
43
+ optionsLoop: for (const _pluginOptions of combinations) {
44
+ pluginOptions = _pluginOptions;
45
+ nxJson.plugins ??= [];
46
+ if (nxJson.plugins.some((p) => typeof p === 'string'
47
+ ? p === pluginName
48
+ : p.plugin === pluginName && !p.include)) {
49
+ // Plugin has already been added
50
+ return;
51
+ }
52
+ global.NX_GRAPH_CREATION = true;
53
+ try {
54
+ projConfigs = await (0, devkit_internals_1.retrieveProjectConfigurations)([pluginFactory(pluginOptions)], tree.root, nxJson);
55
+ }
56
+ catch (e) {
57
+ // Errors are okay for this because we're only running 1 plugin
58
+ if (e instanceof devkit_internals_1.ProjectConfigurationsError) {
59
+ projConfigs = e.partialProjectConfigurationsResult;
60
+ }
61
+ else {
62
+ throw e;
63
+ }
64
+ }
65
+ global.NX_GRAPH_CREATION = false;
66
+ for (const projConfig of Object.values(projConfigs.projects)) {
67
+ const node = graphNodes.find((node) => node.data.root === projConfig.root);
68
+ if (!node) {
69
+ continue;
70
+ }
71
+ for (const targetName in projConfig.targets) {
72
+ if (node.data.targets[targetName]) {
73
+ // Conflicting Target Name, check the next one
74
+ pluginOptions = null;
75
+ continue optionsLoop;
76
+ }
77
+ }
78
+ }
79
+ break;
50
80
  }
81
+ }
82
+ else {
83
+ // If the plugin does not take in options, we add the plugin with empty options.
84
+ nxJson.plugins ??= [];
85
+ pluginOptions = {};
51
86
  global.NX_GRAPH_CREATION = true;
52
87
  try {
53
88
  projConfigs = await (0, devkit_internals_1.retrieveProjectConfigurations)([pluginFactory(pluginOptions)], tree.root, nxJson);
@@ -62,20 +97,6 @@ async function _addPluginInternal(tree, graph, pluginName, pluginFactory, option
62
97
  }
63
98
  }
64
99
  global.NX_GRAPH_CREATION = false;
65
- for (const projConfig of Object.values(projConfigs.projects)) {
66
- const node = graphNodes.find((node) => node.data.root === projConfig.root);
67
- if (!node) {
68
- continue;
69
- }
70
- for (const targetName in projConfig.targets) {
71
- if (node.data.targets[targetName]) {
72
- // Conflicting Target Name, check the next one
73
- pluginOptions = null;
74
- continue optionsLoop;
75
- }
76
- }
77
- }
78
- break;
79
100
  }
80
101
  if (!pluginOptions) {
81
102
  throw new Error('Could not add the plugin in a way which does not conflict with existing targets. Please report this error at: https://github.com/nrwl/nx/issues/new/choose');
@@ -1,2 +1,2 @@
1
- import { CreateNodesContext } from 'nx/src/devkit-exports';
2
- export declare function calculateHashForCreateNodes(projectRoot: string, options: object, context: CreateNodesContext, additionalGlobs?: string[]): Promise<string>;
1
+ import { CreateNodesContext, CreateNodesContextV2 } from 'nx/src/devkit-exports';
2
+ export declare function calculateHashForCreateNodes(projectRoot: string, options: object, context: CreateNodesContext | CreateNodesContextV2, additionalGlobs?: string[]): Promise<string>;
@@ -1,8 +1,8 @@
1
1
  import type { InputDefinition } from 'nx/src/config/workspace-json-project-json';
2
- import { CreateNodesContext } from 'nx/src/devkit-exports';
2
+ import { CreateNodesContext, CreateNodesContextV2 } from 'nx/src/devkit-exports';
3
3
  /**
4
4
  * Get the named inputs available for a directory
5
5
  */
6
- export declare function getNamedInputs(directory: string, context: CreateNodesContext): {
6
+ export declare function getNamedInputs(directory: string, context: CreateNodesContext | CreateNodesContextV2): {
7
7
  [inputName: string]: (string | InputDefinition)[];
8
8
  };
@@ -305,6 +305,7 @@ function ensurePackage(pkgOrTree, requiredVersionOrPackage, maybeRequiredVersion
305
305
  (0, child_process_1.execSync)(preInstallCommand, {
306
306
  cwd: tempDir,
307
307
  stdio: isVerbose ? 'inherit' : 'ignore',
308
+ windowsHide: true,
308
309
  });
309
310
  }
310
311
  let addCommand = (0, devkit_exports_1.getPackageManagerCommand)(packageManager).addDev;
@@ -314,6 +315,7 @@ function ensurePackage(pkgOrTree, requiredVersionOrPackage, maybeRequiredVersion
314
315
  (0, child_process_1.execSync)(`${addCommand} ${pkg}@${requiredVersion}`, {
315
316
  cwd: tempDir,
316
317
  stdio: isVerbose ? 'inherit' : 'ignore',
318
+ windowsHide: true,
317
319
  });
318
320
  addToNodePath((0, path_1.join)(devkit_exports_1.workspaceRoot, 'node_modules'));
319
321
  addToNodePath((0, path_1.join)(tempDir, 'node_modules'));