@nx/devkit 24.0.0-pr.37405.acdaf31 → 24.0.0-rc.1

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.
Files changed (27) hide show
  1. package/dist/internal.d.ts +7 -3
  2. package/dist/internal.js +26 -7
  3. package/dist/src/generators/add-ready-when-signal.d.ts +17 -0
  4. package/dist/src/generators/add-ready-when-signal.js +81 -0
  5. package/dist/src/generators/e2e-web-server-info-utils.d.ts +1 -1
  6. package/dist/src/generators/e2e-web-server-info-utils.js +1 -15
  7. package/dist/src/generators/plugin-migrations/conversion-warnings.d.ts +5 -0
  8. package/dist/src/generators/plugin-migrations/conversion-warnings.js +13 -0
  9. package/dist/src/generators/plugin-migrations/executor-to-plugin-migrator.d.ts +6 -0
  10. package/dist/src/generators/plugin-migrations/executor-to-plugin-migrator.js +58 -3
  11. package/dist/src/generators/plugin-migrations/remove-executors.d.ts +9 -0
  12. package/dist/src/generators/plugin-migrations/remove-executors.js +111 -0
  13. package/dist/src/utils/ignore-server-inputs-directive.d.ts +5 -0
  14. package/dist/src/utils/ignore-server-inputs-directive.js +12 -0
  15. package/dist/src/utils/nx-directives.d.ts +2 -1
  16. package/dist/src/utils/nx-directives.js +4 -3
  17. package/dist/src/utils/readiness.d.ts +71 -0
  18. package/dist/src/utils/readiness.js +259 -0
  19. package/dist/src/utils/split-shell-args.d.ts +7 -0
  20. package/dist/src/utils/split-shell-args.js +190 -0
  21. package/dist/src/utils/test-file-depends-on.d.ts +4 -4
  22. package/dist/src/utils/test-file-depends-on.js +6 -9
  23. package/dist/src/utils/test-file-directives.d.ts +7 -9
  24. package/dist/src/utils/test-file-directives.js +21 -38
  25. package/package.json +2 -2
  26. package/dist/src/utils/ultracache-directive.d.ts +0 -8
  27. package/dist/src/utils/ultracache-directive.js +0 -19
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * The `@nx-<name>: <value>` directives in the comment block that opens a file,
3
- * by name, with their trimmed values in order. Reading stops at the first line
3
+ * by name, with their trimmed values in order. A bare `@nx-<name>` has the
4
+ * value `''`. Reading stops at the first line
4
5
  * that is not blank, a shebang or a comment.
5
6
  */
6
7
  export declare function parseNxDirectives(content: string): Map<string, string[]>;
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseNxDirectives = parseNxDirectives;
4
- const DIRECTIVE = /^@nx-([\w-]+):(.*)$/;
4
+ const DIRECTIVE = /^@nx-([\w-]+)(?::(.*))?$/;
5
5
  /**
6
6
  * The `@nx-<name>: <value>` directives in the comment block that opens a file,
7
- * by name, with their trimmed values in order. Reading stops at the first line
7
+ * by name, with their trimmed values in order. A bare `@nx-<name>` has the
8
+ * value `''`. Reading stops at the first line
8
9
  * that is not blank, a shebang or a comment.
9
10
  */
10
11
  function parseNxDirectives(content) {
@@ -45,7 +46,7 @@ function parseNxDirectives(content) {
45
46
  }
46
47
  const match = line.trim().match(DIRECTIVE);
47
48
  if (match) {
48
- const [, name, value] = match;
49
+ const [, name, value = ''] = match;
49
50
  const values = directives.get(name);
50
51
  if (values) {
51
52
  values.push(value.trim());
@@ -0,0 +1,71 @@
1
+ import { type AddressInfo } from 'net';
2
+ /**
3
+ * The url of a server listening on `address`, for `emitReadySignal`. A
4
+ * wildcard address maps to localhost. Undefined for a socket or a pipe.
5
+ */
6
+ export declare function getListeningUrl(address: AddressInfo | string | null | undefined, https: boolean): string | undefined;
7
+ /**
8
+ * A `readyWhen` that calls `waitForDevServer` from `module` when the task
9
+ * runs, with the config's file name, when given, and then `literals` as its
10
+ * arguments.
11
+ */
12
+ export declare function getReadyCheckReadyWhen(module: string, configFileName?: string, ...literals: string[]): {
13
+ command: string;
14
+ interval: number;
15
+ };
16
+ export type TaskOptions = Record<string, unknown> & {
17
+ cwd?: string;
18
+ };
19
+ /**
20
+ * The arguments run-commands passes the task's command, as the command
21
+ * receives them: built by run-commands' own code, then split the way the shell
22
+ * splits them. A `{args.*}` command gets them as if appended, which matches a
23
+ * command that passes each value to the flag of its name.
24
+ */
25
+ export declare function getForwardedArgs(options: TaskOptions): string[];
26
+ export interface CacArgsOptions {
27
+ /** Each short flag and the camel-case option it aliases, such as `{ c: 'config' }`. */
28
+ aliases?: Record<string, string>;
29
+ /** The flags that take no value, by any of their names. */
30
+ booleans?: string[];
31
+ }
32
+ /**
33
+ * The options a cac CLI, such as Rsbuild's or Rspack's, parses from `args`,
34
+ * before it applies option defaults and types. Ports cac's `mri()` step:
35
+ * - A flag takes the next argument as its value unless that starts with `-`.
36
+ * - A repeated flag collects its values in an array, and numeric values
37
+ * become numbers.
38
+ * - Every alias takes the value of the alias given first.
39
+ * - The kebab-case and camel-case spellings of an option take the value of
40
+ * the spelling that first appears last.
41
+ *
42
+ * Dotted names stay flat keys, where cac nests them.
43
+ */
44
+ export declare function parseCacArgs(args: string[], { aliases, booleans }?: CacArgsOptions): Record<string, unknown>;
45
+ /**
46
+ * The options a commander 12 command, such as the Rsbuild 1.0 and 1.1 CLI's,
47
+ * parses from `args`, by attribute name, given its option flags such as
48
+ * `'-o --open [url]'`. The last value of an option wins, and no defaults or
49
+ * custom argument parsers apply. Throws where the command exits instead: on an
50
+ * unknown option, `--help` included, or a missing value.
51
+ */
52
+ export declare function parseCommanderArgs(args: string[], declarations: string[]): Record<string, string | true>;
53
+ export interface ServerAddress {
54
+ port: number;
55
+ host?: string;
56
+ }
57
+ /**
58
+ * Where to probe a server, or null when its port is not a positive integer:
59
+ * a server that picks its port or listens on a socket can't be probed ahead.
60
+ */
61
+ export declare function getServerAddress(port: unknown, host: unknown): ServerAddress | null;
62
+ /**
63
+ * Runs a task-time readiness check. Reads the options the task runs with,
64
+ * moves to its cwd, where the CLI resolves its config, and waits until every
65
+ * address `resolveAddresses` returns accepts a TCP connection. Probes nothing
66
+ * when it returns null.
67
+ */
68
+ export declare function runReadyCheck(resolveAddresses: (task: {
69
+ options: TaskOptions;
70
+ root: string;
71
+ }) => Promise<ServerAddress[] | null>): Promise<void>;
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getListeningUrl = getListeningUrl;
4
+ exports.getReadyCheckReadyWhen = getReadyCheckReadyWhen;
5
+ exports.getForwardedArgs = getForwardedArgs;
6
+ exports.parseCacArgs = parseCacArgs;
7
+ exports.parseCommanderArgs = parseCommanderArgs;
8
+ exports.getServerAddress = getServerAddress;
9
+ exports.runReadyCheck = runReadyCheck;
10
+ const net_1 = require("net");
11
+ const devkit_internals_1 = require("nx/src/devkit-internals");
12
+ const path_1 = require("path");
13
+ const split_shell_args_1 = require("./split-shell-args");
14
+ /**
15
+ * The url of a server listening on `address`, for `emitReadySignal`. A
16
+ * wildcard address maps to localhost. Undefined for a socket or a pipe.
17
+ */
18
+ function getListeningUrl(address, https) {
19
+ if (!address || typeof address === 'string') {
20
+ return undefined;
21
+ }
22
+ const host = address.address === '::' || address.address === '0.0.0.0'
23
+ ? 'localhost'
24
+ : address.family === 'IPv6'
25
+ ? `[${address.address}]`
26
+ : address.address;
27
+ return `${https ? 'https' : 'http'}://${host}:${address.port}/`;
28
+ }
29
+ /**
30
+ * A `readyWhen` that calls `waitForDevServer` from `module` when the task
31
+ * runs, with the config's file name, when given, and then `literals` as its
32
+ * arguments.
33
+ */
34
+ function getReadyCheckReadyWhen(module, configFileName, ...literals) {
35
+ // Unquoted in the command, as no quoting works in every shell
36
+ if (configFileName !== undefined && !/^[\w.-]+$/.test(configFileName)) {
37
+ throw new Error(`Unsupported config file name: ${configFileName}`);
38
+ }
39
+ const args = [
40
+ ...(configFileName === undefined ? [] : ['process.argv[1]']),
41
+ ...literals.map((literal) => `'${literal}'`),
42
+ ].join(', ');
43
+ return {
44
+ command: `node -e "require('${module}').waitForDevServer(${args})"${configFileName === undefined ? '' : ` ${configFileName}`}`,
45
+ // Each attempt starts node, and loads the config when there is one
46
+ interval: 1000,
47
+ };
48
+ }
49
+ /**
50
+ * The arguments run-commands passes the task's command, as the command
51
+ * receives them: built by run-commands' own code, then split the way the shell
52
+ * splits them. A `{args.*}` command gets them as if appended, which matches a
53
+ * command that passes each value to the flag of its name.
54
+ */
55
+ function getForwardedArgs(options) {
56
+ const commands = options.command
57
+ ? [{ command: [options.command].flat().join(' ') }]
58
+ : Array.isArray(options.commands)
59
+ ? options.commands.map((c) => typeof c === 'string' ? { command: c } : c)
60
+ : [];
61
+ // Which of several commands starts the server is unknown
62
+ const server = commands.length === 1 ? commands[0] : {};
63
+ const template = server.command?.includes('{args}') && !server.command.includes('{args.')
64
+ ? '{args}'
65
+ : '';
66
+ const forwarded = (0, devkit_internals_1.interpolateArgsIntoCommand)(template, (0, devkit_internals_1.normalizeRunCommandsArgs)({
67
+ ...options,
68
+ __unparsed__: Array.isArray(options.__unparsed__)
69
+ ? options.__unparsed__.map(String)
70
+ : [],
71
+ }), server.forwardAllArgs ?? options.forwardAllArgs ?? true);
72
+ return (0, split_shell_args_1.splitShellArgs)(forwarded);
73
+ }
74
+ /**
75
+ * The options a cac CLI, such as Rsbuild's or Rspack's, parses from `args`,
76
+ * before it applies option defaults and types. Ports cac's `mri()` step:
77
+ * - A flag takes the next argument as its value unless that starts with `-`.
78
+ * - A repeated flag collects its values in an array, and numeric values
79
+ * become numbers.
80
+ * - Every alias takes the value of the alias given first.
81
+ * - The kebab-case and camel-case spellings of an option take the value of
82
+ * the spelling that first appears last.
83
+ *
84
+ * Dotted names stay flat keys, where cac nests them.
85
+ */
86
+ function parseCacArgs(args, { aliases = {}, booleans = [] } = {}) {
87
+ const aliasOf = new Map();
88
+ for (const [short, long] of Object.entries(aliases)) {
89
+ aliasOf.set(short, long).set(long, short);
90
+ }
91
+ const isBoolean = (name) => booleans.includes(name) || booleans.includes(aliasOf.get(name));
92
+ const parsed = {};
93
+ const add = (name, value) => {
94
+ const number = Number(value);
95
+ const typed = typeof value === 'boolean'
96
+ ? value
97
+ : isBoolean(name)
98
+ ? value !== 'false' && value !== ''
99
+ : Number.isFinite(number)
100
+ ? number
101
+ : value;
102
+ parsed[name] =
103
+ parsed[name] === undefined ? typed : [parsed[name], typed].flat();
104
+ };
105
+ let i = 0;
106
+ for (; i < args.length && args[i] !== '--'; i++) {
107
+ const arg = args[i];
108
+ const dashes = arg.match(/^-*/)[0].length;
109
+ if (dashes === 0)
110
+ continue;
111
+ if (arg.startsWith('no-', dashes)) {
112
+ parsed[arg.slice(dashes + 3)] = false;
113
+ continue;
114
+ }
115
+ const equals = arg.indexOf('=', dashes + 1);
116
+ const name = arg.slice(dashes, equals === -1 ? undefined : equals);
117
+ const value = (equals === -1 ? '' : arg.slice(equals + 1)) ||
118
+ i + 1 === args.length ||
119
+ args[i + 1].startsWith('-') ||
120
+ args[++i];
121
+ // `-abc` sets `a` and `b` and gives its value to `c`
122
+ const names = dashes === 2 ? [name] : [...name];
123
+ names.forEach((n, index) => add(n, index + 1 < names.length || value));
124
+ }
125
+ const synced = new Set();
126
+ for (const name of Object.keys(parsed)) {
127
+ const alias = aliasOf.get(name);
128
+ if (alias !== undefined && !synced.has(name)) {
129
+ parsed[alias] = parsed[name];
130
+ synced.add(alias);
131
+ }
132
+ }
133
+ const options = { '--': args.slice(i + 1) };
134
+ for (const [name, value] of Object.entries(parsed)) {
135
+ options[name.replace(/([a-z])-([a-z])/g, (_, a, b) => a + b.toUpperCase())] = value;
136
+ }
137
+ return options;
138
+ }
139
+ /**
140
+ * The options a commander 12 command, such as the Rsbuild 1.0 and 1.1 CLI's,
141
+ * parses from `args`, by attribute name, given its option flags such as
142
+ * `'-o --open [url]'`. The last value of an option wins, and no defaults or
143
+ * custom argument parsers apply. Throws where the command exits instead: on an
144
+ * unknown option, `--help` included, or a missing value.
145
+ */
146
+ function parseCommanderArgs(args, declarations) {
147
+ const options = declarations.map((flags) => {
148
+ const names = flags.split(/[ |,]+/).filter((part) => part.startsWith('-'));
149
+ return {
150
+ flags,
151
+ names,
152
+ key: names
153
+ .at(-1)
154
+ .replace(/^-+/, '')
155
+ .split('-')
156
+ .reduce((key, word) => key + word[0].toUpperCase() + word.slice(1)),
157
+ required: flags.includes('<'),
158
+ optional: flags.includes('['),
159
+ };
160
+ });
161
+ const find = (flag) => options.find((o) => o.names.includes(flag));
162
+ const isOption = (arg) => arg.length > 1 && arg[0] === '-';
163
+ const parsed = {};
164
+ const rest = [...args];
165
+ while (rest.length > 0 && rest[0] !== '--') {
166
+ const arg = rest.shift();
167
+ let option = find(arg);
168
+ if (option?.required) {
169
+ if (rest.length === 0) {
170
+ throw new Error(`option '${option.flags}' argument missing`);
171
+ }
172
+ parsed[option.key] = rest.shift();
173
+ }
174
+ else if (option) {
175
+ parsed[option.key] =
176
+ option.optional && rest.length > 0 && !isOption(rest[0])
177
+ ? rest.shift()
178
+ : true;
179
+ }
180
+ else if (arg.length > 2 &&
181
+ arg[0] === '-' &&
182
+ arg[1] !== '-' &&
183
+ (option = find(arg.slice(0, 2)))) {
184
+ // `-abc` gives `a` the value `bc`, or sets it and goes on with `-bc`
185
+ if (option.required || option.optional) {
186
+ parsed[option.key] = arg.slice(2);
187
+ }
188
+ else {
189
+ parsed[option.key] = true;
190
+ rest.unshift(`-${arg.slice(2)}`);
191
+ }
192
+ }
193
+ else if (/^--[^=]+=/.test(arg) &&
194
+ (option = find(arg.slice(0, arg.indexOf('=')))) &&
195
+ (option.required || option.optional)) {
196
+ parsed[option.key] = arg.slice(arg.indexOf('=') + 1);
197
+ }
198
+ else if (isOption(arg)) {
199
+ throw new Error(`unknown option '${arg}'`);
200
+ }
201
+ }
202
+ return parsed;
203
+ }
204
+ // The probe reaches these on loopback
205
+ const LOOPBACK_HOSTS = new Set(['', 'localhost', '0.0.0.0', '::']);
206
+ /**
207
+ * Where to probe a server, or null when its port is not a positive integer:
208
+ * a server that picks its port or listens on a socket can't be probed ahead.
209
+ */
210
+ function getServerAddress(port, host) {
211
+ const number = Number(port);
212
+ if (!Number.isInteger(number) || number <= 0)
213
+ return null;
214
+ return typeof host === 'string' && !LOOPBACK_HOSTS.has(host)
215
+ ? { port: number, host }
216
+ : { port: number };
217
+ }
218
+ /**
219
+ * Runs a task-time readiness check. Reads the options the task runs with,
220
+ * moves to its cwd, where the CLI resolves its config, and waits until every
221
+ * address `resolveAddresses` returns accepts a TCP connection. Probes nothing
222
+ * when it returns null.
223
+ */
224
+ async function runReadyCheck(resolveAddresses) {
225
+ const options = readTaskOptions();
226
+ const root = (0, path_1.resolve)(options.cwd ?? '.');
227
+ process.chdir(root);
228
+ const addresses = await resolveAddresses({ options, root });
229
+ if (addresses) {
230
+ await Promise.all(addresses.map(waitForAddress));
231
+ }
232
+ }
233
+ function readTaskOptions() {
234
+ try {
235
+ return JSON.parse(process.env.NX_TASK_OPTIONS ?? '{}');
236
+ }
237
+ catch {
238
+ return {};
239
+ }
240
+ }
241
+ async function waitForAddress({ port, host }) {
242
+ const hosts = host ? [host] : ['127.0.0.1', '::1'];
243
+ while (!(await Promise.any(hosts.map((h) => connects(port, h))).catch(() => false))) {
244
+ await new Promise((r) => setTimeout(r, 250));
245
+ }
246
+ }
247
+ function connects(port, host) {
248
+ return new Promise((resolve, reject) => {
249
+ const socket = (0, net_1.createConnection)({ port, host });
250
+ socket.once('connect', () => {
251
+ socket.destroy();
252
+ resolve(true);
253
+ });
254
+ socket.once('error', (e) => {
255
+ socket.destroy();
256
+ reject(e);
257
+ });
258
+ });
259
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Splits a string into the words a command gets when a shell runs it, as
3
+ * run-commands does: with POSIX sh rules, or on Windows with cmd.exe and the C
4
+ * runtime's argv rules. Variables expand from `process.env`; command
5
+ * substitution and globs stay literal.
6
+ */
7
+ export declare function splitShellArgs(value: string): string[];
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.splitShellArgs = splitShellArgs;
4
+ /**
5
+ * Splits a string into the words a command gets when a shell runs it, as
6
+ * run-commands does: with POSIX sh rules, or on Windows with cmd.exe and the C
7
+ * runtime's argv rules. Variables expand from `process.env`; command
8
+ * substitution and globs stay literal.
9
+ */
10
+ function splitShellArgs(value) {
11
+ return process.platform === 'win32'
12
+ ? splitWindowsArgs(applyCmdRules(value))
13
+ : splitPosixArgs(value);
14
+ }
15
+ function splitPosixArgs(value) {
16
+ const words = [];
17
+ let word;
18
+ let quote;
19
+ // An unquoted expansion splits into words on whitespace
20
+ const appendUnquoted = (text) => text.split(/[ \t\n]+/).forEach((part, index) => {
21
+ if (index > 0 && word !== undefined) {
22
+ words.push(word);
23
+ word = undefined;
24
+ }
25
+ if (part)
26
+ word = (word ?? '') + part;
27
+ });
28
+ for (let i = 0; i < value.length; i++) {
29
+ const char = value[i];
30
+ if (quote === "'") {
31
+ if (char === "'")
32
+ quote = undefined;
33
+ else
34
+ word += char;
35
+ }
36
+ else if (char === '\\' && value[i + 1] === '\n') {
37
+ i++;
38
+ }
39
+ else if (char === '$' && expandPosixVariable(value, i)) {
40
+ const expansion = expandPosixVariable(value, i);
41
+ if (quote)
42
+ word += expansion.value;
43
+ else
44
+ appendUnquoted(expansion.value);
45
+ i = expansion.end;
46
+ }
47
+ else if (quote === '"') {
48
+ if (char === '"') {
49
+ quote = undefined;
50
+ }
51
+ else if (char === '\\' && '"\\$`'.includes(value[i + 1])) {
52
+ word += value[++i];
53
+ }
54
+ else {
55
+ word += char;
56
+ }
57
+ }
58
+ else if (/[ \t\n]/.test(char)) {
59
+ if (word !== undefined)
60
+ words.push(word);
61
+ word = undefined;
62
+ }
63
+ else if (char === '"' || char === "'") {
64
+ quote = char;
65
+ word ??= '';
66
+ }
67
+ else if (char === '\\' && i + 1 < value.length) {
68
+ word = (word ?? '') + value[++i];
69
+ }
70
+ else {
71
+ word = (word ?? '') + char;
72
+ }
73
+ }
74
+ if (word !== undefined)
75
+ words.push(word);
76
+ return words;
77
+ }
78
+ /**
79
+ * Expands `$NAME`, `${NAME}`, `${NAME-default}` and `${NAME:-default}` at
80
+ * `start`, returning the value and the index of its last character. Returns
81
+ * undefined for anything else, which stays literal.
82
+ */
83
+ function expandPosixVariable(value, start) {
84
+ const name = value.slice(start + 1).match(/^[A-Za-z_][A-Za-z0-9_]*/)?.[0];
85
+ if (name) {
86
+ return { value: process.env[name] ?? '', end: start + name.length };
87
+ }
88
+ if (value[start + 1] !== '{')
89
+ return undefined;
90
+ let depth = 0;
91
+ for (let i = start + 1; i < value.length; i++) {
92
+ if (value[i] === '{')
93
+ depth++;
94
+ else if (value[i] === '}' && --depth === 0) {
95
+ const match = value
96
+ .slice(start + 2, i)
97
+ .match(/^([A-Za-z_][A-Za-z0-9_]*)(?:(:?)-(.*))?$/s);
98
+ if (!match)
99
+ return undefined;
100
+ const [, braced, colon, fallback] = match;
101
+ const env = process.env[braced];
102
+ const useFallback = fallback !== undefined && (colon ? !env : env === undefined);
103
+ return {
104
+ value: useFallback ? expandPosixText(fallback) : (env ?? ''),
105
+ end: i,
106
+ };
107
+ }
108
+ }
109
+ return undefined;
110
+ }
111
+ function expandPosixText(text) {
112
+ let result = '';
113
+ for (let i = 0; i < text.length; i++) {
114
+ const expansion = text[i] === '$' && expandPosixVariable(text, i);
115
+ if (expansion) {
116
+ result += expansion.value;
117
+ i = expansion.end;
118
+ }
119
+ else {
120
+ result += text[i];
121
+ }
122
+ }
123
+ return result;
124
+ }
125
+ // cmd.exe expands defined %NAME% variables, then drops each ^ outside double
126
+ // quotes and keeps the character after it
127
+ function applyCmdRules(value) {
128
+ const expanded = value.replace(/%([^%\s]+)%/g, (match, name) => process.env[name] ?? match);
129
+ let result = '';
130
+ let quoted = false;
131
+ for (let i = 0; i < expanded.length; i++) {
132
+ const char = expanded[i];
133
+ if (char === '^' && !quoted && i + 1 < expanded.length) {
134
+ result += expanded[++i];
135
+ }
136
+ else {
137
+ if (char === '"')
138
+ quoted = !quoted;
139
+ result += char;
140
+ }
141
+ }
142
+ return result;
143
+ }
144
+ // Backslashes are literal unless they precede a double quote, and single
145
+ // quotes are literal
146
+ function splitWindowsArgs(value) {
147
+ const words = [];
148
+ let word;
149
+ let quoted = false;
150
+ for (let i = 0; i < value.length; i++) {
151
+ const char = value[i];
152
+ if (char === '\\') {
153
+ let count = 1;
154
+ while (value[i + count] === '\\')
155
+ count++;
156
+ i += count - 1;
157
+ if (value[i + 1] === '"') {
158
+ word = (word ?? '') + '\\'.repeat(Math.floor(count / 2));
159
+ if (count % 2 === 1) {
160
+ word += '"';
161
+ i++;
162
+ }
163
+ }
164
+ else {
165
+ word = (word ?? '') + '\\'.repeat(count);
166
+ }
167
+ }
168
+ else if (char === '"') {
169
+ if (quoted && value[i + 1] === '"') {
170
+ word += '"';
171
+ i++;
172
+ }
173
+ else {
174
+ quoted = !quoted;
175
+ word ??= '';
176
+ }
177
+ }
178
+ else if (!quoted && (char === ' ' || char === '\t')) {
179
+ if (word !== undefined)
180
+ words.push(word);
181
+ word = undefined;
182
+ }
183
+ else {
184
+ word = (word ?? '') + char;
185
+ }
186
+ }
187
+ if (word !== undefined)
188
+ words.push(word);
189
+ return words;
190
+ }
@@ -3,9 +3,9 @@ import type { TargetConfiguration } from 'nx/src/devkit-exports';
3
3
  export declare function dependsOnProjects(values: string[]): string[];
4
4
  /**
5
5
  * `target` narrowed to the projects a test file names: its dependency inputs
6
- * (`^…`, `dependencies: true`) become one input selecting `projects`, by the
7
- * first named input among them (else `default`) and kept under ultracache
8
- * (`always: true`). Its `dependsOn` entries naming projects, the servers the
9
- * test talks to, get `inputs: false`.
6
+ * (`^…`, `dependencies: true`) become two inputs selecting `projects` and their
7
+ * dependencies, by the first named input among them (else `default`) and kept
8
+ * under ultracache (`always: true`). Its `dependsOn` entries naming projects,
9
+ * the servers the test talks to, get `inputs: false`.
10
10
  */
11
11
  export declare function scopeTestTargetToProjects(target: TargetConfiguration, projects: string[]): TargetConfiguration;
@@ -18,10 +18,10 @@ function dependsOnProjects(values) {
18
18
  }
19
19
  /**
20
20
  * `target` narrowed to the projects a test file names: its dependency inputs
21
- * (`^…`, `dependencies: true`) become one input selecting `projects`, by the
22
- * first named input among them (else `default`) and kept under ultracache
23
- * (`always: true`). Its `dependsOn` entries naming projects, the servers the
24
- * test talks to, get `inputs: false`.
21
+ * (`^…`, `dependencies: true`) become two inputs selecting `projects` and their
22
+ * dependencies, by the first named input among them (else `default`) and kept
23
+ * under ultracache (`always: true`). Its `dependsOn` entries naming projects,
24
+ * the servers the test talks to, get `inputs: false`.
25
25
  */
26
26
  function scopeTestTargetToProjects(target, projects) {
27
27
  return {
@@ -54,10 +54,7 @@ function scopeDependencyInputs(inputs, projects) {
54
54
  }
55
55
  }
56
56
  }
57
- own.splice(position ?? own.length, 0, {
58
- input: namedInput ?? 'default',
59
- projects,
60
- always: true,
61
- });
57
+ const input = namedInput ?? 'default';
58
+ own.splice(position ?? own.length, 0, { input, projects, always: true }, { input, projects, dependencies: true, always: true });
62
59
  return own;
63
60
  }
@@ -1,12 +1,10 @@
1
1
  import type { TargetConfiguration } from 'nx/src/devkit-exports';
2
- declare const TEST_FILE_DIRECTIVES: readonly ['depends-on', 'ultracache'];
3
- /** An `@nx-<name>` directive a plugin can apply to a test file's target. */
4
- type TestFileDirective = (typeof TEST_FILE_DIRECTIVES)[number];
5
2
  /**
6
- * `target` with the `@nx-*` directives of a workspace-relative test file
7
- * applied, for the directives in `supported`. A directive that is not
8
- * supported, has an unsupported value or lists no projects is skipped with a
9
- * warning.
3
+ * `target` with the `@nx-depends-on` and `@nx-ignore-server-inputs`
4
+ * directives of a workspace-relative test file applied. A directive that lists
5
+ * no projects, has a value it does not take, or is not supported
6
+ * (`ignoreServerInputs: false`) is skipped with a warning.
10
7
  */
11
- export declare function applyTestFileDirectives(target: TargetConfiguration, workspaceRoot: string, file: string, supported: readonly TestFileDirective[]): TargetConfiguration;
12
- export {};
8
+ export declare function applyTestFileDirectives(target: TargetConfiguration, workspaceRoot: string, file: string, options: {
9
+ ignoreServerInputs: boolean;
10
+ }): TargetConfiguration;