nx 22.7.0-beta.12 → 22.7.0-beta.13
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/schemas/nx-schema.json +25 -0
- package/dist/schemas/project-schema.json +25 -0
- package/dist/src/adapter/ngcli-adapter.js +11 -12
- package/dist/src/command-line/daemon/daemon.js +8 -4
- package/dist/src/command-line/graph/graph.js +8 -1
- package/dist/src/command-line/show/show-target/utils.js +4 -3
- package/dist/src/command-line/yargs-utils/shared-options.js +5 -1
- package/dist/src/config/nx-json.d.ts +3 -1
- package/dist/src/config/workspace-json-project-json.d.ts +2 -1
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/client/client.d.ts +10 -3
- package/dist/src/daemon/client/client.js +21 -31
- package/dist/src/daemon/client/daemon-environment.d.ts +4 -0
- package/dist/src/daemon/client/daemon-environment.js +119 -0
- package/dist/src/daemon/client/daemon-socket-messenger.d.ts +2 -5
- package/dist/src/daemon/client/daemon-socket-messenger.js +1 -1
- package/dist/src/daemon/message-types/daemon-message.d.ts +6 -0
- package/dist/src/daemon/message-types/daemon-message.js +6 -0
- package/dist/src/daemon/server/handle-hash-tasks.d.ts +1 -1
- package/dist/src/daemon/server/handle-hash-tasks.js +1 -1
- package/dist/src/daemon/server/handle-outputs-tracking.d.ts +4 -4
- package/dist/src/daemon/server/handle-outputs-tracking.js +11 -11
- package/dist/src/daemon/server/outputs-tracking.d.ts +18 -3
- package/dist/src/daemon/server/outputs-tracking.js +49 -22
- package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +2 -1
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +20 -4
- package/dist/src/daemon/server/server.js +71 -40
- package/dist/src/executors/run-commands/running-tasks.js +2 -3
- package/dist/src/executors/run-script/run-script.impl.js +16 -8
- package/dist/src/hasher/hash-task.d.ts +9 -1
- package/dist/src/hasher/hash-task.js +41 -14
- package/dist/src/hasher/native-task-hasher-impl.d.ts +1 -1
- package/dist/src/hasher/native-task-hasher-impl.js +4 -6
- package/dist/src/hasher/task-hasher.d.ts +20 -9
- package/dist/src/hasher/task-hasher.js +34 -6
- package/dist/src/native/index.d.ts +34 -7
- package/dist/src/native/native-bindings.js +1 -1
- package/dist/src/native/nx.wasi-browser.js +0 -1
- package/dist/src/native/nx.wasi.cjs +0 -1
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/plugins/js/utils/register.js +83 -4
- package/dist/src/project-graph/error-types.js +31 -11
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +9 -0
- package/dist/src/project-graph/plugins/isolation/message-types.d.ts +1 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +9 -0
- package/dist/src/project-graph/plugins/isolation/messaging.js +2 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +36 -68
- package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +6 -0
- package/dist/src/tasks-runner/cache.d.ts +6 -0
- package/dist/src/tasks-runner/cache.js +58 -0
- package/dist/src/tasks-runner/init-tasks-runner.js +13 -7
- package/dist/src/tasks-runner/run-command.js +13 -5
- package/dist/src/tasks-runner/task-env.js +17 -1
- package/dist/src/tasks-runner/task-orchestrator.d.ts +79 -7
- package/dist/src/tasks-runner/task-orchestrator.js +327 -120
- package/dist/src/tasks-runner/tasks-schedule.d.ts +5 -2
- package/dist/src/tasks-runner/tasks-schedule.js +31 -17
- package/dist/src/tasks-runner/utils.d.ts +3 -3
- package/dist/src/tasks-runner/utils.js +5 -6
- package/package.json +12 -12
- package/dist/src/daemon/client/exec-is-server-available.d.ts +0 -1
- package/dist/src/daemon/client/exec-is-server-available.js +0 -11
- package/dist/src/daemon/client/generate-help-output.d.ts +0 -1
- package/dist/src/daemon/client/generate-help-output.js +0 -26
|
@@ -8,6 +8,7 @@ exports.registerTranspiler = registerTranspiler;
|
|
|
8
8
|
exports.registerTsConfigPaths = registerTsConfigPaths;
|
|
9
9
|
exports.getTsNodeCompilerOptions = getTsNodeCompilerOptions;
|
|
10
10
|
const path_1 = require("path");
|
|
11
|
+
const fs_1 = require("fs");
|
|
11
12
|
const logger_1 = require("../../../utils/logger");
|
|
12
13
|
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
13
14
|
const typescript_1 = require("./typescript");
|
|
@@ -49,6 +50,14 @@ const isInvokedByTsx = (() => {
|
|
|
49
50
|
return (requireArgs.some((a) => isTsxPath(a)) ||
|
|
50
51
|
importArgs.some((a) => isTsxPath(a)));
|
|
51
52
|
})();
|
|
53
|
+
/**
|
|
54
|
+
* Whether the current Node.js version supports native TypeScript execution
|
|
55
|
+
* via type stripping (Node 22.6+).
|
|
56
|
+
*
|
|
57
|
+
* process.features.typescript is 'strip' | 'transform' | false in Node 22.6+
|
|
58
|
+
*/
|
|
59
|
+
const nodeSupportsNativeTypescript = !!process.features
|
|
60
|
+
?.typescript;
|
|
52
61
|
/**
|
|
53
62
|
* When process.features.typescript is truthy and the user has opted in via
|
|
54
63
|
* NX_PREFER_NODE_STRIP_TYPES=true, we can skip registering swc-node or ts-node
|
|
@@ -63,8 +72,7 @@ const preferNodeStripTypes = (() => {
|
|
|
63
72
|
if (process.env.NX_PREFER_NODE_STRIP_TYPES !== 'true') {
|
|
64
73
|
return false;
|
|
65
74
|
}
|
|
66
|
-
|
|
67
|
-
return !!process.features?.typescript;
|
|
75
|
+
return nodeSupportsNativeTypescript;
|
|
68
76
|
})();
|
|
69
77
|
function registerTsProject(path, configFilename) {
|
|
70
78
|
// See explanation alongside isInvokedByTsx declaration
|
|
@@ -257,7 +265,11 @@ function registerTranspiler(compilerOptions, tsConfigRaw) {
|
|
|
257
265
|
// Function to register transpiler that returns cleanup function
|
|
258
266
|
const transpiler = getTranspiler(compilerOptions, tsConfigRaw);
|
|
259
267
|
if (!transpiler) {
|
|
260
|
-
|
|
268
|
+
// If Node.js natively supports TypeScript (22.6+), no transpiler is needed.
|
|
269
|
+
// Don't warn — Node will handle .ts files via type stripping.
|
|
270
|
+
if (!nodeSupportsNativeTypescript) {
|
|
271
|
+
warnNoTranspiler();
|
|
272
|
+
}
|
|
261
273
|
return () => { };
|
|
262
274
|
}
|
|
263
275
|
return transpiler();
|
|
@@ -279,7 +291,7 @@ function registerTsConfigPaths(tsConfigPath) {
|
|
|
279
291
|
*/
|
|
280
292
|
if (tsConfigResult.resultType === 'success') {
|
|
281
293
|
return tsconfigPaths.register({
|
|
282
|
-
baseUrl:
|
|
294
|
+
baseUrl: resolvePathsBaseUrl(tsConfigPath),
|
|
283
295
|
paths: tsConfigResult.paths,
|
|
284
296
|
});
|
|
285
297
|
}
|
|
@@ -404,3 +416,70 @@ function getTsNodeCompilerOptions(compilerOptions) {
|
|
|
404
416
|
}
|
|
405
417
|
return result;
|
|
406
418
|
}
|
|
419
|
+
function resolvePathsBaseUrl(tsconfigPath) {
|
|
420
|
+
const chain = [];
|
|
421
|
+
const queue = [tsconfigPath];
|
|
422
|
+
while (queue.length > 0) {
|
|
423
|
+
const absolute = (0, path_1.resolve)(queue.shift());
|
|
424
|
+
const dir = (0, path_1.dirname)(absolute);
|
|
425
|
+
try {
|
|
426
|
+
const raw = JSON.parse((0, fs_1.readFileSync)(absolute, 'utf-8'));
|
|
427
|
+
chain.push({ dir, raw });
|
|
428
|
+
const exts = raw.extends
|
|
429
|
+
? Array.isArray(raw.extends)
|
|
430
|
+
? raw.extends
|
|
431
|
+
: [raw.extends]
|
|
432
|
+
: [];
|
|
433
|
+
for (const ext of exts) {
|
|
434
|
+
const resolved = resolveExtendsPath(ext, dir);
|
|
435
|
+
if (resolved) {
|
|
436
|
+
queue.push(resolved);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
// skip unreadable files
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
let pathsIndex = -1;
|
|
445
|
+
for (let i = 0; i < chain.length; i++) {
|
|
446
|
+
if (chain[i].raw.compilerOptions?.paths &&
|
|
447
|
+
Object.keys(chain[i].raw.compilerOptions.paths).length > 0) {
|
|
448
|
+
pathsIndex = i;
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
const searchStart = pathsIndex >= 0 ? pathsIndex : 0;
|
|
453
|
+
for (let i = searchStart; i < chain.length; i++) {
|
|
454
|
+
if (chain[i].raw.compilerOptions?.baseUrl) {
|
|
455
|
+
return (0, path_1.resolve)(chain[i].dir, chain[i].raw.compilerOptions.baseUrl);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return pathsIndex >= 0
|
|
459
|
+
? chain[pathsIndex].dir
|
|
460
|
+
: (0, path_1.dirname)((0, path_1.resolve)(tsconfigPath));
|
|
461
|
+
}
|
|
462
|
+
function resolveExtendsPath(ext, fromDir) {
|
|
463
|
+
if (ext.startsWith('.') || (0, path_1.isAbsolute)(ext)) {
|
|
464
|
+
let resolved = (0, path_1.resolve)(fromDir, ext);
|
|
465
|
+
if ((0, fs_1.existsSync)(resolved))
|
|
466
|
+
return resolved;
|
|
467
|
+
if (!resolved.endsWith('.json')) {
|
|
468
|
+
resolved += '.json';
|
|
469
|
+
if ((0, fs_1.existsSync)(resolved))
|
|
470
|
+
return resolved;
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
return require.resolve(ext, { paths: [fromDir] });
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
try {
|
|
479
|
+
return require.resolve(`${ext}/tsconfig.json`, { paths: [fromDir] });
|
|
480
|
+
}
|
|
481
|
+
catch {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
@@ -254,19 +254,39 @@ function formatAggregateCreateNodesError(error, pluginName) {
|
|
|
254
254
|
`${errorCount} occurred while processing files for the ${pluginName} plugin${pluginLocation}.`,
|
|
255
255
|
];
|
|
256
256
|
const errorStackLines = [];
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
257
|
+
// Group errors by file so repeated file paths aren't printed multiple times
|
|
258
|
+
const groupedErrors = new Map();
|
|
259
|
+
for (const [file, e] of error.errors) {
|
|
260
|
+
const key = file ?? null;
|
|
261
|
+
if (!groupedErrors.has(key)) {
|
|
262
|
+
groupedErrors.set(key, []);
|
|
262
263
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
264
|
+
groupedErrors.get(key).push(e);
|
|
265
|
+
}
|
|
266
|
+
for (const [file, errors] of groupedErrors) {
|
|
267
|
+
if (file) {
|
|
268
|
+
errorBodyLines.push(` - ${file}:`);
|
|
269
|
+
errorStackLines.push(` - ${file}:`);
|
|
266
270
|
}
|
|
267
|
-
|
|
268
|
-
const
|
|
269
|
-
|
|
271
|
+
for (const e of errors) {
|
|
272
|
+
const messageLines = e.message.split('\n');
|
|
273
|
+
const stackLines = e.stack.split('\n');
|
|
274
|
+
if (file) {
|
|
275
|
+
errorBodyLines.push(...messageLines.map((line) => ` ${line}`));
|
|
276
|
+
errorStackLines.push(...stackLines.map((line) => ` ${line}`));
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
errorBodyLines.push(` - ${messageLines[0]}`, ...messageLines.slice(1).map((line) => ` ${line}`));
|
|
280
|
+
errorStackLines.push(` - ${stackLines[0]}`, ...stackLines.slice(1).map((line) => ` ${line}`));
|
|
281
|
+
}
|
|
282
|
+
if (e.stack && process.env.NX_VERBOSE_LOGGING === 'true') {
|
|
283
|
+
const verboseIndent = file ? ' ' : ' ';
|
|
284
|
+
const innerStackTrace = e.stack
|
|
285
|
+
.split('\n')
|
|
286
|
+
.map((line) => `${verboseIndent}${line}`)
|
|
287
|
+
.join('\n');
|
|
288
|
+
errorStackLines.push(innerStackTrace);
|
|
289
|
+
}
|
|
270
290
|
}
|
|
271
291
|
}
|
|
272
292
|
error.stack = errorStackLines.join('\n');
|
|
@@ -55,6 +55,7 @@ export declare class IsolatedPlugin implements LoadedNxPlugin {
|
|
|
55
55
|
private generateTxId;
|
|
56
56
|
private sendRequest;
|
|
57
57
|
private shutdownIfInactive;
|
|
58
|
+
setWorkerEnv(env: Record<string, string>): Promise<void>;
|
|
58
59
|
notifyPhaseAborted(phase: Phase, lastCompletedHook: Hook): void;
|
|
59
60
|
shutdown(): void;
|
|
60
61
|
private registerProcessMetrics;
|
|
@@ -272,6 +272,15 @@ class IsolatedPlugin {
|
|
|
272
272
|
logger_1.logger.verbose(`[isolated-plugin] shutting down worker for "${this.name}" after ${hookName}`);
|
|
273
273
|
this.shutdown();
|
|
274
274
|
}
|
|
275
|
+
async setWorkerEnv(env) {
|
|
276
|
+
if (!this._alive) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const result = await this.sendRequest('setWorkerEnv', env);
|
|
280
|
+
if (result.success === false) {
|
|
281
|
+
throw result.error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
275
284
|
notifyPhaseAborted(phase, lastCompletedHook) {
|
|
276
285
|
if (this.lifecycle?.notifyPhaseAborted(phase, lastCompletedHook)) {
|
|
277
286
|
this.shutdownIfInactive(lastCompletedHook);
|
|
@@ -55,5 +55,5 @@ export type AllResults<TDefs extends MessageDefs> = {
|
|
|
55
55
|
export type Handlers<TDefs extends MessageDefs> = {
|
|
56
56
|
[K in keyof TDefs & string]: (payload: TDefs[K]['payload']) => TDefs[K] extends {
|
|
57
57
|
result: unknown;
|
|
58
|
-
} ? MaybePromise<TDefs[K]['result']
|
|
58
|
+
} ? MaybePromise<TDefs[K]['result']> : MaybePromise<void>;
|
|
59
59
|
};
|
|
@@ -97,6 +97,15 @@ type PluginMessageDefs = DefineMessages<{
|
|
|
97
97
|
error: Error;
|
|
98
98
|
};
|
|
99
99
|
};
|
|
100
|
+
setWorkerEnv: {
|
|
101
|
+
payload: Record<string, string>;
|
|
102
|
+
result: {
|
|
103
|
+
success: true;
|
|
104
|
+
} | {
|
|
105
|
+
success: false;
|
|
106
|
+
error: Error;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
100
109
|
}>;
|
|
101
110
|
/** Union of all plugin worker message types */
|
|
102
111
|
export type PluginWorkerMessage = AllMessages<PluginMessageDefs>;
|
|
@@ -15,6 +15,7 @@ const MESSAGE_TYPES = [
|
|
|
15
15
|
'createMetadata',
|
|
16
16
|
'preTasksExecution',
|
|
17
17
|
'postTasksExecution',
|
|
18
|
+
'setWorkerEnv',
|
|
18
19
|
];
|
|
19
20
|
const RESULT_TYPES = [
|
|
20
21
|
'loadResult',
|
|
@@ -23,6 +24,7 @@ const RESULT_TYPES = [
|
|
|
23
24
|
'createMetadataResult',
|
|
24
25
|
'preTasksExecutionResult',
|
|
25
26
|
'postTasksExecutionResult',
|
|
27
|
+
'setWorkerEnvResult',
|
|
26
28
|
];
|
|
27
29
|
function isPluginWorkerMessage(message) {
|
|
28
30
|
return (typeof message === 'object' &&
|
|
@@ -8,8 +8,8 @@ const serializable_error_1 = require("../../../utils/serializable-error");
|
|
|
8
8
|
const messaging_1 = require("./messaging");
|
|
9
9
|
const fs_1 = require("fs");
|
|
10
10
|
const net_1 = require("net");
|
|
11
|
-
require("../../../utils/perf-logging");
|
|
12
11
|
const analytics_1 = require("../../../analytics");
|
|
12
|
+
require("../../../utils/perf-logging");
|
|
13
13
|
const environment = process.env;
|
|
14
14
|
(0, analytics_1.startAnalytics)();
|
|
15
15
|
node_perf_hooks_1.performance.mark(`plugin worker ${process.pid} code loading -- end`);
|
|
@@ -41,7 +41,7 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
41
41
|
load: async ({ plugin: pluginConfiguration, root, name, pluginPath, shouldRegisterTSTranspiler, }) => {
|
|
42
42
|
loadErrorTimeout?.clear();
|
|
43
43
|
process.chdir(root);
|
|
44
|
-
|
|
44
|
+
return withErrorHandling(async () => {
|
|
45
45
|
const { loadResolvedNxPluginAsync } = await Promise.resolve(require(require.resolve('../load-resolved-plugin')));
|
|
46
46
|
// Register the ts-transpiler if we are pointing to a
|
|
47
47
|
// plain ts file that's not part of a plugin project
|
|
@@ -62,74 +62,30 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
62
62
|
hasPostTasksExecution: 'postTasksExecution' in plugin && !!plugin.postTasksExecution,
|
|
63
63
|
success: true,
|
|
64
64
|
};
|
|
65
|
-
}
|
|
66
|
-
catch (e) {
|
|
67
|
-
return {
|
|
68
|
-
success: false,
|
|
69
|
-
error: (0, serializable_error_1.createSerializableError)(e),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
createNodes: async function createNodes({ configFiles, context }) {
|
|
74
|
-
try {
|
|
75
|
-
const result = await plugin.createNodes[1](configFiles, context);
|
|
76
|
-
return { result, success: true };
|
|
77
|
-
}
|
|
78
|
-
catch (e) {
|
|
79
|
-
return {
|
|
80
|
-
success: false,
|
|
81
|
-
error: (0, serializable_error_1.createSerializableError)(e),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
createDependencies: async function createDependencies({ context }) {
|
|
86
|
-
try {
|
|
87
|
-
const result = await plugin.createDependencies(context);
|
|
88
|
-
return { dependencies: result, success: true };
|
|
89
|
-
}
|
|
90
|
-
catch (e) {
|
|
91
|
-
return {
|
|
92
|
-
success: false,
|
|
93
|
-
error: (0, serializable_error_1.createSerializableError)(e),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
createMetadata: async function createMetadata({ graph, context }) {
|
|
98
|
-
try {
|
|
99
|
-
const result = await plugin.createMetadata(graph, context);
|
|
100
|
-
return { metadata: result, success: true };
|
|
101
|
-
}
|
|
102
|
-
catch (e) {
|
|
103
|
-
return {
|
|
104
|
-
success: false,
|
|
105
|
-
error: (0, serializable_error_1.createSerializableError)(e),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
preTasksExecution: async ({ context }) => {
|
|
110
|
-
try {
|
|
111
|
-
const mutations = await plugin.preTasksExecution?.(context);
|
|
112
|
-
return { success: true, mutations };
|
|
113
|
-
}
|
|
114
|
-
catch (e) {
|
|
115
|
-
return {
|
|
116
|
-
success: false,
|
|
117
|
-
error: (0, serializable_error_1.createSerializableError)(e),
|
|
118
|
-
};
|
|
119
|
-
}
|
|
65
|
+
});
|
|
120
66
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
67
|
+
createNodes: async ({ configFiles, context }) => withErrorHandling(async () => {
|
|
68
|
+
const result = await plugin.createNodes[1](configFiles, context);
|
|
69
|
+
return { result, success: true };
|
|
70
|
+
}),
|
|
71
|
+
createDependencies: async ({ context }) => withErrorHandling(async () => {
|
|
72
|
+
const result = await plugin.createDependencies(context);
|
|
73
|
+
return { dependencies: result, success: true };
|
|
74
|
+
}),
|
|
75
|
+
createMetadata: async ({ graph, context }) => withErrorHandling(async () => {
|
|
76
|
+
const result = await plugin.createMetadata(graph, context);
|
|
77
|
+
return { metadata: result, success: true };
|
|
78
|
+
}),
|
|
79
|
+
preTasksExecution: async ({ context }) => withErrorHandling(async () => {
|
|
80
|
+
const mutations = await plugin.preTasksExecution?.(context);
|
|
81
|
+
return { success: true, mutations };
|
|
82
|
+
}),
|
|
83
|
+
postTasksExecution: async ({ context }) => withErrorHandling(() => plugin.postTasksExecution?.(context)),
|
|
84
|
+
setWorkerEnv: (env) => withErrorHandling(() => {
|
|
85
|
+
for (const envKey in env) {
|
|
86
|
+
process.env[envKey] = env[envKey];
|
|
125
87
|
}
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
success: false,
|
|
129
|
-
error: (0, serializable_error_1.createSerializableError)(e),
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
},
|
|
88
|
+
}),
|
|
133
89
|
});
|
|
134
90
|
}));
|
|
135
91
|
// When the host disconnects, clean up and exit.
|
|
@@ -144,6 +100,18 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
144
100
|
});
|
|
145
101
|
server.listen(socketPath);
|
|
146
102
|
logger_1.logger.verbose(`[plugin-worker] "${expectedPluginName}" (pid: ${process.pid}) listening on ${socketPath}`);
|
|
103
|
+
async function withErrorHandling(cb) {
|
|
104
|
+
try {
|
|
105
|
+
const result = await cb();
|
|
106
|
+
return result ?? { success: true };
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
error: (0, serializable_error_1.createSerializableError)(e),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
147
115
|
function setErrorTimeout(timeoutMs, errorMessage) {
|
|
148
116
|
if (environment.NX_PLUGIN_NO_TIMEOUTS === 'true') {
|
|
149
117
|
return;
|
|
@@ -31,5 +31,11 @@ export declare class LoadedNxPlugin {
|
|
|
31
31
|
* abort (e.g. 'createNodes').
|
|
32
32
|
*/
|
|
33
33
|
notifyPhaseAborted?(phase: string, lastCompletedHook: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Forwards updated environment variables to the plugin worker process.
|
|
36
|
+
* Only meaningful for isolated plugins; in-process plugins share the
|
|
37
|
+
* daemon's process.env automatically.
|
|
38
|
+
*/
|
|
39
|
+
setWorkerEnv?(env: Record<string, string>): Promise<void>;
|
|
34
40
|
constructor(plugin: NxPluginV2, pluginDefinition: PluginConfiguration, index?: number);
|
|
35
41
|
}
|
|
@@ -26,6 +26,9 @@ export declare class DbCache {
|
|
|
26
26
|
});
|
|
27
27
|
init(): Promise<void>;
|
|
28
28
|
get(task: Task): Promise<CachedResult | null>;
|
|
29
|
+
getBatch(tasks: Task[]): Promise<Map<string, CachedResult & {
|
|
30
|
+
remote: boolean;
|
|
31
|
+
}>>;
|
|
29
32
|
getUsedCacheSpace(): number;
|
|
30
33
|
private applyRemoteCacheResults;
|
|
31
34
|
put(task: Task, terminalOutput: string | null, outputs: string[], code: number): Promise<void>;
|
|
@@ -54,6 +57,9 @@ export declare class Cache {
|
|
|
54
57
|
constructor(options: DefaultTasksRunnerOptions);
|
|
55
58
|
removeOldCacheRecords(): void;
|
|
56
59
|
get(task: Task): Promise<CachedResult | null>;
|
|
60
|
+
getBatch(tasks: Task[]): Promise<Map<string, CachedResult & {
|
|
61
|
+
remote: boolean;
|
|
62
|
+
}>>;
|
|
57
63
|
put(task: Task, terminalOutput: string | null, outputs: string[], code: number): Promise<void>;
|
|
58
64
|
copyFilesFromCache(hash: string, cachedResult: CachedResult, outputs: string[]): Promise<void>;
|
|
59
65
|
temporaryOutputPath(task: Task): string;
|
|
@@ -97,6 +97,54 @@ class DbCache {
|
|
|
97
97
|
return null;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
+
async getBatch(tasks) {
|
|
101
|
+
const results = new Map();
|
|
102
|
+
if (tasks.length === 0)
|
|
103
|
+
return results;
|
|
104
|
+
// Single-task fast path: direct primary-key lookup beats rarray
|
|
105
|
+
// vtable overhead when there's nothing to amortize over.
|
|
106
|
+
if (tasks.length === 1) {
|
|
107
|
+
const res = await this.get(tasks[0]);
|
|
108
|
+
if (res)
|
|
109
|
+
results.set(tasks[0].hash, res);
|
|
110
|
+
return results;
|
|
111
|
+
}
|
|
112
|
+
const hashes = tasks.map((t) => t.hash);
|
|
113
|
+
// 1. Local: one rarray SQL query + parallel terminal output reads.
|
|
114
|
+
// batchResults is index-aligned with tasks, so we walk in lockstep.
|
|
115
|
+
const batchResults = this.cache.getBatch(hashes);
|
|
116
|
+
const remoteMisses = [];
|
|
117
|
+
for (const [i, task] of tasks.entries()) {
|
|
118
|
+
const res = batchResults[i];
|
|
119
|
+
if (res) {
|
|
120
|
+
results.set(task.hash, {
|
|
121
|
+
...res,
|
|
122
|
+
terminalOutput: res.terminalOutput ?? '',
|
|
123
|
+
remote: false,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
else if (this.remoteCache) {
|
|
127
|
+
remoteMisses.push(task);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// 2. Remote: parallel HTTP retrievals for anything the local SQL missed.
|
|
131
|
+
// The RemoteCache interface has no batch endpoint, so this is just
|
|
132
|
+
// Promise.all over individual retrieve() calls.
|
|
133
|
+
if (remoteMisses.length > 0) {
|
|
134
|
+
await Promise.all(remoteMisses.map(async (task) => {
|
|
135
|
+
const res = await this.remoteCache.retrieve(task.hash, this.cache.cacheDirectory);
|
|
136
|
+
if (res) {
|
|
137
|
+
this.applyRemoteCacheResults(task.hash, res, task.outputs);
|
|
138
|
+
results.set(task.hash, {
|
|
139
|
+
...res,
|
|
140
|
+
terminalOutput: res.terminalOutput ?? '',
|
|
141
|
+
remote: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
100
148
|
getUsedCacheSpace() {
|
|
101
149
|
return this.cache.getCacheSize();
|
|
102
150
|
}
|
|
@@ -304,6 +352,16 @@ class Cache {
|
|
|
304
352
|
return null;
|
|
305
353
|
}
|
|
306
354
|
}
|
|
355
|
+
async getBatch(tasks) {
|
|
356
|
+
// Legacy file-based cache has no native batch support — loop in parallel.
|
|
357
|
+
const results = new Map();
|
|
358
|
+
const entries = await Promise.all(tasks.map(async (t) => ({ task: t, res: await this.get(t) })));
|
|
359
|
+
for (const { task, res } of entries) {
|
|
360
|
+
if (res)
|
|
361
|
+
results.set(task.hash, res);
|
|
362
|
+
}
|
|
363
|
+
return results;
|
|
364
|
+
}
|
|
307
365
|
async put(task, terminalOutput, outputs, code) {
|
|
308
366
|
return tryAndRetry(async () => {
|
|
309
367
|
/**
|
|
@@ -114,7 +114,7 @@ async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson
|
|
|
114
114
|
const orchestrator = await createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle);
|
|
115
115
|
let groupId = 0;
|
|
116
116
|
let nextBatch = orchestrator.nextBatch();
|
|
117
|
-
|
|
117
|
+
const batchResults = [];
|
|
118
118
|
/**
|
|
119
119
|
* Set of task ids that were part of batches
|
|
120
120
|
*/
|
|
@@ -126,12 +126,18 @@ async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson
|
|
|
126
126
|
batchResults.push(orchestrator.applyFromCacheOrRunBatch(true, nextBatch, groupId++));
|
|
127
127
|
nextBatch = orchestrator.nextBatch();
|
|
128
128
|
}
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
const discreteTasks = tasks.filter((task) => !batchTasks.has(task.id));
|
|
130
|
+
// Bulk-resolve every discrete task's cache state in one shot —
|
|
131
|
+
// single SQL call plus parallel remote retrievals. Batches kicked
|
|
132
|
+
// off above continue running concurrently while we await this.
|
|
133
|
+
const cacheHits = await orchestrator.resolveCachedTasks(true, discreteTasks, groupId++);
|
|
134
|
+
const cacheHitsById = new Map(cacheHits.map((h) => [h.task.id, h]));
|
|
135
|
+
const taskResults = discreteTasks.map(async (task) => {
|
|
136
|
+
const hit = cacheHitsById.get(task.id);
|
|
137
|
+
if (hit)
|
|
138
|
+
return [hit];
|
|
139
|
+
return [await orchestrator.runTaskDirectly(true, task, groupId++)];
|
|
140
|
+
});
|
|
135
141
|
return [...batchResults, ...taskResults];
|
|
136
142
|
}
|
|
137
143
|
async function runContinuousTasks(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
|
|
@@ -674,7 +674,7 @@ async function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nx
|
|
|
674
674
|
}
|
|
675
675
|
return hasher.hashTask(task, taskGraph_, env);
|
|
676
676
|
},
|
|
677
|
-
hashTasks(
|
|
677
|
+
hashTasks(tasks, taskGraph_, envOrPerTaskEnvs) {
|
|
678
678
|
if (!taskGraph_) {
|
|
679
679
|
output_1.output.warn({
|
|
680
680
|
title: `TaskGraph is now required as an argument to hashTasks`,
|
|
@@ -685,7 +685,7 @@ async function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nx
|
|
|
685
685
|
});
|
|
686
686
|
taskGraph_ = taskGraph;
|
|
687
687
|
}
|
|
688
|
-
if (!
|
|
688
|
+
if (!envOrPerTaskEnvs) {
|
|
689
689
|
output_1.output.warn({
|
|
690
690
|
title: `The environment variables are now required as an argument to hashTasks`,
|
|
691
691
|
bodyLines: [
|
|
@@ -693,9 +693,11 @@ async function invokeTasksRunner({ tasks, projectGraph, taskGraph, lifeCycle, nx
|
|
|
693
693
|
'This will result in an error in Nx 20',
|
|
694
694
|
],
|
|
695
695
|
});
|
|
696
|
-
|
|
696
|
+
envOrPerTaskEnvs = process.env;
|
|
697
697
|
}
|
|
698
|
-
|
|
698
|
+
// hasher.hashTasks accepts either legacy single-env or the new
|
|
699
|
+
// per-task-env shape and normalizes internally.
|
|
700
|
+
return hasher.hashTasks(tasks, taskGraph_, envOrPerTaskEnvs);
|
|
699
701
|
},
|
|
700
702
|
},
|
|
701
703
|
daemon: client_1.daemonClient,
|
|
@@ -816,7 +818,13 @@ function getTasksRunnerPath(runner, nxJson) {
|
|
|
816
818
|
process.env.NX_CLOUD_ACCESS_TOKEN ||
|
|
817
819
|
// Nx Cloud ID specified in nxJson
|
|
818
820
|
nxJson.nxCloudId;
|
|
819
|
-
|
|
821
|
+
// NX_NO_CLOUD / neverConnectToCloud wins over any ambient token — otherwise
|
|
822
|
+
// a surrounding CI env variable would still route through the cloud shell,
|
|
823
|
+
// which resolves the default tasks runner via its own require bridge and
|
|
824
|
+
// can pull in a different Nx version than the workspace's own.
|
|
825
|
+
return isCloudRunner && !(0, nx_cloud_utils_1.isNxCloudDisabled)(nxJson)
|
|
826
|
+
? 'nx-cloud'
|
|
827
|
+
: defaultTasksRunnerPath;
|
|
820
828
|
}
|
|
821
829
|
function getRunnerOptions(runner, nxJson, nxArgs, isCloudDefault) {
|
|
822
830
|
const defaultCacheableOperations = [];
|
|
@@ -19,13 +19,29 @@ function getEnvVariablesForBatchProcess(skipNxCache, captureStderr) {
|
|
|
19
19
|
...getNxEnvVariablesForForkedProcess(process.env.FORCE_COLOR === undefined ? 'true' : process.env.FORCE_COLOR, skipNxCache, captureStderr),
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
+
// The orchestrator now calls this eagerly during the coordinator pre-hash
|
|
23
|
+
// in addition to processTask (and again in hashBatchTasks), so the same
|
|
24
|
+
// task hits this function multiple times per run. Each call reads 3+ .env
|
|
25
|
+
// files from disk — memoize by task.id to skip the repeat work.
|
|
26
|
+
//
|
|
27
|
+
// Cache lifetime is the current Nx invocation: the function is only called
|
|
28
|
+
// from CLI/orchestrator code (not the long-lived daemon), so the map is
|
|
29
|
+
// scoped to a single run. Callers must not mutate the returned env — they
|
|
30
|
+
// already spread it into new objects before adding task-specific overrides
|
|
31
|
+
// (see getEnvVariablesForTask).
|
|
32
|
+
const taskSpecificEnvCache = new Map();
|
|
22
33
|
function getTaskSpecificEnv(task, graph) {
|
|
34
|
+
const cached = taskSpecificEnvCache.get(task.id);
|
|
35
|
+
if (cached)
|
|
36
|
+
return cached;
|
|
23
37
|
// Unload any dot env files at the root of the workspace that were loaded on init of Nx.
|
|
24
38
|
const taskEnv = unloadDotEnvFiles({ ...process.env });
|
|
25
|
-
|
|
39
|
+
const env = process.env.NX_LOAD_DOT_ENV_FILES === 'true'
|
|
26
40
|
? loadDotEnvFilesForTask(task, graph, taskEnv)
|
|
27
41
|
: // If not loading dot env files, ensure env vars created by system are still loaded
|
|
28
42
|
taskEnv;
|
|
43
|
+
taskSpecificEnvCache.set(task.id, env);
|
|
44
|
+
return env;
|
|
29
45
|
}
|
|
30
46
|
function getEnvVariablesForTask(task, taskSpecificEnv, forceColor, skipNxCache, captureStderr, outputPath, streamOutput) {
|
|
31
47
|
const res = {
|