@nx/webpack 24.0.0-beta.3 → 24.0.0-beta.4
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/ready-check.d.ts +1 -0
- package/dist/ready-check.js +9 -0
- package/dist/ready-signal.d.ts +1 -0
- package/dist/ready-signal.js +5 -0
- package/dist/src/plugins/nx-webpack-plugin/nx-app-webpack-plugin.d.ts +2 -0
- package/dist/src/plugins/nx-webpack-plugin/nx-app-webpack-plugin.js +6 -0
- package/dist/src/plugins/plugin.js +24 -0
- package/dist/src/plugins/use-legacy-nx-plugin/use-legacy-nx-plugin.js +3 -0
- package/dist/src/utils/dev-server-ready-check.d.ts +48 -0
- package/dist/src/utils/dev-server-ready-check.js +325 -0
- package/dist/src/utils/dev-server-ready-signal.d.ts +29 -0
- package/dist/src/utils/dev-server-ready-signal.js +60 -0
- package/dist/src/utils/webpack/read-webpack-options.d.ts +1 -1
- package/dist/src/utils/webpack/read-webpack-options.js +4 -4
- package/package.json +20 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function waitForDevServer(configPath: string): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForDevServer = waitForDevServer;
|
|
4
|
+
const dev_server_ready_check_1 = require("./src/utils/dev-server-ready-check");
|
|
5
|
+
// The config can leave handles open, such as a type checker's workers
|
|
6
|
+
async function waitForDevServer(configPath) {
|
|
7
|
+
await (0, dev_server_ready_check_1.waitForWebpackDevServer)(configPath);
|
|
8
|
+
process.exit(0);
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NxReadySignalPlugin } from './src/utils/dev-server-ready-signal';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NxReadySignalPlugin = void 0;
|
|
4
|
+
var dev_server_ready_signal_1 = require("./src/utils/dev-server-ready-signal");
|
|
5
|
+
Object.defineProperty(exports, "NxReadySignalPlugin", { enumerable: true, get: function () { return dev_server_ready_signal_1.NxReadySignalPlugin; } });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Compiler } from 'webpack';
|
|
2
2
|
import { NxAppWebpackPluginOptions } from './nx-app-webpack-plugin-options';
|
|
3
|
+
import { READY_SIGNAL_PLUGIN } from '../../utils/dev-server-ready-signal';
|
|
3
4
|
/**
|
|
4
5
|
* This plugin provides features to build Node and Web applications.
|
|
5
6
|
* - TS support (including tsconfig paths)
|
|
@@ -12,6 +13,7 @@ import { NxAppWebpackPluginOptions } from './nx-app-webpack-plugin-options';
|
|
|
12
13
|
*/
|
|
13
14
|
export declare class NxAppWebpackPlugin {
|
|
14
15
|
private readonly options;
|
|
16
|
+
readonly [READY_SIGNAL_PLUGIN] = true;
|
|
15
17
|
constructor(options?: NxAppWebpackPluginOptions);
|
|
16
18
|
apply(compiler: Compiler): void;
|
|
17
19
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var _a;
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
exports.NxAppWebpackPlugin = void 0;
|
|
4
5
|
const normalize_options_1 = require("./lib/normalize-options");
|
|
6
|
+
const dev_server_ready_signal_1 = require("../../utils/dev-server-ready-signal");
|
|
5
7
|
/**
|
|
6
8
|
* This plugin provides features to build Node and Web applications.
|
|
7
9
|
* - TS support (including tsconfig paths)
|
|
@@ -14,6 +16,7 @@ const normalize_options_1 = require("./lib/normalize-options");
|
|
|
14
16
|
*/
|
|
15
17
|
class NxAppWebpackPlugin {
|
|
16
18
|
constructor(options = {}) {
|
|
19
|
+
this[_a] = true;
|
|
17
20
|
// If we're building inferred targets, skip normalizing build options.
|
|
18
21
|
if (!global.NX_GRAPH_CREATION) {
|
|
19
22
|
this.options = (0, normalize_options_1.normalizeOptions)(options);
|
|
@@ -23,6 +26,8 @@ class NxAppWebpackPlugin {
|
|
|
23
26
|
// Lazy-required so a generator can load this without the webpack bundler.
|
|
24
27
|
const { applyBaseConfig } = require('./lib/apply-base-config');
|
|
25
28
|
const { applyWebConfig } = require('./lib/apply-web-config');
|
|
29
|
+
const { applyDevServerReadySignal } = require('../../utils/dev-server-ready-signal');
|
|
30
|
+
applyDevServerReadySignal(compiler);
|
|
26
31
|
// Defaults to 'web' if not specified to match Webpack's default.
|
|
27
32
|
const target = this.options.target ?? compiler.options.target ?? 'web';
|
|
28
33
|
this.options.outputPath ??= compiler.options.output?.path;
|
|
@@ -54,3 +59,4 @@ class NxAppWebpackPlugin {
|
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
exports.NxAppWebpackPlugin = NxAppWebpackPlugin;
|
|
62
|
+
_a = dev_server_ready_signal_1.READY_SIGNAL_PLUGIN;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createNodesV2 = exports.createNodes = void 0;
|
|
4
4
|
const internal_1 = require("@nx/devkit/internal");
|
|
5
|
+
const dev_server_ready_signal_1 = require("../utils/dev-server-ready-signal");
|
|
5
6
|
const devkit_1 = require("@nx/devkit");
|
|
6
7
|
const js_1 = require("@nx/js");
|
|
7
8
|
const internal_2 = require("@nx/js/internal");
|
|
@@ -121,6 +122,7 @@ async function createWebpackTargets(configFilePath, projectRoot, options, contex
|
|
|
121
122
|
},
|
|
122
123
|
},
|
|
123
124
|
};
|
|
125
|
+
const devServerReadyWhen = getDevServerReadyWhen(configFilePath, webpackOptions);
|
|
124
126
|
targets[options.serveTargetName] = {
|
|
125
127
|
continuous: true,
|
|
126
128
|
inputs: [...buildInputs],
|
|
@@ -129,6 +131,7 @@ async function createWebpackTargets(configFilePath, projectRoot, options, contex
|
|
|
129
131
|
cwd: projectRoot,
|
|
130
132
|
env: { NODE_ENV: 'development' },
|
|
131
133
|
},
|
|
134
|
+
readyWhen: devServerReadyWhen,
|
|
132
135
|
metadata: {
|
|
133
136
|
technologies: ['webpack'],
|
|
134
137
|
description: 'Starts Webpack dev server',
|
|
@@ -150,6 +153,7 @@ async function createWebpackTargets(configFilePath, projectRoot, options, contex
|
|
|
150
153
|
cwd: projectRoot,
|
|
151
154
|
env: { NODE_ENV: 'production' },
|
|
152
155
|
},
|
|
156
|
+
readyWhen: devServerReadyWhen,
|
|
153
157
|
metadata: {
|
|
154
158
|
technologies: ['webpack'],
|
|
155
159
|
description: 'Starts Webpack dev server in production mode',
|
|
@@ -172,6 +176,7 @@ async function createWebpackTargets(configFilePath, projectRoot, options, contex
|
|
|
172
176
|
buildTarget: options.buildTargetName,
|
|
173
177
|
spa: true,
|
|
174
178
|
},
|
|
179
|
+
readyWhen: { port: '{options.port}', host: '{options.host ?? localhost}' },
|
|
175
180
|
};
|
|
176
181
|
// for `convert-to-inferred` we need to leave the port undefined or the options will not match
|
|
177
182
|
if (webpackConfig.devServer?.port && webpackConfig.devServer?.port !== 4200) {
|
|
@@ -255,3 +260,22 @@ function normalizeOptions(options) {
|
|
|
255
260
|
watchDepsTargetName: 'watch-deps',
|
|
256
261
|
};
|
|
257
262
|
}
|
|
263
|
+
// A dev server whose config loads NxAppWebpackPlugin or NxReadySignalPlugin reports
|
|
264
|
+
// when it listens. Otherwise the check reads the config when the task runs,
|
|
265
|
+
// with the task's env and options, and waits for the dev server's port.
|
|
266
|
+
function getDevServerReadyWhen(configFilePath, configs) {
|
|
267
|
+
// webpack-cli starts a dev server for each config with devServer. Only the
|
|
268
|
+
// first one to listen would signal, so several get the check that waits
|
|
269
|
+
// for each of them.
|
|
270
|
+
const withDevServer = configs.filter((config) => config.devServer);
|
|
271
|
+
const signals = withDevServer.length === 1 && (0, dev_server_ready_signal_1.sendsReadySignal)(withDevServer[0]);
|
|
272
|
+
return signals
|
|
273
|
+
? { signal: true }
|
|
274
|
+
: {
|
|
275
|
+
// Unquoted: the plugin's glob fixes the file name. Quote it if it ever
|
|
276
|
+
// becomes user-chosen, since the shell expands $, backticks and %.
|
|
277
|
+
command: `node -e "require('@nx/webpack/ready-check').waitForDevServer(process.argv[1])" ${(0, path_1.basename)(configFilePath)}`,
|
|
278
|
+
// Each attempt loads the config
|
|
279
|
+
interval: 1000,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
@@ -59,6 +59,9 @@ async function useLegacyNxPlugin(fn, executorOptions) {
|
|
|
59
59
|
apply(compiler) {
|
|
60
60
|
compiler.hooks.beforeCompile.tapPromise('NxLegacyAsyncPlugin', () => {
|
|
61
61
|
return new Promise((resolve) => {
|
|
62
|
+
// Legacy functions are typed to take `Configuration` but mutate the
|
|
63
|
+
// normalized options in place. From webpack 5.111 the two types
|
|
64
|
+
// conflict (`output.copy`), and the public signature can't widen.
|
|
62
65
|
fn(compiler.options, ctx).then((updated) => {
|
|
63
66
|
// Merge options back shallowly since it's a fully functional configuration.
|
|
64
67
|
// Most likely, the user modified the config in place, but this guarantees that updates are applied if users did something like:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface ServeFlags {
|
|
2
|
+
port?: string;
|
|
3
|
+
host?: string;
|
|
4
|
+
configNames: string[];
|
|
5
|
+
configs: string[];
|
|
6
|
+
merge: boolean;
|
|
7
|
+
env: Record<string, unknown>;
|
|
8
|
+
extends: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The flags `webpack-cli serve` gets from the target's options, in the order
|
|
12
|
+
* run-commands passes them: named options, `args`, then overrides.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseServeFlags(options: Record<string, unknown>): ServeFlags;
|
|
15
|
+
/**
|
|
16
|
+
* The `argv` webpack-cli passes to a function config: its commander options,
|
|
17
|
+
* with `env` set by the caller. Values stay strings except `--port`, which
|
|
18
|
+
* webpack-cli converts to a number when it is numeric.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseServeArgv(options: Record<string, unknown>, env: Record<string, unknown>): Record<string, unknown>;
|
|
21
|
+
export type DevServerAddress = {
|
|
22
|
+
port: number;
|
|
23
|
+
host?: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Where a dev server listens, or null when it picks its port or listens on a
|
|
27
|
+
* socket. Returns null for those, since nothing can be probed ahead of them.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getDevServerAddress(devServer: {
|
|
30
|
+
port?: unknown;
|
|
31
|
+
host?: unknown;
|
|
32
|
+
ipc?: unknown;
|
|
33
|
+
} | undefined, flags: ServeFlags, basePort: string | undefined): DevServerAddress | null;
|
|
34
|
+
/** Resolves once every address accepts a TCP connection. */
|
|
35
|
+
export declare function waitForAddresses(addresses: DevServerAddress[]): Promise<void>;
|
|
36
|
+
export declare function readTaskOptions(): Record<string, unknown> & {
|
|
37
|
+
cwd?: string;
|
|
38
|
+
};
|
|
39
|
+
export declare function enterTaskCwd(options: {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
}): void;
|
|
42
|
+
/**
|
|
43
|
+
* The readyWhen of the inferred serve targets when the config doesn't load the
|
|
44
|
+
* signal plugin. Runs in the env and with the options of the serve task, reads
|
|
45
|
+
* the config the way `webpack-cli serve` does, and waits for each dev server it
|
|
46
|
+
* starts.
|
|
47
|
+
*/
|
|
48
|
+
export declare function waitForWebpackDevServer(configPath: string): Promise<void>;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseServeFlags = parseServeFlags;
|
|
4
|
+
exports.parseServeArgv = parseServeArgv;
|
|
5
|
+
exports.getDevServerAddress = getDevServerAddress;
|
|
6
|
+
exports.waitForAddresses = waitForAddresses;
|
|
7
|
+
exports.readTaskOptions = readTaskOptions;
|
|
8
|
+
exports.enterTaskCwd = enterTaskCwd;
|
|
9
|
+
exports.waitForWebpackDevServer = waitForWebpackDevServer;
|
|
10
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
11
|
+
const module_1 = require("module");
|
|
12
|
+
const net_1 = require("net");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const SHORT_FLAGS = {
|
|
15
|
+
c: 'config',
|
|
16
|
+
m: 'merge',
|
|
17
|
+
e: 'extends',
|
|
18
|
+
};
|
|
19
|
+
const VARIADIC_FLAGS = new Set(['config', 'config-name', 'env', 'extends']);
|
|
20
|
+
// The flags read here that webpack-cli accepts as `--name=value`, the form
|
|
21
|
+
// run-commands gives named options
|
|
22
|
+
const NAMED_FLAGS = ['port', 'host', 'config', 'config-name', 'extends'];
|
|
23
|
+
/**
|
|
24
|
+
* The flags `webpack-cli serve` gets from the target's options, in the order
|
|
25
|
+
* run-commands passes them: named options, `args`, then overrides.
|
|
26
|
+
*/
|
|
27
|
+
function parseServeFlags(options) {
|
|
28
|
+
const flags = {
|
|
29
|
+
configNames: [],
|
|
30
|
+
configs: [],
|
|
31
|
+
merge: false,
|
|
32
|
+
env: {},
|
|
33
|
+
extends: [],
|
|
34
|
+
};
|
|
35
|
+
const set = (name, value) => {
|
|
36
|
+
if (name === 'port')
|
|
37
|
+
flags.port = value;
|
|
38
|
+
else if (name === 'host')
|
|
39
|
+
flags.host = value;
|
|
40
|
+
else if (name === 'config-name')
|
|
41
|
+
flags.configNames.push(value);
|
|
42
|
+
else if (name === 'config')
|
|
43
|
+
flags.configs.push(value);
|
|
44
|
+
else if (name === 'extends')
|
|
45
|
+
flags.extends.push(value);
|
|
46
|
+
else if (name === 'env')
|
|
47
|
+
parseEnv(value, flags.env);
|
|
48
|
+
};
|
|
49
|
+
const argv = getServeArgv(options);
|
|
50
|
+
const isValue = (arg) => arg && !arg.startsWith('-');
|
|
51
|
+
for (let i = 0; i < argv.length; i++) {
|
|
52
|
+
const [, long, inline] = argv[i].match(/^--([^=]+)(?:=(.*))?$/) ?? [];
|
|
53
|
+
const [, short, attached] = long
|
|
54
|
+
? []
|
|
55
|
+
: (argv[i].match(/^-(.)(.+)?$/) ?? []);
|
|
56
|
+
const name = long ?? SHORT_FLAGS[short];
|
|
57
|
+
if (!name)
|
|
58
|
+
continue;
|
|
59
|
+
if (name === 'merge') {
|
|
60
|
+
flags.merge = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const value = inline ?? attached ?? (isValue(argv[i + 1]) ? argv[++i] : undefined);
|
|
64
|
+
if (value === undefined)
|
|
65
|
+
continue;
|
|
66
|
+
set(name, value);
|
|
67
|
+
// Commander continues a variadic list only after a value in its own arg
|
|
68
|
+
if (inline === undefined &&
|
|
69
|
+
attached === undefined &&
|
|
70
|
+
VARIADIC_FLAGS.has(name)) {
|
|
71
|
+
while (isValue(argv[i + 1]))
|
|
72
|
+
set(name, argv[++i]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return flags;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The `argv` webpack-cli passes to a function config: its commander options,
|
|
79
|
+
* with `env` set by the caller. Values stay strings except `--port`, which
|
|
80
|
+
* webpack-cli converts to a number when it is numeric.
|
|
81
|
+
*/
|
|
82
|
+
function parseServeArgv(options, env) {
|
|
83
|
+
const argv = {};
|
|
84
|
+
const tokens = getServeArgv(options);
|
|
85
|
+
const isValue = (arg) => arg && !arg.startsWith('-');
|
|
86
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
87
|
+
const [, long, inline] = tokens[i].match(/^--([^=]+)(?:=(.*))?$/) ?? [];
|
|
88
|
+
const [, short, attached] = long
|
|
89
|
+
? []
|
|
90
|
+
: (tokens[i].match(/^-(.)(.+)?$/) ?? []);
|
|
91
|
+
const flag = long ?? SHORT_FLAGS[short];
|
|
92
|
+
if (!flag)
|
|
93
|
+
continue;
|
|
94
|
+
const negated = flag.startsWith('no-');
|
|
95
|
+
const name = (negated ? flag.slice(3) : flag).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
96
|
+
if (negated) {
|
|
97
|
+
argv[name] = false;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const values = [];
|
|
101
|
+
const first = inline ?? attached ?? (isValue(tokens[i + 1]) ? tokens[++i] : undefined);
|
|
102
|
+
if (first !== undefined) {
|
|
103
|
+
values.push(first);
|
|
104
|
+
if (inline === undefined &&
|
|
105
|
+
attached === undefined &&
|
|
106
|
+
VARIADIC_FLAGS.has(flag)) {
|
|
107
|
+
while (isValue(tokens[i + 1]))
|
|
108
|
+
values.push(tokens[++i]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (flag === 'env')
|
|
112
|
+
continue;
|
|
113
|
+
if (VARIADIC_FLAGS.has(flag)) {
|
|
114
|
+
argv[name] = [...(argv[name] ?? []), ...values];
|
|
115
|
+
}
|
|
116
|
+
else if (values.length === 0) {
|
|
117
|
+
argv[name] = true;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const value = values[0];
|
|
121
|
+
argv[name] =
|
|
122
|
+
flag === 'port' && value.trim() !== '' && !isNaN(Number(value))
|
|
123
|
+
? Number(value)
|
|
124
|
+
: value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { ...argv, env };
|
|
128
|
+
}
|
|
129
|
+
function getServeArgv(options) {
|
|
130
|
+
const args = (0, internal_1.splitShellArgs)(Array.isArray(options.args)
|
|
131
|
+
? options.args.join(' ')
|
|
132
|
+
: typeof options.args === 'string'
|
|
133
|
+
? options.args
|
|
134
|
+
: '');
|
|
135
|
+
const unparsed = Array.isArray(options.__unparsed__)
|
|
136
|
+
? options.__unparsed__.map(String)
|
|
137
|
+
: [];
|
|
138
|
+
// run-commands drops a named option that overrides set, or that args set to
|
|
139
|
+
// another value
|
|
140
|
+
const named = NAMED_FLAGS.filter((name) => {
|
|
141
|
+
const value = options[name];
|
|
142
|
+
const fromArgs = flagValues(args, name);
|
|
143
|
+
return (['string', 'number', 'boolean'].includes(typeof value) &&
|
|
144
|
+
flagValues(unparsed, name).length === 0 &&
|
|
145
|
+
(fromArgs.length === 0 ||
|
|
146
|
+
(fromArgs.length === 1 && fromArgs[0] === String(value))));
|
|
147
|
+
}).map((name) => `--${name}=${options[name]}`);
|
|
148
|
+
// run-commands keeps `--env` for itself and drops it from the overrides,
|
|
149
|
+
// with the value it parsed for it
|
|
150
|
+
const env = flagValues(unparsed, 'env');
|
|
151
|
+
const envValue = String(env.length === 1 ? env[0] : env);
|
|
152
|
+
const overrides = unparsed.filter((arg, i) => !/^--env(=|$|\.[^=]*=)/.test(arg) &&
|
|
153
|
+
!(unparsed[i - 1] === '--env' && arg === envValue));
|
|
154
|
+
return [...named, ...args, ...overrides];
|
|
155
|
+
}
|
|
156
|
+
// The values yargs-parser reads for `--name`, as strings
|
|
157
|
+
function flagValues(argv, name) {
|
|
158
|
+
const values = [];
|
|
159
|
+
argv.forEach((arg, i) => {
|
|
160
|
+
if (arg === `--no-${name}`)
|
|
161
|
+
values.push(false);
|
|
162
|
+
else if (arg.startsWith(`--${name}=`)) {
|
|
163
|
+
values.push(arg.slice(name.length + 3));
|
|
164
|
+
}
|
|
165
|
+
else if (arg === `--${name}`) {
|
|
166
|
+
const next = argv[i + 1];
|
|
167
|
+
values.push(next && !next.startsWith('-') ? next : true);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
return values;
|
|
171
|
+
}
|
|
172
|
+
// webpack-cli's `--env` parser, edge cases included
|
|
173
|
+
function parseEnv(value, env) {
|
|
174
|
+
const [allKeys, val] = value.split(/[=](.+)/, 2);
|
|
175
|
+
const splitKeys = allKeys.split(/\.(?!$)/);
|
|
176
|
+
let prevRef = env;
|
|
177
|
+
for (let [index, someKey] of splitKeys.entries()) {
|
|
178
|
+
if (someKey.endsWith('=')) {
|
|
179
|
+
someKey = someKey.slice(0, -1);
|
|
180
|
+
prevRef[someKey] = undefined;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (!prevRef[someKey] || typeof prevRef[someKey] === 'string') {
|
|
184
|
+
prevRef[someKey] = {};
|
|
185
|
+
}
|
|
186
|
+
if (index === splitKeys.length - 1) {
|
|
187
|
+
prevRef[someKey] = typeof val === 'string' ? val : true;
|
|
188
|
+
}
|
|
189
|
+
prevRef = prevRef[someKey];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Where a dev server listens, or null when it picks its port or listens on a
|
|
194
|
+
* socket. Returns null for those, since nothing can be probed ahead of them.
|
|
195
|
+
*/
|
|
196
|
+
function getDevServerAddress(devServer, flags, basePort) {
|
|
197
|
+
if (devServer?.ipc)
|
|
198
|
+
return null;
|
|
199
|
+
const port = flags.port ?? devServer?.port ?? basePort ?? 8080;
|
|
200
|
+
if (port === 'auto' || String(port) === '0')
|
|
201
|
+
return null;
|
|
202
|
+
const number = Number(port);
|
|
203
|
+
if (!Number.isInteger(number) || number <= 0)
|
|
204
|
+
return null;
|
|
205
|
+
const configured = flags.host ?? devServer?.host;
|
|
206
|
+
const host = typeof configured === 'string' ? (0, internal_1.resolveStartupHost)(configured) : undefined;
|
|
207
|
+
return host && !LOOPBACK_HOSTS.has(host)
|
|
208
|
+
? { port: number, host }
|
|
209
|
+
: { port: number };
|
|
210
|
+
}
|
|
211
|
+
// The probe reaches these on loopback. `local-ip` and friends resolve to an
|
|
212
|
+
// interface address first, the way the dev server resolves them.
|
|
213
|
+
const LOOPBACK_HOSTS = new Set(['localhost', '0.0.0.0', '::']);
|
|
214
|
+
/** Resolves once every address accepts a TCP connection. */
|
|
215
|
+
async function waitForAddresses(addresses) {
|
|
216
|
+
await Promise.all(addresses.map(waitForAddress));
|
|
217
|
+
}
|
|
218
|
+
async function waitForAddress({ port, host }) {
|
|
219
|
+
const hosts = host ? [host] : ['127.0.0.1', '::1'];
|
|
220
|
+
while (!(await Promise.any(hosts.map((h) => connects(port, h))).catch(() => false))) {
|
|
221
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function connects(port, host) {
|
|
225
|
+
return new Promise((resolve, reject) => {
|
|
226
|
+
const socket = (0, net_1.createConnection)({ port, host });
|
|
227
|
+
socket.once('connect', () => {
|
|
228
|
+
socket.destroy();
|
|
229
|
+
resolve(true);
|
|
230
|
+
});
|
|
231
|
+
socket.once('error', (e) => {
|
|
232
|
+
socket.destroy();
|
|
233
|
+
reject(e);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function readTaskOptions() {
|
|
238
|
+
try {
|
|
239
|
+
return JSON.parse(process.env.NX_TASK_OPTIONS ?? '{}');
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return {};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// The serve command runs from the target's cwd, so its config does too
|
|
246
|
+
function enterTaskCwd(options) {
|
|
247
|
+
if (options.cwd)
|
|
248
|
+
process.chdir((0, path_1.resolve)(options.cwd));
|
|
249
|
+
}
|
|
250
|
+
async function loadConfigExport(path) {
|
|
251
|
+
if (/\.(ts|mts|cts)$/.test(path)) {
|
|
252
|
+
const { getRootTsConfigPath } = require('@nx/js');
|
|
253
|
+
const { resolveUserDefinedWebpackConfig } = require('./webpack/resolve-user-defined-webpack-config');
|
|
254
|
+
return resolveUserDefinedWebpackConfig(path, getRootTsConfigPath());
|
|
255
|
+
}
|
|
256
|
+
const config = await (0, internal_1.loadConfigFile)(path);
|
|
257
|
+
// webpack-cli also unwraps a default export's own `default`
|
|
258
|
+
return config && typeof config === 'object' && 'default' in config
|
|
259
|
+
? config.default
|
|
260
|
+
: config;
|
|
261
|
+
}
|
|
262
|
+
// webpack-cli merges with its own webpack-merge, and runs from the task's cwd
|
|
263
|
+
function loadWebpackMerge() {
|
|
264
|
+
const cliRequire = (0, module_1.createRequire)(require.resolve('webpack-cli', { paths: [process.cwd()] }));
|
|
265
|
+
return cliRequire('webpack-merge').merge;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The readyWhen of the inferred serve targets when the config doesn't load the
|
|
269
|
+
* signal plugin. Runs in the env and with the options of the serve task, reads
|
|
270
|
+
* the config the way `webpack-cli serve` does, and waits for each dev server it
|
|
271
|
+
* starts.
|
|
272
|
+
*/
|
|
273
|
+
async function waitForWebpackDevServer(configPath) {
|
|
274
|
+
const options = readTaskOptions();
|
|
275
|
+
const flags = parseServeFlags(options);
|
|
276
|
+
// The config's file name, in the cwd of the serve task
|
|
277
|
+
const absoluteConfigPath = (0, path_1.resolve)(options.cwd ?? '.', configPath);
|
|
278
|
+
enterTaskCwd(options);
|
|
279
|
+
// Skips NxAppWebpackPlugin's option normalization, which nothing here uses
|
|
280
|
+
global.NX_GRAPH_CREATION = true;
|
|
281
|
+
const { readWebpackOptions } = require('./webpack/read-webpack-options');
|
|
282
|
+
const env = { WEBPACK_SERVE: true, ...flags.env };
|
|
283
|
+
const argv = parseServeArgv(options, env);
|
|
284
|
+
const load = async (path) => readWebpackOptions(await loadConfigExport((0, path_1.resolve)(path)), env, argv);
|
|
285
|
+
let configs = (await Promise.all((flags.configs.length > 0 ? flags.configs : [absoluteConfigPath]).map(load)))
|
|
286
|
+
.flat()
|
|
287
|
+
.filter((config) => flags.configNames.length === 0 ||
|
|
288
|
+
flags.configNames.includes(config.name));
|
|
289
|
+
let webpackMerge;
|
|
290
|
+
const merge = (...configs) => (webpackMerge ??= loadWebpackMerge())(...configs);
|
|
291
|
+
const resolveExtends = async (config, extendsPaths, seen) => {
|
|
292
|
+
delete config.extends;
|
|
293
|
+
const paths = extendsPaths.map((path) => (0, path_1.resolve)(path));
|
|
294
|
+
if (paths.some((path) => seen.includes(path))) {
|
|
295
|
+
throw new Error('Recursive configuration detected.');
|
|
296
|
+
}
|
|
297
|
+
const extended = (await Promise.all(paths.map(load))).flat();
|
|
298
|
+
if (extended.length > 0)
|
|
299
|
+
config = merge(...extended, config);
|
|
300
|
+
return config.extends
|
|
301
|
+
? resolveExtends(config, [config.extends].flat(), [...seen, ...paths])
|
|
302
|
+
: config;
|
|
303
|
+
};
|
|
304
|
+
configs = await Promise.all(configs.map((config) => flags.extends.length > 0
|
|
305
|
+
? resolveExtends(config, flags.extends, [])
|
|
306
|
+
: config.extends
|
|
307
|
+
? resolveExtends(config, [config.extends].flat(), [])
|
|
308
|
+
: config));
|
|
309
|
+
if (flags.merge) {
|
|
310
|
+
if (configs.length < 2) {
|
|
311
|
+
throw new Error('At least two configurations are required for merge.');
|
|
312
|
+
}
|
|
313
|
+
configs = [configs.reduce((merged, config) => merge(merged, config), {})];
|
|
314
|
+
}
|
|
315
|
+
// webpack-cli starts a dev server for every config with devServer, or for
|
|
316
|
+
// the first config when none has one
|
|
317
|
+
const withDevServer = configs.filter((config) => config.devServer);
|
|
318
|
+
const servers = withDevServer.length > 0 ? withDevServer : configs.slice(0, 1);
|
|
319
|
+
const addresses = servers
|
|
320
|
+
.filter((config) => config.devServer !== false)
|
|
321
|
+
.map((config) => getDevServerAddress(config.devServer, flags, process.env.WEBPACK_DEV_SERVER_BASE_PORT));
|
|
322
|
+
if (addresses.some((address) => address === null))
|
|
323
|
+
return;
|
|
324
|
+
await waitForAddresses(addresses);
|
|
325
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AddressInfo } from 'net';
|
|
2
|
+
import type { Compiler } from 'webpack';
|
|
3
|
+
export declare const READY_SIGNAL_PLUGIN: unique symbol;
|
|
4
|
+
/** Whether the dev server of this config will report its readiness. */
|
|
5
|
+
export declare function sendsReadySignal(config: {
|
|
6
|
+
devServer?: unknown;
|
|
7
|
+
plugins?: unknown[];
|
|
8
|
+
}): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Reports the dev server's address to an Nx task whose readyWhen is `signal`.
|
|
11
|
+
* `webpack-cli serve` reads `devServer` from the compiler after plugins apply,
|
|
12
|
+
* so wrapping `onListening` here reaches the server it starts.
|
|
13
|
+
*/
|
|
14
|
+
export declare function applyDevServerReadySignal(compiler: Compiler): void;
|
|
15
|
+
/** Reports the dev server's address to an Nx task whose readyWhen is `signal`. */
|
|
16
|
+
export declare class NxReadySignalPlugin {
|
|
17
|
+
readonly [READY_SIGNAL_PLUGIN] = true;
|
|
18
|
+
apply(compiler: Compiler): void;
|
|
19
|
+
}
|
|
20
|
+
export declare function getDevServerUrl(server: {
|
|
21
|
+
server?: {
|
|
22
|
+
address(): AddressInfo | string | null;
|
|
23
|
+
};
|
|
24
|
+
options?: {
|
|
25
|
+
server?: string | {
|
|
26
|
+
type?: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}): string | undefined;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.NxReadySignalPlugin = exports.READY_SIGNAL_PLUGIN = void 0;
|
|
5
|
+
exports.sendsReadySignal = sendsReadySignal;
|
|
6
|
+
exports.applyDevServerReadySignal = applyDevServerReadySignal;
|
|
7
|
+
exports.getDevServerUrl = getDevServerUrl;
|
|
8
|
+
// Set on the plugins that report readiness, so inference can find them in a
|
|
9
|
+
// config it has loaded
|
|
10
|
+
exports.READY_SIGNAL_PLUGIN = Symbol.for('@nx/ready-signal-plugin');
|
|
11
|
+
/** Whether the dev server of this config will report its readiness. */
|
|
12
|
+
function sendsReadySignal(config) {
|
|
13
|
+
return (typeof config.devServer === 'object' &&
|
|
14
|
+
!!config.devServer &&
|
|
15
|
+
!!config.plugins?.some((plugin) => plugin?.[exports.READY_SIGNAL_PLUGIN]));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Reports the dev server's address to an Nx task whose readyWhen is `signal`.
|
|
19
|
+
* `webpack-cli serve` reads `devServer` from the compiler after plugins apply,
|
|
20
|
+
* so wrapping `onListening` here reaches the server it starts.
|
|
21
|
+
*/
|
|
22
|
+
function applyDevServerReadySignal(compiler) {
|
|
23
|
+
const devServer = compiler.options.devServer;
|
|
24
|
+
if (!devServer || !process.env.NX_READY_SIGNAL_SOCKET) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { signalTaskReady } = require('@nx/devkit');
|
|
28
|
+
const onListening = devServer.onListening;
|
|
29
|
+
devServer.onListening = (server) => {
|
|
30
|
+
onListening?.(server);
|
|
31
|
+
signalTaskReady({ url: getDevServerUrl(server) });
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Reports the dev server's address to an Nx task whose readyWhen is `signal`. */
|
|
35
|
+
class NxReadySignalPlugin {
|
|
36
|
+
constructor() {
|
|
37
|
+
this[_a] = true;
|
|
38
|
+
}
|
|
39
|
+
apply(compiler) {
|
|
40
|
+
applyDevServerReadySignal(compiler);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.NxReadySignalPlugin = NxReadySignalPlugin;
|
|
44
|
+
_a = exports.READY_SIGNAL_PLUGIN;
|
|
45
|
+
function getDevServerUrl(server) {
|
|
46
|
+
const address = server.server?.address();
|
|
47
|
+
if (!address || typeof address === 'string') {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const type = typeof server.options?.server === 'string'
|
|
51
|
+
? server.options.server
|
|
52
|
+
: server.options?.server?.type;
|
|
53
|
+
const protocol = type && type !== 'http' ? 'https' : 'http';
|
|
54
|
+
const host = address.address === '::' || address.address === '0.0.0.0'
|
|
55
|
+
? 'localhost'
|
|
56
|
+
: address.family === 'IPv6'
|
|
57
|
+
? `[${address.address}]`
|
|
58
|
+
: address.address;
|
|
59
|
+
return `${protocol}://${host}:${address.port}/`;
|
|
60
|
+
}
|
|
@@ -8,4 +8,4 @@ import type { Configuration } from 'webpack';
|
|
|
8
8
|
*
|
|
9
9
|
* @param webpackConfig
|
|
10
10
|
*/
|
|
11
|
-
export declare function readWebpackOptions(webpackConfig: unknown): Promise<Configuration[]>;
|
|
11
|
+
export declare function readWebpackOptions(webpackConfig: unknown, env?: Record<string, unknown>, argv?: Record<string, unknown>): Promise<Configuration[]>;
|
|
@@ -13,7 +13,9 @@ const internal_1 = require("@nx/devkit/internal");
|
|
|
13
13
|
*
|
|
14
14
|
* @param webpackConfig
|
|
15
15
|
*/
|
|
16
|
-
async function readWebpackOptions(webpackConfig
|
|
16
|
+
async function readWebpackOptions(webpackConfig,
|
|
17
|
+
// A function config gets the production build's env unless told otherwise
|
|
18
|
+
env = { production: true }, argv = {}) {
|
|
17
19
|
const configs = [];
|
|
18
20
|
const resolveConfig = async (config) => {
|
|
19
21
|
if ((0, config_1.isNxWebpackComposablePlugin)(config)) {
|
|
@@ -39,9 +41,7 @@ async function readWebpackOptions(webpackConfig) {
|
|
|
39
41
|
});
|
|
40
42
|
}
|
|
41
43
|
else if (typeof config === 'function') {
|
|
42
|
-
const resolved = await config(
|
|
43
|
-
production: true, // we want the production build options
|
|
44
|
-
}, {});
|
|
44
|
+
const resolved = await config(env, argv);
|
|
45
45
|
// If the resolved configuration is an array, resolve each configuration
|
|
46
46
|
return Array.isArray(resolved)
|
|
47
47
|
? (await Promise.all(resolved.map(resolveConfig))).flat()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/webpack",
|
|
3
|
-
"version": "24.0.0-beta.
|
|
3
|
+
"version": "24.0.0-beta.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"files": [
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
],
|
|
39
39
|
"internal": [
|
|
40
40
|
"dist/internal.d.ts"
|
|
41
|
+
],
|
|
42
|
+
"ready-check": [
|
|
43
|
+
"dist/ready-check.d.ts"
|
|
44
|
+
],
|
|
45
|
+
"ready-signal": [
|
|
46
|
+
"dist/ready-signal.d.ts"
|
|
41
47
|
]
|
|
42
48
|
}
|
|
43
49
|
},
|
|
@@ -74,6 +80,16 @@
|
|
|
74
80
|
"types": "./dist/plugin.d.ts",
|
|
75
81
|
"default": "./dist/plugin.js"
|
|
76
82
|
},
|
|
83
|
+
"./ready-check": {
|
|
84
|
+
"@nx/nx-source": "./ready-check.ts",
|
|
85
|
+
"types": "./dist/ready-check.d.ts",
|
|
86
|
+
"default": "./dist/ready-check.js"
|
|
87
|
+
},
|
|
88
|
+
"./ready-signal": {
|
|
89
|
+
"@nx/nx-source": "./ready-signal.ts",
|
|
90
|
+
"types": "./dist/ready-signal.d.ts",
|
|
91
|
+
"default": "./dist/ready-signal.js"
|
|
92
|
+
},
|
|
77
93
|
"./tsconfig-paths-plugin": {
|
|
78
94
|
"@nx/nx-source": "./tsconfig-paths-plugin.ts",
|
|
79
95
|
"types": "./dist/tsconfig-paths-plugin.d.ts",
|
|
@@ -118,8 +134,8 @@
|
|
|
118
134
|
"tslib": "^2.3.0",
|
|
119
135
|
"webpack-node-externals": "^3.0.0",
|
|
120
136
|
"webpack-subresource-integrity": "^5.1.0",
|
|
121
|
-
"@nx/devkit": "24.0.0-beta.
|
|
122
|
-
"@nx/js": "24.0.0-beta.
|
|
137
|
+
"@nx/devkit": "24.0.0-beta.4",
|
|
138
|
+
"@nx/js": "24.0.0-beta.4"
|
|
123
139
|
},
|
|
124
140
|
"peerDependencies": {
|
|
125
141
|
"webpack": "^5.0.0",
|
|
@@ -138,7 +154,7 @@
|
|
|
138
154
|
}
|
|
139
155
|
},
|
|
140
156
|
"devDependencies": {
|
|
141
|
-
"nx": "24.0.0-beta.
|
|
157
|
+
"nx": "24.0.0-beta.4"
|
|
142
158
|
},
|
|
143
159
|
"publishConfig": {
|
|
144
160
|
"access": "public"
|