@nx/devkit 17.2.0-beta.2 → 17.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/devkit",
3
- "version": "17.2.0-beta.2",
3
+ "version": "17.2.0-beta.3",
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,7 @@
34
34
  "tmp": "~0.2.1",
35
35
  "tslib": "^2.3.0",
36
36
  "semver": "7.5.3",
37
- "@nrwl/devkit": "17.2.0-beta.2"
37
+ "@nrwl/devkit": "17.2.0-beta.3"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "nx": ">= 16 <= 18"
@@ -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['derived'].projectName} --directory ${formats['derived'].projectRoot}`;
47
+ function logDeprecationMessage(callingGenerator, formats) {
48
+ logger.warn(stripIndents `
49
+ In Nx 18, 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}`;
@@ -78,113 +82,154 @@ async function determineFormat(tree, formats, callingGenerator) {
78
82
  ],
79
83
  initial: 'as-provided',
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
+ }