@nx/devkit 20.0.0-canary.20241001-8fa7065 → 20.0.0-canary.20241002-1d10a19

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/devkit",
3
- "version": "20.0.0-canary.20241001-8fa7065",
3
+ "version": "20.0.0-canary.20241002-1d10a19",
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": {
@@ -34,7 +34,8 @@
34
34
  "tslib": "^2.3.0",
35
35
  "semver": "^7.5.3",
36
36
  "yargs-parser": "21.1.1",
37
- "minimatch": "9.0.3"
37
+ "minimatch": "9.0.3",
38
+ "enquirer": "~2.3.6"
38
39
  },
39
40
  "peerDependencies": {
40
41
  "nx": ">= 17 <= 20"
@@ -1,11 +1,9 @@
1
1
  import { type Tree } from 'nx/src/devkit-exports';
2
- export type NameAndDirectoryFormat = 'as-provided';
3
2
  export type ArtifactGenerationOptions = {
4
- name: string;
5
- directory?: string;
3
+ path: string;
4
+ name?: string;
6
5
  fileExtension?: 'js' | 'jsx' | 'ts' | 'tsx' | 'vue';
7
6
  fileName?: string;
8
- nameAndDirectoryFormat?: NameAndDirectoryFormat;
9
7
  suffix?: string;
10
8
  };
11
9
  export type NameAndDirectoryOptions = {
@@ -30,9 +28,7 @@ export type NameAndDirectoryOptions = {
30
28
  */
31
29
  project: string;
32
30
  };
33
- export declare function determineArtifactNameAndDirectoryOptions(tree: Tree, options: ArtifactGenerationOptions): Promise<NameAndDirectoryOptions & {
34
- nameAndDirectoryFormat: NameAndDirectoryFormat;
35
- }>;
31
+ export declare function determineArtifactNameAndDirectoryOptions(tree: Tree, options: ArtifactGenerationOptions): Promise<NameAndDirectoryOptions>;
36
32
  export declare function getRelativeCwd(): string;
37
33
  /**
38
34
  * Function for setting cwd during testing
@@ -7,68 +7,38 @@ const devkit_exports_1 = require("nx/src/devkit-exports");
7
7
  const devkit_internals_1 = require("nx/src/devkit-internals");
8
8
  const path_1 = require("path");
9
9
  async function determineArtifactNameAndDirectoryOptions(tree, options) {
10
- const nameAndDirectoryOptions = getNameAndDirectoryOptions(tree, options);
11
- validateResolvedProject(tree, nameAndDirectoryOptions.project, options, nameAndDirectoryOptions.directory);
12
- return {
13
- ...nameAndDirectoryOptions,
14
- nameAndDirectoryFormat: 'as-provided',
15
- };
10
+ const normalizedOptions = getNameAndDirectoryOptions(tree, options);
11
+ validateResolvedProject(normalizedOptions.project, normalizedOptions.directory);
12
+ return normalizedOptions;
16
13
  }
17
14
  function getNameAndDirectoryOptions(tree, options) {
18
- const directory = options.directory
19
- ? (0, devkit_exports_1.normalizePath)(options.directory.replace(/^\.?\//, ''))
15
+ const path = options.path
16
+ ? (0, devkit_exports_1.normalizePath)(options.path.replace(/^\.?\//, ''))
20
17
  : undefined;
21
18
  const fileExtension = options.fileExtension ?? 'ts';
22
- const { name: extractedName, directory: extractedDirectory } = extractNameAndDirectoryFromName(options.name);
23
- if (extractedDirectory && directory) {
24
- throw new Error(`You can't specify both a directory (${options.directory}) and a name with a directory path (${options.name}). ` +
25
- `Please specify either a directory or a name with a directory path.`);
26
- }
27
- const asProvidedOptions = getAsProvidedOptions(tree, {
28
- ...options,
29
- directory: directory ?? extractedDirectory,
30
- fileExtension,
31
- name: extractedName,
32
- });
33
- return asProvidedOptions;
34
- }
35
- function getAsProvidedOptions(tree, options) {
19
+ let { name: extractedName, directory } = extractNameAndDirectoryFromPath(path);
36
20
  const relativeCwd = getRelativeCwd();
37
- let asProvidedDirectory;
38
- if (options.directory) {
39
- // append the directory to the current working directory if it doesn't start with it
40
- if (options.directory === relativeCwd ||
41
- options.directory.startsWith(`${relativeCwd}/`)) {
42
- asProvidedDirectory = options.directory;
43
- }
44
- else {
45
- asProvidedDirectory = (0, devkit_exports_1.joinPathFragments)(relativeCwd, options.directory);
46
- }
47
- }
48
- else {
49
- asProvidedDirectory = relativeCwd;
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);
50
24
  }
51
- const asProvidedProject = findProjectFromPath(tree, asProvidedDirectory);
52
- const asProvidedFileName = options.fileName ??
53
- (options.suffix ? `${options.name}.${options.suffix}` : options.name);
54
- const asProvidedFilePath = (0, devkit_exports_1.joinPathFragments)(asProvidedDirectory, `${asProvidedFileName}.${options.fileExtension}`);
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}`);
55
29
  return {
56
- artifactName: options.name,
57
- directory: asProvidedDirectory,
58
- fileName: asProvidedFileName,
59
- filePath: asProvidedFilePath,
60
- project: asProvidedProject,
30
+ artifactName: options.name ?? extractedName,
31
+ directory: directory,
32
+ fileName: name,
33
+ filePath: filePath,
34
+ project: project,
61
35
  };
62
36
  }
63
- function validateResolvedProject(tree, project, options, normalizedDirectory) {
37
+ function validateResolvedProject(project, normalizedDirectory) {
64
38
  if (project) {
65
39
  return;
66
40
  }
67
- if (options.directory) {
68
- throw new Error(`The provided directory resolved relative to the current working directory "${normalizedDirectory}" does not exist under any project root. ` +
69
- `Please make sure to navigate to a location or provide a directory that exists under a project root.`);
70
- }
71
- 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. ` +
72
42
  `Please make sure to navigate to a location or provide a directory that exists under a project root.`);
73
43
  }
74
44
  function findProjectFromPath(tree, path) {
@@ -94,9 +64,11 @@ function getCwd() {
94
64
  ? process.env.INIT_CWD
95
65
  : process.cwd();
96
66
  }
97
- function extractNameAndDirectoryFromName(rawName) {
98
- const parsedName = (0, devkit_exports_1.normalizePath)(rawName).split('/');
99
- const name = parsedName.pop();
100
- 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('/');
101
73
  return { name, directory };
102
74
  }
@@ -1,18 +1,15 @@
1
1
  import { type ProjectType, type Tree } from 'nx/src/devkit-exports';
2
- export type ProjectNameAndRootFormat = 'as-provided';
3
2
  export type ProjectGenerationOptions = {
4
- name: string;
3
+ directory: string;
4
+ name?: string;
5
5
  projectType: ProjectType;
6
- directory?: string;
7
6
  importPath?: string;
8
7
  rootProject?: boolean;
9
- projectNameAndRootFormat?: ProjectNameAndRootFormat;
10
8
  };
11
9
  export type ProjectNameAndRootOptions = {
12
10
  /**
13
11
  * Normalized full project name, including scope if name was provided with
14
- * scope (e.g., `@scope/name`, only available when `projectNameAndRootFormat`
15
- * is `as-provided`).
12
+ * scope (e.g., `@scope/name`)
16
13
  */
17
14
  projectName: string;
18
15
  /**
@@ -36,10 +33,5 @@ export type ProjectNameAndRootOptions = {
36
33
  */
37
34
  importPath?: string;
38
35
  };
39
- export declare function determineProjectNameAndRootOptions(tree: Tree, options: ProjectGenerationOptions): Promise<ProjectNameAndRootOptions & {
40
- projectNameAndRootFormat: ProjectNameAndRootFormat;
41
- }>;
42
- /**
43
- * Function for setting cwd during testing
44
- */
45
- 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,72 +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
+ const enquirer_1 = require("enquirer");
5
6
  const devkit_exports_1 = require("nx/src/devkit-exports");
6
7
  const path_1 = require("path");
7
8
  async function determineProjectNameAndRootOptions(tree, options) {
8
- validateName(options.name);
9
- const projectNameAndRootOptions = getProjectNameAndRootOptions(tree, options);
10
- return {
11
- ...projectNameAndRootOptions,
12
- projectNameAndRootFormat: 'as-provided',
13
- };
14
- }
15
- function validateName(name) {
16
- /**
17
- * Matches two types of project names:
18
- *
19
- * 1. Valid npm package names (e.g., '@scope/name' or 'name').
20
- * 2. Names starting with a letter and can contain any character except whitespace and ':'.
21
- *
22
- * The second case is to support the legacy behavior (^[a-zA-Z].*$) with the difference
23
- * that it doesn't allow the ":" character. It was wrong to allow it because it would
24
- * conflict with the notation for tasks.
25
- */
26
- const pattern = '(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$';
27
- const validationRegex = new RegExp(pattern);
28
- if (!validationRegex.test(name)) {
29
- throw new Error(`The project name should match the pattern "${pattern}". The provided value "${name}" does not match.`);
30
- }
31
- }
32
- function getProjectNameAndRootOptions(tree, options) {
33
- const directory = options.directory
34
- ? (0, devkit_exports_1.normalizePath)(options.directory.replace(/^\.?\//, ''))
35
- : undefined;
36
- const { name: asProvidedParsedName, directory: asProvidedParsedDirectory } = parseNameForAsProvided(options.name);
37
- if (asProvidedParsedDirectory && directory) {
38
- throw new Error(`You can't specify both a directory (${options.directory}) and a name with a directory path (${options.name}). ` +
39
- `Please specify either a directory or a name with a directory path.`);
40
- }
41
- const asProvidedOptions = getAsProvidedOptions(tree, {
42
- ...options,
43
- directory: directory ?? asProvidedParsedDirectory,
44
- name: asProvidedParsedName,
45
- });
46
- return asProvidedOptions;
47
- }
48
- 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);
49
14
  let projectSimpleName;
50
15
  let projectFileName;
51
- if (options.name.startsWith('@')) {
52
- const [_scope, ...rest] = options.name.split('/');
16
+ if (name.startsWith('@')) {
17
+ const [_scope, ...rest] = name.split('/');
53
18
  projectFileName = rest.join('-');
54
19
  projectSimpleName = rest.pop();
55
20
  }
56
21
  else {
57
- projectSimpleName = options.name;
58
- projectFileName = options.name;
22
+ projectSimpleName = name;
23
+ projectFileName = name;
59
24
  }
60
25
  let projectRoot;
61
26
  const relativeCwd = getRelativeCwd();
62
- if (options.directory) {
27
+ if (directory) {
63
28
  // append the directory to the current working directory if it doesn't start with it
64
- if (options.directory === relativeCwd ||
65
- options.directory.startsWith(`${relativeCwd}/`)) {
66
- projectRoot = options.directory;
29
+ if (directory === relativeCwd || directory.startsWith(`${relativeCwd}/`)) {
30
+ projectRoot = directory;
67
31
  }
68
32
  else {
69
- projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, options.directory);
33
+ projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, directory);
70
34
  }
71
35
  }
72
36
  else if (options.rootProject) {
@@ -75,29 +39,29 @@ function getAsProvidedOptions(tree, options) {
75
39
  else {
76
40
  projectRoot = relativeCwd;
77
41
  // append the project name to the current working directory if it doesn't end with it
78
- if (!relativeCwd.endsWith(options.name)) {
79
- projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, options.name);
42
+ if (!relativeCwd.endsWith(name)) {
43
+ projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, name);
80
44
  }
81
45
  }
82
46
  let importPath = undefined;
83
47
  if (options.projectType === 'library') {
84
48
  importPath = options.importPath;
85
49
  if (!importPath) {
86
- if (options.name.startsWith('@')) {
87
- importPath = options.name;
50
+ if (name.startsWith('@')) {
51
+ importPath = name;
88
52
  }
89
53
  else {
90
54
  const npmScope = getNpmScope(tree);
91
55
  importPath =
92
56
  projectRoot === '.'
93
57
  ? (0, devkit_exports_1.readJson)(tree, 'package.json').name ??
94
- getImportPath(npmScope, options.name)
95
- : getImportPath(npmScope, options.name);
58
+ getImportPath(npmScope, name)
59
+ : getImportPath(npmScope, name);
96
60
  }
97
61
  }
98
62
  }
99
63
  return {
100
- projectName: options.name,
64
+ projectName: name,
101
65
  names: {
102
66
  projectSimpleName,
103
67
  projectFileName,
@@ -106,6 +70,52 @@ function getAsProvidedOptions(tree, options) {
106
70
  projectRoot,
107
71
  };
108
72
  }
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.`);
96
+ }
97
+ }
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
+ }
117
+ }
118
+ }
109
119
  function getImportPath(npmScope, name) {
110
120
  return npmScope ? `${npmScope === '@' ? '' : '@'}${npmScope}/${name}` : name;
111
121
  }
@@ -129,26 +139,3 @@ function getCwd() {
129
139
  function getRelativeCwd() {
130
140
  return (0, devkit_exports_1.normalizePath)((0, path_1.relative)(devkit_exports_1.workspaceRoot, getCwd())).replace(/\/$/, '');
131
141
  }
132
- /**
133
- * Function for setting cwd during testing
134
- */
135
- function setCwd(path) {
136
- process.env.INIT_CWD = (0, path_1.join)(devkit_exports_1.workspaceRoot, path);
137
- }
138
- function parseNameForAsProvided(rawName) {
139
- const directory = (0, devkit_exports_1.normalizePath)(rawName);
140
- if (rawName.includes('@')) {
141
- const index = directory.lastIndexOf('@');
142
- if (index === 0) {
143
- return { name: rawName, directory: undefined };
144
- }
145
- const name = directory.substring(index);
146
- return { name, directory };
147
- }
148
- if (rawName.includes('/')) {
149
- const index = directory.lastIndexOf('/');
150
- const name = directory.substring(index + 1);
151
- return { name, directory };
152
- }
153
- return { name: rawName, directory: undefined };
154
- }