@nx/devkit 20.0.0-canary.20240928-f221a41 → 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.
|
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
|
-
|
5
|
-
|
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
|
11
|
-
validateResolvedProject(
|
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
|
19
|
-
? (0, devkit_exports_1.normalizePath)(options.
|
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
|
-
|
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
|
-
|
38
|
-
if (
|
39
|
-
|
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
|
52
|
-
const
|
53
|
-
(options.suffix ? `${
|
54
|
-
const
|
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:
|
58
|
-
fileName:
|
59
|
-
filePath:
|
60
|
-
project:
|
30
|
+
artifactName: options.name ?? extractedName,
|
31
|
+
directory: directory,
|
32
|
+
fileName: name,
|
33
|
+
filePath: filePath,
|
34
|
+
project: project,
|
61
35
|
};
|
62
36
|
}
|
63
|
-
function validateResolvedProject(
|
37
|
+
function validateResolvedProject(project, normalizedDirectory) {
|
64
38
|
if (project) {
|
65
39
|
return;
|
66
40
|
}
|
67
|
-
|
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
|
98
|
-
|
99
|
-
|
100
|
-
const
|
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
|
-
|
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
|
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
|
-
|
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.
|
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
|
-
|
9
|
-
const
|
10
|
-
|
11
|
-
|
12
|
-
|
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 (
|
52
|
-
const [_scope, ...rest] =
|
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 =
|
58
|
-
projectFileName =
|
22
|
+
projectSimpleName = name;
|
23
|
+
projectFileName = name;
|
59
24
|
}
|
60
25
|
let projectRoot;
|
61
26
|
const relativeCwd = getRelativeCwd();
|
62
|
-
if (
|
27
|
+
if (directory) {
|
63
28
|
// append the directory to the current working directory if it doesn't start with it
|
64
|
-
if (
|
65
|
-
|
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,
|
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(
|
79
|
-
projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd,
|
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 (
|
87
|
-
importPath =
|
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,
|
95
|
-
: getImportPath(npmScope,
|
58
|
+
getImportPath(npmScope, name)
|
59
|
+
: getImportPath(npmScope, name);
|
96
60
|
}
|
97
61
|
}
|
98
62
|
}
|
99
63
|
return {
|
100
|
-
projectName:
|
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
|
-
}
|