@nx/web 24.0.0-pr.37405.acdaf31 → 24.0.0-rc.0
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/dist/internal.js +2 -2
- package/dist/src/executors/file-server/file-server.impl.js +38 -16
- package/dist/src/generators/application/application.js +13 -62
- package/dist/src/generators/application/files/app-webpack/webpack.config.js__tmpl__ +5 -12
- package/dist/src/generators/application/schema.d.ts +0 -5
- package/dist/src/generators/application/schema.json +0 -6
- package/dist/src/generators/static-serve/static-serve-configuration.js +1 -0
- package/dist/src/migrations/update-24-0-0/add-ready-when-signal.d.ts +4 -0
- package/dist/src/migrations/update-24-0-0/add-ready-when-signal.js +82 -0
- package/dist/src/migrations/update-24-0-0/add-ready-when-signal.md +71 -0
- package/migrations.json +6 -0
- package/package.json +11 -11
package/dist/internal.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fileServerExecutor = exports.waitForPortOpen = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
// Curated internal API surface for other first-party Nx plugins (e.g. @nx/react,
|
|
6
|
-
// @nx/angular, @nx/rspack, @nx/next
|
|
7
|
-
//
|
|
6
|
+
// @nx/angular, @nx/rspack, @nx/next). Prefer this over deep `@nx/web/src/*`
|
|
7
|
+
// imports — those are not part of the public API.
|
|
8
8
|
var wait_for_port_open_1 = require("./src/utils/wait-for-port-open");
|
|
9
9
|
Object.defineProperty(exports, "waitForPortOpen", { enumerable: true, get: function () { return wait_for_port_open_1.waitForPortOpen; } });
|
|
10
10
|
var file_server_impl_1 = require("./src/executors/file-server/file-server.impl");
|
|
@@ -7,9 +7,11 @@ const child_process_1 = require("child_process");
|
|
|
7
7
|
const pc = tslib_1.__importStar(require("picocolors"));
|
|
8
8
|
const devkit_1 = require("@nx/devkit");
|
|
9
9
|
const fs_1 = require("fs");
|
|
10
|
+
const net_1 = require("net");
|
|
10
11
|
const path_1 = require("path");
|
|
11
12
|
const internal_2 = require("@nx/js/internal");
|
|
12
13
|
const detect_port_1 = tslib_1.__importDefault(require("detect-port"));
|
|
14
|
+
const wait_for_port_open_1 = require("../../utils/wait-for-port-open");
|
|
13
15
|
function getHttpServerArgs(options) {
|
|
14
16
|
const { buildTarget, parallel, host, proxyUrl, ssl, sslCert, sslKey, proxyOptions, watch, spa, cacheSeconds, ...rest } = options;
|
|
15
17
|
const args = [`-c${options.cacheSeconds}`];
|
|
@@ -92,6 +94,27 @@ function getBuildTargetOutputPath(options, context) {
|
|
|
92
94
|
}
|
|
93
95
|
return outputPath;
|
|
94
96
|
}
|
|
97
|
+
/** Whether the server accepts connections before `exited` settles. */
|
|
98
|
+
async function waitUntilListening(port, host, exited) {
|
|
99
|
+
let alive = true;
|
|
100
|
+
exited.then(() => (alive = false));
|
|
101
|
+
// A wildcard address accepts connections on the loopback of its family. `::`
|
|
102
|
+
// also accepts IPv4 ones, and a host may have no ::1, so it gets both probes.
|
|
103
|
+
const probeHosts = host === '0.0.0.0'
|
|
104
|
+
? ['127.0.0.1']
|
|
105
|
+
: host === '::'
|
|
106
|
+
? ['::1', '127.0.0.1']
|
|
107
|
+
: [host];
|
|
108
|
+
while (alive) {
|
|
109
|
+
const connected = await Promise.any(probeHosts.map((probeHost) => (0, wait_for_port_open_1.waitForPortOpen)(port, { host: probeHost, retries: 0 }))).then(() => true, () => false);
|
|
110
|
+
if (connected) {
|
|
111
|
+
// A child that failed to bind the port leaves it to whoever holds it
|
|
112
|
+
return alive;
|
|
113
|
+
}
|
|
114
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
95
118
|
function createFileWatcher(project, changeHandler) {
|
|
96
119
|
return internal_1.daemonClient.registerFileWatcher({
|
|
97
120
|
watchProjects: project ? [project] : 'all',
|
|
@@ -193,6 +216,13 @@ async function* fileServerExecutor(options, context) {
|
|
|
193
216
|
...process.env,
|
|
194
217
|
},
|
|
195
218
|
});
|
|
219
|
+
const exited = new Promise((res) => {
|
|
220
|
+
serve.on('exit', (code, signal) => {
|
|
221
|
+
if (code === null)
|
|
222
|
+
code = (0, internal_1.signalToCode)(signal);
|
|
223
|
+
res({ success: code == 0 });
|
|
224
|
+
});
|
|
225
|
+
});
|
|
196
226
|
const processExitListener = () => {
|
|
197
227
|
serve.kill();
|
|
198
228
|
if (disposeWatch) {
|
|
@@ -212,20 +242,12 @@ async function* fileServerExecutor(options, context) {
|
|
|
212
242
|
serve.stderr.on('data', (chunk) => {
|
|
213
243
|
process.stderr.write(chunk);
|
|
214
244
|
});
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (code == 0) {
|
|
224
|
-
res({ success: true });
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
res({ success: false });
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
});
|
|
245
|
+
// http-server's own default when no `-a` is passed
|
|
246
|
+
const host = options.host || '0.0.0.0';
|
|
247
|
+
if (!(await waitUntilListening(port, host, exited))) {
|
|
248
|
+
return exited;
|
|
249
|
+
}
|
|
250
|
+
const baseUrl = (0, internal_1.getListeningUrl)({ address: host, family: (0, net_1.isIPv6)(host) ? 'IPv6' : 'IPv4', port }, !!options.ssl);
|
|
251
|
+
yield { success: true, baseUrl };
|
|
252
|
+
return exited;
|
|
231
253
|
}
|
|
@@ -79,10 +79,6 @@ function createApplicationFiles(tree, options) {
|
|
|
79
79
|
async function setupBundler(tree, options) {
|
|
80
80
|
const main = (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'src/main.ts');
|
|
81
81
|
const tsConfig = (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'tsconfig.app.json');
|
|
82
|
-
const assets = [
|
|
83
|
-
(0, devkit_1.joinPathFragments)(options.appProjectRoot, 'src/favicon.ico'),
|
|
84
|
-
(0, devkit_1.joinPathFragments)(options.appProjectRoot, 'src/assets'),
|
|
85
|
-
];
|
|
86
82
|
if (options.bundler === 'webpack') {
|
|
87
83
|
const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/webpack', versions_1.nxVersion);
|
|
88
84
|
await configurationGenerator(tree, {
|
|
@@ -94,43 +90,7 @@ async function setupBundler(tree, options) {
|
|
|
94
90
|
devServer: true,
|
|
95
91
|
webpackConfig: (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'webpack.config.js'),
|
|
96
92
|
skipFormat: true,
|
|
97
|
-
addPlugin: options.addPlugin,
|
|
98
93
|
});
|
|
99
|
-
const project = (0, devkit_1.readProjectConfiguration)(tree, options.projectName);
|
|
100
|
-
// Only when asked: @nx/webpack:dev-server already defaults to 4200.
|
|
101
|
-
if (options.port != null && project.targets?.serve) {
|
|
102
|
-
project.targets.serve.options ??= {};
|
|
103
|
-
project.targets.serve.options.port = options.port;
|
|
104
|
-
(0, devkit_1.updateProjectConfiguration)(tree, options.projectName, project);
|
|
105
|
-
}
|
|
106
|
-
if (project.targets?.build) {
|
|
107
|
-
const prodConfig = project.targets.build.configurations.production;
|
|
108
|
-
const buildOptions = project.targets.build.options;
|
|
109
|
-
buildOptions.assets = assets;
|
|
110
|
-
buildOptions.index = (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'src/index.html');
|
|
111
|
-
buildOptions.baseHref = '/';
|
|
112
|
-
buildOptions.styles = [
|
|
113
|
-
(0, devkit_1.joinPathFragments)(options.appProjectRoot, `src/styles.${options.style}`),
|
|
114
|
-
];
|
|
115
|
-
// We can delete that, because this projest is an application
|
|
116
|
-
// and applications have a .babelrc file in their root dir.
|
|
117
|
-
// So Nx will find it and use it
|
|
118
|
-
delete buildOptions.babelUpwardRootMode;
|
|
119
|
-
buildOptions.scripts = [];
|
|
120
|
-
prodConfig.fileReplacements = [
|
|
121
|
-
{
|
|
122
|
-
replace: (0, devkit_1.joinPathFragments)(options.appProjectRoot, `src/environments/environment.ts`),
|
|
123
|
-
with: (0, devkit_1.joinPathFragments)(options.appProjectRoot, `src/environments/environment.prod.ts`),
|
|
124
|
-
},
|
|
125
|
-
];
|
|
126
|
-
prodConfig.optimization = true;
|
|
127
|
-
prodConfig.outputHashing = 'all';
|
|
128
|
-
prodConfig.sourceMap = false;
|
|
129
|
-
prodConfig.namedChunks = false;
|
|
130
|
-
prodConfig.extractLicenses = true;
|
|
131
|
-
prodConfig.vendorChunk = false;
|
|
132
|
-
(0, devkit_1.updateProjectConfiguration)(tree, options.projectName, project);
|
|
133
|
-
}
|
|
134
94
|
// TODO(jack): Flush this out... no bundler should be possible for web but the experience isn't holistic due to missing features (e.g. writing index.html).
|
|
135
95
|
}
|
|
136
96
|
else if (options.bundler === 'none') {
|
|
@@ -193,10 +153,7 @@ function setDefaults(tree, options) {
|
|
|
193
153
|
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
194
154
|
}
|
|
195
155
|
async function applicationGenerator(host, schema) {
|
|
196
|
-
return await applicationGeneratorInternal(host,
|
|
197
|
-
addPlugin: false,
|
|
198
|
-
...schema,
|
|
199
|
-
});
|
|
156
|
+
return await applicationGeneratorInternal(host, schema);
|
|
200
157
|
}
|
|
201
158
|
async function applicationGeneratorInternal(host, schema) {
|
|
202
159
|
const options = await normalizeOptions(host, schema);
|
|
@@ -227,15 +184,14 @@ async function applicationGeneratorInternal(host, schema) {
|
|
|
227
184
|
linter: options.linter,
|
|
228
185
|
project: options.projectName,
|
|
229
186
|
unitTestRunner: options.unitTestRunner,
|
|
230
|
-
addPlugin: options.addPlugin,
|
|
231
187
|
}));
|
|
232
188
|
}
|
|
233
189
|
if (options.linter === 'eslint') {
|
|
234
190
|
const { lintProjectGenerator } = (0, devkit_1.ensurePackage)('@nx/eslint', versions_1.nxVersion);
|
|
235
191
|
// CommonJS `require` instead of dynamic ESM `import`: `ensurePackage`
|
|
236
192
|
// exposes the temp install via `Module._initPaths`, which ESM ignores.
|
|
237
|
-
const {
|
|
238
|
-
enableTypedLinting =
|
|
193
|
+
const { addIgnoresToLintConfig, } = require('@nx/eslint/internal');
|
|
194
|
+
enableTypedLinting = options.enableTypedLinting;
|
|
239
195
|
const lintTask = await lintProjectGenerator(host, {
|
|
240
196
|
linter: options.linter,
|
|
241
197
|
project: options.projectName,
|
|
@@ -245,7 +201,6 @@ async function applicationGeneratorInternal(host, schema) {
|
|
|
245
201
|
unitTestRunner: options.unitTestRunner,
|
|
246
202
|
skipFormat: true,
|
|
247
203
|
enableTypedLinting,
|
|
248
|
-
addPlugin: options.addPlugin,
|
|
249
204
|
});
|
|
250
205
|
tasks.push(lintTask);
|
|
251
206
|
// Add out-tsc ignore pattern when using TS solution setup
|
|
@@ -267,7 +222,6 @@ async function applicationGeneratorInternal(host, schema) {
|
|
|
267
222
|
includeVitest: options.unitTestRunner === 'vitest',
|
|
268
223
|
inSourceTests: options.inSourceTests,
|
|
269
224
|
skipFormat: true,
|
|
270
|
-
addPlugin: options.addPlugin,
|
|
271
225
|
port: options.port,
|
|
272
226
|
});
|
|
273
227
|
tasks.push(viteTask);
|
|
@@ -293,7 +247,6 @@ async function applicationGeneratorInternal(host, schema) {
|
|
|
293
247
|
coverageProvider: 'v8',
|
|
294
248
|
inSourceTests: options.inSourceTests,
|
|
295
249
|
skipFormat: true,
|
|
296
|
-
addPlugin: options.addPlugin,
|
|
297
250
|
compiler: options.compiler,
|
|
298
251
|
});
|
|
299
252
|
tasks.push(vitestTask);
|
|
@@ -326,22 +279,24 @@ async function applicationGeneratorInternal(host, schema) {
|
|
|
326
279
|
port: options.port,
|
|
327
280
|
});
|
|
328
281
|
}
|
|
282
|
+
// Only a --bundler=none app keeps these defaults. It has no `serve` target,
|
|
283
|
+
// so `serve-static` serves local runs too.
|
|
329
284
|
let e2eWebServerInfo = {
|
|
330
285
|
e2eWebServerAddress: `http://localhost:${options.port ?? 4200}`,
|
|
331
|
-
e2eWebServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx run ${options.projectName}:serve`,
|
|
286
|
+
e2eWebServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx run ${options.projectName}:serve-static`,
|
|
332
287
|
e2eCiWebServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx run ${options.projectName}:serve-static`,
|
|
333
288
|
e2eCiBaseUrl: `http://localhost:${options.port ?? 4200}`,
|
|
334
|
-
e2eDevServerTarget: `${options.projectName}:serve`,
|
|
289
|
+
e2eDevServerTarget: `${options.projectName}:serve-static`,
|
|
335
290
|
};
|
|
336
291
|
if (options.bundler === 'webpack') {
|
|
337
292
|
const { getWebpackE2EWebServerInfo } = (0, devkit_1.ensurePackage)('@nx/webpack', versions_1.nxVersion);
|
|
338
|
-
e2eWebServerInfo = await getWebpackE2EWebServerInfo(host, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `webpack.config.js`),
|
|
293
|
+
e2eWebServerInfo = await getWebpackE2EWebServerInfo(host, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `webpack.config.js`),
|
|
339
294
|
// Undefined unless --port was passed, so targetDefaults still apply.
|
|
340
295
|
options.port);
|
|
341
296
|
}
|
|
342
297
|
else if (options.bundler === 'vite') {
|
|
343
298
|
const { getViteE2EWebServerInfo } = (0, devkit_1.ensurePackage)('@nx/vite', versions_1.nxVersion);
|
|
344
|
-
e2eWebServerInfo = await getViteE2EWebServerInfo(host, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `vite.config.ts`), options.
|
|
299
|
+
e2eWebServerInfo = await getViteE2EWebServerInfo(host, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `vite.config.ts`), options.port ?? 4200,
|
|
345
300
|
// An explicit port serves both dev and preview, as in @nx/react:app.
|
|
346
301
|
options.port);
|
|
347
302
|
}
|
|
@@ -420,9 +375,10 @@ async function applicationGeneratorInternal(host, schema) {
|
|
|
420
375
|
js: false,
|
|
421
376
|
linter: options.linter,
|
|
422
377
|
enableTypedLinting,
|
|
423
|
-
webServerCommand: e2eWebServerInfo.
|
|
424
|
-
webServerAddress: e2eWebServerInfo.
|
|
425
|
-
|
|
378
|
+
webServerCommand: e2eWebServerInfo.e2eWebServerCommand,
|
|
379
|
+
webServerAddress: e2eWebServerInfo.e2eWebServerAddress,
|
|
380
|
+
ciWebServerCommand: e2eWebServerInfo.e2eCiWebServerCommand,
|
|
381
|
+
ciWebServerAddress: e2eWebServerInfo.e2eCiBaseUrl,
|
|
426
382
|
});
|
|
427
383
|
tasks.push(playwrightTask);
|
|
428
384
|
}
|
|
@@ -436,7 +392,6 @@ async function applicationGeneratorInternal(host, schema) {
|
|
|
436
392
|
setupFile: 'none',
|
|
437
393
|
compiler: options.compiler,
|
|
438
394
|
skipFormat: true,
|
|
439
|
-
addPlugin: options.addPlugin,
|
|
440
395
|
});
|
|
441
396
|
tasks.push(jestTask);
|
|
442
397
|
}
|
|
@@ -485,10 +440,6 @@ async function normalizeOptions(host, options) {
|
|
|
485
440
|
projectType: 'application',
|
|
486
441
|
directory: options.directory,
|
|
487
442
|
});
|
|
488
|
-
const nxJson = (0, devkit_1.readNxJson)(host);
|
|
489
|
-
const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
490
|
-
nxJson.useInferencePlugins !== false;
|
|
491
|
-
options.addPlugin ??= addPluginDefault;
|
|
492
443
|
const isUsingTsSolutionConfig = (0, internal_2.isUsingTsSolutionSetup)(host);
|
|
493
444
|
const appProjectName = !isUsingTsSolutionConfig || options.name ? projectName : importPath;
|
|
494
445
|
const e2eProjectName = `${appProjectName}-e2e`;
|
|
@@ -25,17 +25,10 @@ module.exports = {
|
|
|
25
25
|
],
|
|
26
26
|
};
|
|
27
27
|
<% } else { %>
|
|
28
|
-
const {
|
|
28
|
+
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
(config) => {
|
|
35
|
-
// Update the webpack config as needed here.
|
|
36
|
-
// e.g. `config.plugins.push(new MyPlugin())`
|
|
37
|
-
config.output.clean = true;
|
|
38
|
-
return config;
|
|
39
|
-
}
|
|
40
|
-
);
|
|
30
|
+
module.exports = {
|
|
31
|
+
output: { clean: true },
|
|
32
|
+
plugins: [new NxAppWebpackPlugin()],
|
|
33
|
+
};
|
|
41
34
|
<% } %>
|
|
@@ -15,12 +15,7 @@ export interface Schema {
|
|
|
15
15
|
linter?: LinterType;
|
|
16
16
|
formatter?: 'none' | 'prettier' | 'oxfmt';
|
|
17
17
|
enableTypedLinting?: boolean;
|
|
18
|
-
/**
|
|
19
|
-
* @deprecated Use `enableTypedLinting` instead. This option will be removed in Nx v24.
|
|
20
|
-
*/
|
|
21
|
-
setParserOptionsProject?: boolean;
|
|
22
18
|
strict?: boolean;
|
|
23
|
-
addPlugin?: boolean;
|
|
24
19
|
useProjectJson?: boolean;
|
|
25
20
|
port?: number;
|
|
26
21
|
}
|
|
@@ -100,12 +100,6 @@
|
|
|
100
100
|
"description": "Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons.",
|
|
101
101
|
"default": false
|
|
102
102
|
},
|
|
103
|
-
"setParserOptionsProject": {
|
|
104
|
-
"type": "boolean",
|
|
105
|
-
"description": "Deprecated alias for `enableTypedLinting`.",
|
|
106
|
-
"default": false,
|
|
107
|
-
"x-deprecated": "Use `enableTypedLinting` instead. This option will be removed in Nx v24."
|
|
108
|
-
},
|
|
109
103
|
"strict": {
|
|
110
104
|
"type": "boolean",
|
|
111
105
|
"description": "Creates an application with strict mode and strict type checking.",
|
|
@@ -71,6 +71,7 @@ function addStaticConfig(tree, opts) {
|
|
|
71
71
|
const staticServeOptions = {
|
|
72
72
|
executor: '@nx/web:file-server',
|
|
73
73
|
dependsOn: [opts.parsedBuildTarget],
|
|
74
|
+
readyWhen: { signal: true },
|
|
74
75
|
options: {
|
|
75
76
|
buildTarget: opts.buildTarget,
|
|
76
77
|
staticFilePath: opts.outputPath,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = update;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
6
|
+
async function update(tree) {
|
|
7
|
+
return (0, internal_1.addReadyWhenSignalToTargets)(tree, ['@nx/web:file-server'], (target, projectName, defaults) => addBuildDependency(tree, target, projectName, defaults));
|
|
8
|
+
}
|
|
9
|
+
// The file server runs its build target before it listens, which would count
|
|
10
|
+
// against the readiness timeout unless the build already ran as a dependency.
|
|
11
|
+
// Returns false when `dependsOn` cannot express that build.
|
|
12
|
+
function addBuildDependency(tree, target, projectName, defaults) {
|
|
13
|
+
const effective = (0, internal_1.mergeTargetConfigurations)(target, defaults ?? undefined, undefined, undefined, undefined, false);
|
|
14
|
+
// `dependsOn` is per target, so a configuration that swaps the build target
|
|
15
|
+
// cannot be covered
|
|
16
|
+
if (Object.values(effective.configurations ?? {}).some((config) => config?.buildTarget !== undefined)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (typeof effective.options?.buildTarget !== 'string') {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
const projects = (0, devkit_1.getProjects)(tree);
|
|
23
|
+
const nodes = Object.fromEntries([...projects].map(([name, data]) => [
|
|
24
|
+
name,
|
|
25
|
+
{ name, type: 'lib', data },
|
|
26
|
+
]));
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
const { buildTarget } = (0, internal_1.resolveNxTokensInOptions)({ buildTarget: effective.options.buildTarget }, { ...projects.get(projectName), name: projectName }, 'buildTarget');
|
|
30
|
+
parsed = (0, devkit_1.parseTargetString)(buildTarget, {
|
|
31
|
+
projectName,
|
|
32
|
+
projectGraph: { nodes, dependencies: {} },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const { project, target: name, configuration } = parsed;
|
|
39
|
+
// `dependsOn` can't name a configuration, so only the build target's default
|
|
40
|
+
// one is covered
|
|
41
|
+
const buildTargetConfig = projects.get(project)?.targets?.[name];
|
|
42
|
+
// Nx merges defaults into a run-commands or run-script target based on its
|
|
43
|
+
// command or script, so only targets with other executors read them
|
|
44
|
+
const buildTargetDefaults = buildTargetConfig?.executor &&
|
|
45
|
+
buildTargetConfig.executor !== 'nx:run-commands' &&
|
|
46
|
+
buildTargetConfig.executor !== 'nx:run-script'
|
|
47
|
+
? (0, internal_1.readApplicableTargetDefaults)(name, buildTargetConfig.executor, (0, devkit_1.readNxJson)(tree)?.targetDefaults, project, nodes[project])
|
|
48
|
+
: null;
|
|
49
|
+
const defaultConfiguration = buildTargetConfig?.defaultConfiguration ??
|
|
50
|
+
buildTargetDefaults?.defaultConfiguration;
|
|
51
|
+
if (configuration !== undefined && configuration !== defaultConfiguration) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// Nx runs the `dependsOn` build in the requested configuration when the build
|
|
55
|
+
// target has it. An inferred build target's configurations are unknown.
|
|
56
|
+
const buildConfigurations = {
|
|
57
|
+
...buildTargetDefaults?.configurations,
|
|
58
|
+
...buildTargetConfig?.configurations,
|
|
59
|
+
};
|
|
60
|
+
const inferredBuildTarget = !buildTargetConfig?.executor && !buildTargetConfig?.command;
|
|
61
|
+
if (Object.keys(effective.configurations ?? {}).some((config) => config !== defaultConfiguration &&
|
|
62
|
+
(inferredBuildTarget || config in buildConfigurations))) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if ((effective.dependsOn ?? []).some((entry) => project === projectName
|
|
66
|
+
? typeof entry === 'string'
|
|
67
|
+
? entry === name
|
|
68
|
+
: entry.target === name && !entry.projects && !entry.dependencies
|
|
69
|
+
: typeof entry === 'string'
|
|
70
|
+
? entry === `${project}:${name}`
|
|
71
|
+
: entry.target === name && [entry.projects].flat().includes(project))) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
const buildDependency = project === projectName ? name : { projects: [project], target: name };
|
|
75
|
+
// A `dependsOn` in the project replaces the inherited one unless it spreads it
|
|
76
|
+
target.dependsOn = target.dependsOn
|
|
77
|
+
? [...target.dependsOn, buildDependency]
|
|
78
|
+
: effective.dependsOn
|
|
79
|
+
? ['...', buildDependency]
|
|
80
|
+
: [buildDependency];
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#### Report when file server targets are ready
|
|
2
|
+
|
|
3
|
+
The `@nx/web:file-server` executor now tells Nx when its server is listening.
|
|
4
|
+
Nx listens for that report only on targets that set `readyWhen: { signal: true }`.
|
|
5
|
+
Tasks that depend on such a target with `waitFor: 'ready'` then start once the server is listening, instead of as soon as the target starts.
|
|
6
|
+
|
|
7
|
+
This migration sets `readyWhen: { signal: true }` on the `@nx/web:file-server` targets in your projects.
|
|
8
|
+
A target without its own `executor` is updated too when a `targetDefaults` entry in `nx.json` gives it `@nx/web:file-server`.
|
|
9
|
+
|
|
10
|
+
The file server runs its `buildTarget` before it listens, and that time counts against the 10-minute readiness timeout.
|
|
11
|
+
So the migration also adds the build target to the target's `dependsOn`, which runs the build first.
|
|
12
|
+
The migration reads `buildTarget`, `configurations`, and `dependsOn` from the target and from the `targetDefaults` entry that applies to it.
|
|
13
|
+
It leaves `dependsOn` unchanged when an entry there already names the build target.
|
|
14
|
+
When the target inherits `dependsOn` and sets none of its own, the migration writes `"..."` before the build target, so the inherited entries still apply.
|
|
15
|
+
|
|
16
|
+
The migration leaves a target unchanged in these cases:
|
|
17
|
+
|
|
18
|
+
- The target already sets `readyWhen`, including `null`.
|
|
19
|
+
- The target sets `continuous: false`, or sets no `continuous` and the `targetDefaults` entry that applies to it sets `continuous: false`. Nx only checks readiness for continuous targets.
|
|
20
|
+
- The `targetDefaults` entry that applies to the target sets `readyWhen`.
|
|
21
|
+
|
|
22
|
+
The migration also leaves a target unchanged when `dependsOn` can't run its build:
|
|
23
|
+
|
|
24
|
+
- A configuration of the target sets `buildTarget`. `dependsOn` applies to every configuration.
|
|
25
|
+
- The `buildTarget` names a configuration of the build target other than its `defaultConfiguration`. `dependsOn` can't name a configuration.
|
|
26
|
+
The migration reads `defaultConfiguration` from the build target in `project.json`, and from `targetDefaults` when that build target runs an executor other than `nx:run-commands` or `nx:run-script`.
|
|
27
|
+
- The target has a configuration that the build target also has, other than the build target's `defaultConfiguration`. When you run the target in that configuration, `dependsOn` builds that configuration, but the file server still builds the `defaultConfiguration`.
|
|
28
|
+
The migration reads the build target's configurations the same way as its `defaultConfiguration`. When the build target isn't in `project.json`, or `project.json` sets neither its `executor` nor its `command`, any configuration of the target leaves it unchanged.
|
|
29
|
+
- Nx can't parse the `buildTarget`, such as an empty string.
|
|
30
|
+
|
|
31
|
+
Set `readyWhen: { signal: true }` on those targets yourself, and add a `dependsOn` entry that builds the app first.
|
|
32
|
+
|
|
33
|
+
#### Sample code changes
|
|
34
|
+
|
|
35
|
+
##### Before
|
|
36
|
+
|
|
37
|
+
```json title="apps/app1/project.json"
|
|
38
|
+
{
|
|
39
|
+
// ...
|
|
40
|
+
"targets": {
|
|
41
|
+
"serve-static": {
|
|
42
|
+
"executor": "@nx/web:file-server",
|
|
43
|
+
"options": {
|
|
44
|
+
"buildTarget": "app1:build",
|
|
45
|
+
"port": 4200
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
##### After
|
|
53
|
+
|
|
54
|
+
```json title="apps/app1/project.json" {10-13}
|
|
55
|
+
{
|
|
56
|
+
// ...
|
|
57
|
+
"targets": {
|
|
58
|
+
"serve-static": {
|
|
59
|
+
"executor": "@nx/web:file-server",
|
|
60
|
+
"options": {
|
|
61
|
+
"buildTarget": "app1:build",
|
|
62
|
+
"port": 4200
|
|
63
|
+
},
|
|
64
|
+
"readyWhen": {
|
|
65
|
+
"signal": true
|
|
66
|
+
},
|
|
67
|
+
"dependsOn": ["build"]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
package/migrations.json
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
"version": "23.0.0-beta.25",
|
|
5
5
|
"description": "Rewrites `@nx/web/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/web`'s exports map. Named imports/exports of public symbols are routed to `@nx/web` and the rest to the new `@nx/web/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/web/internal`.",
|
|
6
6
|
"implementation": "./dist/src/migrations/update-23-0-0/rewrite-internal-subpath-imports"
|
|
7
|
+
},
|
|
8
|
+
"update-24-0-0-add-ready-when-signal": {
|
|
9
|
+
"version": "24.0.0-beta.5",
|
|
10
|
+
"description": "Sets `readyWhen: { signal: true }` on `@nx/web:file-server` targets that get no `readyWhen` from their own configuration or `targetDefaults`, so tasks that depend on them with `waitFor: 'ready'` start once the server is listening. Those targets also get their build target as a dependency.",
|
|
11
|
+
"implementation": "./dist/src/migrations/update-24-0-0/add-ready-when-signal",
|
|
12
|
+
"documentation": "./dist/src/migrations/update-24-0-0/add-ready-when-signal.md"
|
|
7
13
|
}
|
|
8
14
|
},
|
|
9
15
|
"packageJsonUpdates": {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/web",
|
|
3
|
-
"version": "24.0.0-
|
|
3
|
+
"version": "24.0.0-rc.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"files": [
|
|
@@ -79,20 +79,20 @@
|
|
|
79
79
|
"http-server": "^14.1.0",
|
|
80
80
|
"picocolors": "^1.1.0",
|
|
81
81
|
"tslib": "^2.3.0",
|
|
82
|
-
"@nx/devkit": "24.0.0-
|
|
83
|
-
"@nx/js": "24.0.0-
|
|
82
|
+
"@nx/devkit": "24.0.0-rc.0",
|
|
83
|
+
"@nx/js": "24.0.0-rc.0"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"nx": "24.0.0-
|
|
87
|
-
"@nx/vitest": "24.0.0-
|
|
86
|
+
"nx": "24.0.0-rc.0",
|
|
87
|
+
"@nx/vitest": "24.0.0-rc.0"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
|
-
"@nx/cypress": "24.0.0-
|
|
91
|
-
"@nx/eslint": "24.0.0-
|
|
92
|
-
"@nx/jest": "24.0.0-
|
|
93
|
-
"@nx/playwright": "24.0.0-
|
|
94
|
-
"@nx/vite": "24.0.0-
|
|
95
|
-
"@nx/webpack": "24.0.0-
|
|
90
|
+
"@nx/cypress": "24.0.0-rc.0",
|
|
91
|
+
"@nx/eslint": "24.0.0-rc.0",
|
|
92
|
+
"@nx/jest": "24.0.0-rc.0",
|
|
93
|
+
"@nx/playwright": "24.0.0-rc.0",
|
|
94
|
+
"@nx/vite": "24.0.0-rc.0",
|
|
95
|
+
"@nx/webpack": "24.0.0-rc.0"
|
|
96
96
|
},
|
|
97
97
|
"peerDependenciesMeta": {
|
|
98
98
|
"@nx/cypress": {
|