nx 22.7.10 → 22.7.12
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/src/daemon/client/client.d.ts +2 -0
- package/dist/src/daemon/client/client.js +73 -7
- package/dist/src/daemon/client/daemon-environment.d.ts +11 -0
- package/dist/src/daemon/client/daemon-environment.js +20 -0
- package/dist/src/daemon/client/daemon-socket-messenger.d.ts +1 -1
- package/dist/src/daemon/client/daemon-socket-messenger.js +8 -3
- package/dist/src/daemon/is-v8-serializer-enabled.d.ts +2 -2
- package/dist/src/daemon/is-v8-serializer-enabled.js +2 -2
- package/dist/src/daemon/server/client-socket-context.d.ts +1 -1
- package/dist/src/daemon/server/client-socket-context.js +2 -3
- package/dist/src/daemon/server/handle-hash-tasks.js +1 -1
- package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +1 -4
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +1 -14
- package/dist/src/daemon/server/server.js +17 -24
- package/dist/src/daemon/server/shutdown-utils.d.ts +1 -1
- package/dist/src/daemon/server/shutdown-utils.js +2 -2
- package/dist/src/daemon/socket-utils.d.ts +26 -7
- package/dist/src/daemon/socket-utils.js +45 -31
- package/dist/src/hasher/create-task-hasher.js +1 -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/project-graph/build-project-graph.d.ts +2 -4
- package/dist/src/project-graph/build-project-graph.js +2 -7
- package/dist/src/project-graph/file-map-utils.d.ts +2 -4
- package/dist/src/project-graph/file-map-utils.js +0 -3
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +18 -2
- package/dist/src/project-graph/plugins/isolation/messaging.js +1 -3
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +9 -1
- package/dist/src/project-graph/project-graph.js +4 -4
- package/dist/src/project-graph/utils/retrieve-workspace-files.d.ts +1 -2
- package/dist/src/project-graph/utils/retrieve-workspace-files.js +1 -3
- package/dist/src/tasks-runner/forked-process-task-runner.d.ts +3 -0
- package/dist/src/tasks-runner/forked-process-task-runner.js +15 -6
- package/dist/src/tasks-runner/init-tasks-runner.js +32 -2
- package/dist/src/tasks-runner/life-cycles/task-history-life-cycle.js +16 -11
- package/dist/src/tasks-runner/pseudo-ipc.js +14 -13
- package/dist/src/tasks-runner/running-tasks/batch-process.js +3 -0
- package/dist/src/tasks-runner/running-tasks/shared-running-task.d.ts +1 -0
- package/dist/src/tasks-runner/running-tasks/shared-running-task.js +12 -1
- package/dist/src/tasks-runner/task-orchestrator.d.ts +3 -0
- package/dist/src/tasks-runner/task-orchestrator.js +40 -6
- package/dist/src/utils/consume-messages-from-socket.d.ts +42 -7
- package/dist/src/utils/consume-messages-from-socket.js +272 -38
- package/dist/src/utils/params.js +1 -1
- package/package.json +11 -11
|
@@ -4,7 +4,9 @@ exports.getPluginOsSocketPath = exports.getForkedProcessOsSocketPath = exports.g
|
|
|
4
4
|
exports.getPluginSocketFileName = getPluginSocketFileName;
|
|
5
5
|
exports.killSocketOrPath = killSocketOrPath;
|
|
6
6
|
exports.serializeResult = serializeResult;
|
|
7
|
+
exports.serializeWithFallback = serializeWithFallback;
|
|
7
8
|
exports.serialize = serialize;
|
|
9
|
+
exports.sendMessage = sendMessage;
|
|
8
10
|
const fs_1 = require("fs");
|
|
9
11
|
const os_1 = require("os");
|
|
10
12
|
const path_1 = require("path");
|
|
@@ -12,6 +14,7 @@ const tmp_dir_1 = require("./tmp-dir");
|
|
|
12
14
|
const serializable_error_1 = require("../utils/serializable-error");
|
|
13
15
|
const is_v8_serializer_enabled_1 = require("./is-v8-serializer-enabled");
|
|
14
16
|
const v8_1 = require("v8");
|
|
17
|
+
const consume_messages_from_socket_1 = require("../utils/consume-messages-from-socket");
|
|
15
18
|
exports.isWindows = (0, os_1.platform)() === 'win32';
|
|
16
19
|
/**
|
|
17
20
|
* For IPC with the daemon server we use unix sockets or windows named pipes, depending on the user's operating system.
|
|
@@ -77,41 +80,52 @@ function serializeResult(error, serializedProjectGraph, serializedSourceMaps) {
|
|
|
77
80
|
// We do not want to repeat work `JSON.stringify`ing an object containing the potentially large project graph so merge as strings
|
|
78
81
|
return `{ "error": ${JSON.stringify(error ? (0, serializable_error_1.createSerializableError)(error) : error)}, "projectGraph": ${serializedProjectGraph}, "sourceMaps": ${serializedSourceMaps} }`;
|
|
79
82
|
}
|
|
83
|
+
function serializeAs(data, format) {
|
|
84
|
+
return format === 'v8'
|
|
85
|
+
? (0, v8_1.serialize)(data)
|
|
86
|
+
: Buffer.from(JSON.stringify(data), 'utf8');
|
|
87
|
+
}
|
|
80
88
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* by the client it is communicating with.
|
|
89
|
+
* Serialize using `preferred`, falling back to the other format when it throws.
|
|
90
|
+
* Neither format subsumes the other: JSON cannot represent a BigInt and hits the
|
|
91
|
+
* max string length far sooner, while v8 cannot clone a function.
|
|
85
92
|
*
|
|
86
93
|
* @param data Data to serialize
|
|
87
|
-
* @param
|
|
88
|
-
* @returns Serialized data as a
|
|
94
|
+
* @param preferred Format to attempt first
|
|
95
|
+
* @returns Serialized data as bytes ready to be framed onto a socket
|
|
89
96
|
*/
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return (0, v8_1.serialize)(data).toString('binary');
|
|
94
|
-
}
|
|
95
|
-
catch (e) {
|
|
96
|
-
if (force !== 'v8') {
|
|
97
|
-
console.warn(`Data could not be serialized using v8 serialization: ${e}. Falling back to JSON serialization.`);
|
|
98
|
-
// Fall back to JSON serialization
|
|
99
|
-
return JSON.stringify(data);
|
|
100
|
-
}
|
|
101
|
-
throw e;
|
|
102
|
-
}
|
|
97
|
+
function serializeWithFallback(data, preferred) {
|
|
98
|
+
try {
|
|
99
|
+
return serializeAs(data, preferred);
|
|
103
100
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
catch (e) {
|
|
109
|
-
if (force !== 'json') {
|
|
110
|
-
// Fall back to v8 serialization
|
|
111
|
-
console.warn(`Data could not be serialized using JSON.stringify: ${e}. Falling back to v8 serialization.`);
|
|
112
|
-
return (0, v8_1.serialize)(data).toString('binary');
|
|
113
|
-
}
|
|
114
|
-
throw e;
|
|
115
|
-
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
const fallback = preferred === 'v8' ? 'json' : 'v8';
|
|
103
|
+
console.warn(`Data could not be serialized using ${preferred} serialization: ${e}. Falling back to ${fallback} serialization.`);
|
|
104
|
+
return serializeAs(data, fallback);
|
|
116
105
|
}
|
|
117
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Serialize data for IPC using the format the user configured.
|
|
109
|
+
*
|
|
110
|
+
* @param data Data to serialize
|
|
111
|
+
* @param force Use this format without falling back. For callers whose data is
|
|
112
|
+
* known to be unrepresentable in the other format, where a fallback
|
|
113
|
+
* would only swap one failure for a less obvious one.
|
|
114
|
+
* @returns Serialized data as bytes ready to be framed onto a socket
|
|
115
|
+
*/
|
|
116
|
+
function serialize(data, force) {
|
|
117
|
+
return force
|
|
118
|
+
? serializeAs(data, force)
|
|
119
|
+
: serializeWithFallback(data, (0, is_v8_serializer_enabled_1.isV8SerializerEnabled)() ? 'v8' : 'json');
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Serialize `data` and write it as one framed message.
|
|
123
|
+
*
|
|
124
|
+
* Lives here rather than in `writeMessage` so the framing stays a byte-level
|
|
125
|
+
* primitive: `utils/consume-messages-from-socket` is shared by callers that
|
|
126
|
+
* already hold bytes, and having it reach back into the daemon's serializer
|
|
127
|
+
* would invert the dependency.
|
|
128
|
+
*/
|
|
129
|
+
function sendMessage(socket, data, force, callback) {
|
|
130
|
+
(0, consume_messages_from_socket_1.writeMessage)(socket, serialize(data, force), callback);
|
|
131
|
+
}
|
|
@@ -9,7 +9,7 @@ function createTaskHasher(projectGraph, nxJson, runnerOptions) {
|
|
|
9
9
|
return new task_hasher_1.DaemonBasedTaskHasher(client_1.daemonClient, runnerOptions);
|
|
10
10
|
}
|
|
11
11
|
else {
|
|
12
|
-
const {
|
|
12
|
+
const { rustReferences } = (0, build_project_graph_1.getFileMap)();
|
|
13
13
|
return new task_hasher_1.InProcessTaskHasher(projectGraph, nxJson, rustReferences, runnerOptions);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
Binary file
|
|
Binary file
|
|
@@ -2,18 +2,16 @@ import { FileMap, ProjectGraph, ProjectGraphExternalNode } from '../config/proje
|
|
|
2
2
|
import { ProjectConfiguration } from '../config/workspace-json-project-json';
|
|
3
3
|
import { NxWorkspaceFilesExternals } from '../native';
|
|
4
4
|
import { CreateMetadataError } from './error-types';
|
|
5
|
-
import { FileData } from './file-utils';
|
|
6
5
|
import { FileMapCache } from './nx-deps-cache';
|
|
7
6
|
import { CreateMetadataContext } from './plugins';
|
|
8
7
|
import type { LoadedNxPlugin } from './plugins/loaded-nx-plugin';
|
|
9
8
|
import type { ConfigurationSourceMaps } from './utils/project-configuration/source-maps';
|
|
10
9
|
export declare function getFileMap(): {
|
|
11
10
|
fileMap: FileMap;
|
|
12
|
-
allWorkspaceFiles: FileData[];
|
|
13
11
|
rustReferences: NxWorkspaceFilesExternals | null;
|
|
14
12
|
};
|
|
15
|
-
export declare function hydrateFileMap(fileMap: FileMap,
|
|
16
|
-
export declare function buildProjectGraphUsingProjectFileMap(projectRootMap: Record<string, ProjectConfiguration>, externalNodes: Record<string, ProjectGraphExternalNode>, fileMap: FileMap,
|
|
13
|
+
export declare function hydrateFileMap(fileMap: FileMap, rustReferences: NxWorkspaceFilesExternals): void;
|
|
14
|
+
export declare function buildProjectGraphUsingProjectFileMap(projectRootMap: Record<string, ProjectConfiguration>, externalNodes: Record<string, ProjectGraphExternalNode>, fileMap: FileMap, rustReferences: NxWorkspaceFilesExternals, fileMapCache: FileMapCache | null, plugins: LoadedNxPlugin[], sourceMap: ConfigurationSourceMaps): Promise<{
|
|
17
15
|
projectGraph: ProjectGraph;
|
|
18
16
|
projectFileMapCache: FileMapCache;
|
|
19
17
|
}>;
|
|
@@ -23,13 +23,11 @@ const implicit_project_dependencies_1 = require("./utils/implicit-project-depend
|
|
|
23
23
|
const normalize_project_nodes_1 = require("./utils/normalize-project-nodes");
|
|
24
24
|
const target_merging_1 = require("./utils/project-configuration/target-merging");
|
|
25
25
|
let storedFileMap = null;
|
|
26
|
-
let storedAllWorkspaceFiles = null;
|
|
27
26
|
let storedRustReferences = null;
|
|
28
27
|
function getFileMap() {
|
|
29
28
|
if (!!storedFileMap) {
|
|
30
29
|
return {
|
|
31
30
|
fileMap: storedFileMap,
|
|
32
|
-
allWorkspaceFiles: storedAllWorkspaceFiles,
|
|
33
31
|
rustReferences: storedRustReferences,
|
|
34
32
|
};
|
|
35
33
|
}
|
|
@@ -39,19 +37,16 @@ function getFileMap() {
|
|
|
39
37
|
nonProjectFiles: [],
|
|
40
38
|
projectFileMap: {},
|
|
41
39
|
},
|
|
42
|
-
allWorkspaceFiles: [],
|
|
43
40
|
rustReferences: null,
|
|
44
41
|
};
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
|
-
function hydrateFileMap(fileMap,
|
|
44
|
+
function hydrateFileMap(fileMap, rustReferences) {
|
|
48
45
|
storedFileMap = fileMap;
|
|
49
|
-
storedAllWorkspaceFiles = allWorkspaceFiles;
|
|
50
46
|
storedRustReferences = rustReferences;
|
|
51
47
|
}
|
|
52
|
-
async function buildProjectGraphUsingProjectFileMap(projectRootMap, externalNodes, fileMap,
|
|
48
|
+
async function buildProjectGraphUsingProjectFileMap(projectRootMap, externalNodes, fileMap, rustReferences, fileMapCache, plugins, sourceMap) {
|
|
53
49
|
storedFileMap = fileMap;
|
|
54
|
-
storedAllWorkspaceFiles = allWorkspaceFiles;
|
|
55
50
|
storedRustReferences = rustReferences;
|
|
56
51
|
const projects = {};
|
|
57
52
|
for (const root in projectRootMap) {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { FileData, FileMap, ProjectFileMap, ProjectGraph } from '../config/project-graph';
|
|
2
|
-
import { ProjectConfiguration, ProjectsConfigurations } from '../config/workspace-json-project-json';
|
|
1
|
+
import type { FileData, FileMap, ProjectFileMap, ProjectGraph } from '../config/project-graph';
|
|
2
|
+
import type { ProjectConfiguration, ProjectsConfigurations } from '../config/workspace-json-project-json';
|
|
3
3
|
import { NxWorkspaceFilesExternals } from '../native';
|
|
4
4
|
export interface WorkspaceFileMap {
|
|
5
|
-
allWorkspaceFiles: FileData[];
|
|
6
5
|
fileMap: FileMap;
|
|
7
6
|
}
|
|
8
7
|
export declare function createProjectFileMapUsingProjectGraph(graph: ProjectGraph): Promise<ProjectFileMap>;
|
|
@@ -10,6 +9,5 @@ export declare function createFileMapUsingProjectGraph(graph: ProjectGraph): Pro
|
|
|
10
9
|
export declare function createFileMap(projectsConfigurations: ProjectsConfigurations, allWorkspaceFiles: FileData[]): WorkspaceFileMap;
|
|
11
10
|
export declare function updateFileMap(projectsConfigurations: Record<string, ProjectConfiguration>, rustReferences: NxWorkspaceFilesExternals, updatedFiles: Record<string, string>, deletedFiles: string[]): {
|
|
12
11
|
fileMap: import("../native").FileMap;
|
|
13
|
-
allWorkspaceFiles: FileData[];
|
|
14
12
|
rustReferences: NxWorkspaceFilesExternals;
|
|
15
13
|
};
|
|
@@ -7,7 +7,6 @@ exports.updateFileMap = updateFileMap;
|
|
|
7
7
|
const workspace_context_1 = require("../utils/workspace-context");
|
|
8
8
|
const workspace_root_1 = require("../utils/workspace-root");
|
|
9
9
|
const project_graph_1 = require("./project-graph");
|
|
10
|
-
const build_all_workspace_files_1 = require("./utils/build-all-workspace-files");
|
|
11
10
|
const find_project_for_path_1 = require("./utils/find-project-for-path");
|
|
12
11
|
async function createProjectFileMapUsingProjectGraph(graph) {
|
|
13
12
|
return (await createFileMapUsingProjectGraph(graph)).fileMap.projectFileMap;
|
|
@@ -38,7 +37,6 @@ function createFileMap(projectsConfigurations, allWorkspaceFiles) {
|
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
return {
|
|
41
|
-
allWorkspaceFiles,
|
|
42
40
|
fileMap: {
|
|
43
41
|
projectFileMap,
|
|
44
42
|
nonProjectFiles,
|
|
@@ -49,7 +47,6 @@ function updateFileMap(projectsConfigurations, rustReferences, updatedFiles, del
|
|
|
49
47
|
const updates = (0, workspace_context_1.updateProjectFiles)(Object.fromEntries((0, find_project_for_path_1.createProjectRootMappingsFromProjectConfigurations)(projectsConfigurations)), rustReferences, updatedFiles, deletedFiles);
|
|
50
48
|
return {
|
|
51
49
|
fileMap: updates.fileMap,
|
|
52
|
-
allWorkspaceFiles: (0, build_all_workspace_files_1.buildAllWorkspaceFiles)(updates.fileMap.projectFileMap, updates.fileMap.nonProjectFiles),
|
|
53
50
|
rustReferences: updates.externalReferences,
|
|
54
51
|
};
|
|
55
52
|
}
|
|
@@ -52,7 +52,17 @@ class IsolatedPlugin {
|
|
|
52
52
|
this.responseHandlers = new Map();
|
|
53
53
|
this.exitHandler = null;
|
|
54
54
|
this.handleSocketData = (raw) => {
|
|
55
|
-
|
|
55
|
+
let message;
|
|
56
|
+
try {
|
|
57
|
+
message = (0, consume_messages_from_socket_1.parseMessage)(raw);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
// Runs inside a synchronous socket 'data' callback, so a throw here
|
|
61
|
+
// becomes an uncaughtException in the host process rather than a failed
|
|
62
|
+
// plugin call.
|
|
63
|
+
logger_2.logger.error(`[plugin-client] "${this.name}" sent a message that could not be parsed: ${e instanceof Error ? e.message : e}\nReceived: ${(0, consume_messages_from_socket_1.describeMessage)(raw)}`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
56
66
|
if ((0, messaging_1.isPluginWorkerNotification)(message)) {
|
|
57
67
|
handlePluginWorkerNotification(message);
|
|
58
68
|
return;
|
|
@@ -94,7 +104,13 @@ class IsolatedPlugin {
|
|
|
94
104
|
this.responseHandlers.clear();
|
|
95
105
|
};
|
|
96
106
|
worker.on('exit', this.exitHandler);
|
|
97
|
-
socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)(this.handleSocketData)
|
|
107
|
+
socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)(this.handleSocketData, (err) => {
|
|
108
|
+
// Nothing else settles the pending hook promises on a framing
|
|
109
|
+
// failure, so surface it the same way a dead worker would.
|
|
110
|
+
this.exitHandler?.();
|
|
111
|
+
socket.destroy();
|
|
112
|
+
console.error(err.message);
|
|
113
|
+
}));
|
|
98
114
|
return this.sendLoadMessage();
|
|
99
115
|
}
|
|
100
116
|
/**
|
|
@@ -6,7 +6,6 @@ exports.isPluginWorkerResult = isPluginWorkerResult;
|
|
|
6
6
|
exports.consumeMessage = consumeMessage;
|
|
7
7
|
exports.sendMessageOverSocket = sendMessageOverSocket;
|
|
8
8
|
const socket_utils_1 = require("../../../daemon/socket-utils");
|
|
9
|
-
const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
|
|
10
9
|
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
11
10
|
const NOTIFICATION_TYPES = [
|
|
12
11
|
'emitLog',
|
|
@@ -84,6 +83,5 @@ async function consumeMessage(socket, raw, handlers) {
|
|
|
84
83
|
function sendMessageOverSocket(socket, message) {
|
|
85
84
|
// Stamped here rather than in each constructor, mirroring the daemon.
|
|
86
85
|
message.workspaceRoot = workspace_root_1.workspaceRoot;
|
|
87
|
-
|
|
88
|
-
socket.write(consume_messages_from_socket_1.MESSAGE_END_SEQ);
|
|
86
|
+
(0, socket_utils_1.sendMessage)(socket, message);
|
|
89
87
|
}
|
|
@@ -87,7 +87,8 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
87
87
|
exclude: plugin.exclude,
|
|
88
88
|
createNodesPattern: plugin.createNodes?.[0],
|
|
89
89
|
hasCreateDependencies: 'createDependencies' in plugin && !!plugin.createDependencies,
|
|
90
|
-
hasProcessProjectGraph: 'processProjectGraph' in plugin &&
|
|
90
|
+
hasProcessProjectGraph: 'processProjectGraph' in plugin &&
|
|
91
|
+
!!plugin.processProjectGraph,
|
|
91
92
|
hasCreateMetadata: 'createMetadata' in plugin && !!plugin.createMetadata,
|
|
92
93
|
hasPreTasksExecution: 'preTasksExecution' in plugin && !!plugin.preTasksExecution,
|
|
93
94
|
hasPostTasksExecution: 'postTasksExecution' in plugin && !!plugin.postTasksExecution,
|
|
@@ -118,6 +119,13 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
118
119
|
}
|
|
119
120
|
}),
|
|
120
121
|
});
|
|
122
|
+
}, (err) => {
|
|
123
|
+
// The host link cannot resynchronize. Exit so the host's 'exit' handler
|
|
124
|
+
// rejects the pending hooks instead of waiting on a reply that the
|
|
125
|
+
// framing failure guarantees will never arrive.
|
|
126
|
+
console.error(err.message);
|
|
127
|
+
socket.destroy();
|
|
128
|
+
process.exit(1);
|
|
121
129
|
}));
|
|
122
130
|
// When the host disconnects, clean up and exit.
|
|
123
131
|
socket.on('end', () => {
|
|
@@ -104,14 +104,14 @@ async function buildProjectGraphAndSourceMapsWithoutDaemon() {
|
|
|
104
104
|
const { projects, externalNodes, sourceMaps, projectRootMap } = configurationResult;
|
|
105
105
|
perf_hooks_1.performance.mark('retrieve-project-configurations:end');
|
|
106
106
|
perf_hooks_1.performance.mark('retrieve-workspace-files:start');
|
|
107
|
-
const {
|
|
107
|
+
const { fileMap, rustReferences } = await (0, retrieve_workspace_files_1.retrieveWorkspaceFiles)(workspace_root_1.workspaceRoot, projectRootMap);
|
|
108
108
|
perf_hooks_1.performance.mark('retrieve-workspace-files:end');
|
|
109
109
|
const cacheEnabled = process.env.NX_CACHE_PROJECT_GRAPH !== 'false';
|
|
110
110
|
perf_hooks_1.performance.mark('build-project-graph-using-project-file-map:start');
|
|
111
111
|
let projectGraphError;
|
|
112
112
|
let projectGraphResult;
|
|
113
113
|
try {
|
|
114
|
-
projectGraphResult = await (0, build_project_graph_1.buildProjectGraphUsingProjectFileMap)(projects, externalNodes, fileMap,
|
|
114
|
+
projectGraphResult = await (0, build_project_graph_1.buildProjectGraphUsingProjectFileMap)(projects, externalNodes, fileMap, rustReferences, cacheEnabled ? (0, nx_deps_cache_1.readFileMapCache)() : null, plugins, sourceMaps);
|
|
115
115
|
}
|
|
116
116
|
catch (e) {
|
|
117
117
|
if ((0, error_types_1.isAggregateProjectGraphError)(e)) {
|
|
@@ -180,8 +180,8 @@ async function readCachedGraphAndHydrateFileMap(minimumComputedAt) {
|
|
|
180
180
|
data.root,
|
|
181
181
|
project,
|
|
182
182
|
]));
|
|
183
|
-
const {
|
|
184
|
-
(0, build_project_graph_1.hydrateFileMap)(fileMap,
|
|
183
|
+
const { fileMap, rustReferences } = await (0, retrieve_workspace_files_1.retrieveWorkspaceFiles)(workspace_root_1.workspaceRoot, projectRootMap);
|
|
184
|
+
(0, build_project_graph_1.hydrateFileMap)(fileMap, rustReferences);
|
|
185
185
|
return graph;
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
@@ -3,13 +3,12 @@ import { NxJsonConfiguration } from '../../config/nx-json';
|
|
|
3
3
|
import { ConfigurationResult } from './project-configuration-utils';
|
|
4
4
|
import type { LoadedNxPlugin } from '../plugins/loaded-nx-plugin';
|
|
5
5
|
/**
|
|
6
|
-
* Walks the workspace directory to create the `projectFileMap
|
|
6
|
+
* Walks the workspace directory to create the `projectFileMap` and `ProjectConfigurations`
|
|
7
7
|
* @throws
|
|
8
8
|
* @param workspaceRoot
|
|
9
9
|
* @param nxJson
|
|
10
10
|
*/
|
|
11
11
|
export declare function retrieveWorkspaceFiles(workspaceRoot: string, projectRootMap: Record<string, string>): Promise<{
|
|
12
|
-
allWorkspaceFiles: import("../file-utils").FileData[];
|
|
13
12
|
fileMap: {
|
|
14
13
|
projectFileMap: Record<string, import("../../native").FileData[]>;
|
|
15
14
|
nonProjectFiles: import("../../native").FileData[];
|
|
@@ -11,11 +11,10 @@ const angular_json_1 = require("../../adapter/angular-json");
|
|
|
11
11
|
const nx_json_1 = require("../../config/nx-json");
|
|
12
12
|
const project_configuration_utils_1 = require("./project-configuration-utils");
|
|
13
13
|
const workspace_context_1 = require("../../utils/workspace-context");
|
|
14
|
-
const build_all_workspace_files_1 = require("./build-all-workspace-files");
|
|
15
14
|
const path_1 = require("path");
|
|
16
15
|
const get_plugins_1 = require("../plugins/get-plugins");
|
|
17
16
|
/**
|
|
18
|
-
* Walks the workspace directory to create the `projectFileMap
|
|
17
|
+
* Walks the workspace directory to create the `projectFileMap` and `ProjectConfigurations`
|
|
19
18
|
* @throws
|
|
20
19
|
* @param workspaceRoot
|
|
21
20
|
* @param nxJson
|
|
@@ -29,7 +28,6 @@ async function retrieveWorkspaceFiles(workspaceRoot, projectRootMap) {
|
|
|
29
28
|
perf_hooks_1.performance.mark('get-workspace-files:end');
|
|
30
29
|
perf_hooks_1.performance.measure('get-workspace-files', 'get-workspace-files:start', 'get-workspace-files:end');
|
|
31
30
|
return {
|
|
32
|
-
allWorkspaceFiles: (0, build_all_workspace_files_1.buildAllWorkspaceFiles)(projectFileMap, globalFiles),
|
|
33
31
|
fileMap: {
|
|
34
32
|
projectFileMap,
|
|
35
33
|
nonProjectFiles: globalFiles,
|
|
@@ -11,6 +11,8 @@ export declare class ForkedProcessTaskRunner {
|
|
|
11
11
|
cliPath: string;
|
|
12
12
|
private readonly verbose;
|
|
13
13
|
private processes;
|
|
14
|
+
private processMessageHandler;
|
|
15
|
+
private processExitHandler;
|
|
14
16
|
private finishedProcesses;
|
|
15
17
|
private pseudoTerminals;
|
|
16
18
|
constructor(options: DefaultTasksRunnerOptions, tuiEnabled: boolean);
|
|
@@ -39,4 +41,5 @@ export declare class ForkedProcessTaskRunner {
|
|
|
39
41
|
private writeTerminalOutput;
|
|
40
42
|
cleanup(signal?: NodeJS.Signals): void;
|
|
41
43
|
private setupProcessEventListeners;
|
|
44
|
+
removeProcessEventListeners(): void;
|
|
42
45
|
}
|
|
@@ -271,7 +271,7 @@ class ForkedProcessTaskRunner {
|
|
|
271
271
|
this.cleanUpBatchProcesses();
|
|
272
272
|
}
|
|
273
273
|
setupProcessEventListeners() {
|
|
274
|
-
|
|
274
|
+
this.processMessageHandler = (message) => {
|
|
275
275
|
this.pseudoTerminals.forEach((p) => {
|
|
276
276
|
p.sendMessageToChildren(message);
|
|
277
277
|
});
|
|
@@ -281,14 +281,23 @@ class ForkedProcessTaskRunner {
|
|
|
281
281
|
}
|
|
282
282
|
});
|
|
283
283
|
};
|
|
284
|
+
this.processExitHandler = () => {
|
|
285
|
+
this.cleanup();
|
|
286
|
+
process.off('message', this.processMessageHandler);
|
|
287
|
+
};
|
|
284
288
|
// When the nx process gets a message, it will be sent into the task's process
|
|
285
|
-
process.on('message',
|
|
289
|
+
process.on('message', this.processMessageHandler);
|
|
286
290
|
// Terminate any task processes on exit
|
|
287
|
-
process.once('exit',
|
|
288
|
-
this.cleanup();
|
|
289
|
-
process.off('message', messageHandler);
|
|
290
|
-
});
|
|
291
|
+
process.once('exit', this.processExitHandler);
|
|
291
292
|
// Signal handlers removed - TaskOrchestrator owns signal handling
|
|
292
293
|
}
|
|
294
|
+
// Long-lived processes (Nx Cloud agents) create a runner per invocation;
|
|
295
|
+
// without this each instance stays reachable from `process` forever.
|
|
296
|
+
// Call only after cleanup(): the exit handler is the last-resort child
|
|
297
|
+
// kill, so it must not be removed while children may still be alive.
|
|
298
|
+
removeProcessEventListeners() {
|
|
299
|
+
process.off('message', this.processMessageHandler);
|
|
300
|
+
process.off('exit', this.processExitHandler);
|
|
301
|
+
}
|
|
293
302
|
}
|
|
294
303
|
exports.ForkedProcessTaskRunner = ForkedProcessTaskRunner;
|
|
@@ -14,6 +14,7 @@ const life_cycle_1 = require("./life-cycle");
|
|
|
14
14
|
const task_orchestrator_1 = require("./task-orchestrator");
|
|
15
15
|
const create_task_hasher_1 = require("../hasher/create-task-hasher");
|
|
16
16
|
const client_1 = require("../daemon/client/client");
|
|
17
|
+
const shared_running_task_1 = require("./running-tasks/shared-running-task");
|
|
17
18
|
const task_results_life_cycle_1 = require("./life-cycles/task-results-life-cycle");
|
|
18
19
|
/**
|
|
19
20
|
* This function is deprecated. Do not use this
|
|
@@ -110,6 +111,11 @@ async function createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJs
|
|
|
110
111
|
orchestrator.processAllScheduledTasks();
|
|
111
112
|
return orchestrator;
|
|
112
113
|
}
|
|
114
|
+
// Nothing awaits the dispose chains below, so an unhandled rejection would take
|
|
115
|
+
// down a long-lived agent process.
|
|
116
|
+
function logDisposeFailure(e) {
|
|
117
|
+
console.error('Failed to dispose the task orchestrator:', e);
|
|
118
|
+
}
|
|
113
119
|
async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
|
|
114
120
|
const orchestrator = await createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle);
|
|
115
121
|
let groupId = 0;
|
|
@@ -138,12 +144,36 @@ async function runDiscreteTasks(tasks, projectGraph, taskGraphForHashing, nxJson
|
|
|
138
144
|
return [hit];
|
|
139
145
|
return [await orchestrator.runTaskDirectly(true, task, groupId++)];
|
|
140
146
|
});
|
|
141
|
-
|
|
147
|
+
const results = [...batchResults, ...taskResults];
|
|
148
|
+
// Callers like Nx Cloud agents create an orchestrator per invocation in a
|
|
149
|
+
// long-lived process; release its process-level listeners once all tasks
|
|
150
|
+
// settle, otherwise every invocation's orchestrator stays reachable forever.
|
|
151
|
+
// Not awaited, so callers keep consuming handles as they settle; the forked
|
|
152
|
+
// runner's exit handler still reaps children until dispose() runs.
|
|
153
|
+
Promise.allSettled(results)
|
|
154
|
+
.then(() => orchestrator.dispose())
|
|
155
|
+
.catch(logDisposeFailure);
|
|
156
|
+
return results;
|
|
142
157
|
}
|
|
143
158
|
async function runContinuousTasks(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle) {
|
|
144
159
|
const orchestrator = await createOrchestrator(tasks, projectGraph, taskGraphForHashing, nxJson, lifeCycle);
|
|
145
|
-
|
|
160
|
+
const runningTasks = tasks.reduce((current, task, index) => {
|
|
146
161
|
current[task.id] = orchestrator.startContinuousTask(task, index);
|
|
147
162
|
return current;
|
|
148
163
|
}, {});
|
|
164
|
+
// Unlike runDiscreteTasks, this must resolve at task start: callers keep
|
|
165
|
+
// the RunningTask handles to kill later, so disposal has to be deferred
|
|
166
|
+
// until every task actually exits.
|
|
167
|
+
Promise.allSettled(Object.entries(runningTasks).map(async ([taskId, promise]) => {
|
|
168
|
+
const runningTask = await promise;
|
|
169
|
+
// A shared task is owned by another nx process; this orchestrator has
|
|
170
|
+
// no child to protect for it, so disposal does not wait on it.
|
|
171
|
+
if (runningTask instanceof shared_running_task_1.SharedRunningTask) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
await orchestrator.waitForContinuousTaskExit(taskId);
|
|
175
|
+
}))
|
|
176
|
+
.then(() => orchestrator.dispose())
|
|
177
|
+
.catch(logDisposeFailure);
|
|
178
|
+
return runningTasks;
|
|
149
179
|
}
|
|
@@ -22,6 +22,8 @@ function getTasksHistoryLifeCycle() {
|
|
|
22
22
|
class TaskHistoryLifeCycle {
|
|
23
23
|
constructor() {
|
|
24
24
|
this.startTimings = {};
|
|
25
|
+
// Holds no terminalOutput: this singleton outlives every run in the
|
|
26
|
+
// process, and agents complete thousands of tasks in one process.
|
|
25
27
|
this.pendingResults = new Map();
|
|
26
28
|
this.taskRuns = new Map();
|
|
27
29
|
this.taskHistory = (0, task_history_1.getTaskHistory)();
|
|
@@ -36,25 +38,28 @@ class TaskHistoryLifeCycle {
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
async endTasks(taskResults) {
|
|
39
|
-
for (const
|
|
40
|
-
this.pendingResults.set(
|
|
41
|
+
for (const { task, code, status } of taskResults) {
|
|
42
|
+
this.pendingResults.set(task.id, { task, code, status });
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
async endCommand() {
|
|
46
|
+
const pendingResults = Array.from(this.pendingResults.values());
|
|
47
|
+
this.pendingResults.clear();
|
|
44
48
|
if (!this.taskHistory) {
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
47
51
|
// Build TaskRun objects now — task.hash is guaranteed to be set by this point
|
|
48
|
-
for (const
|
|
49
|
-
this.taskRuns.set(
|
|
50
|
-
hash:
|
|
51
|
-
target:
|
|
52
|
-
code
|
|
53
|
-
status
|
|
54
|
-
start:
|
|
55
|
-
end:
|
|
56
|
-
cacheable:
|
|
52
|
+
for (const { task, code, status } of pendingResults) {
|
|
53
|
+
this.taskRuns.set(task.hash, {
|
|
54
|
+
hash: task.hash,
|
|
55
|
+
target: task.target,
|
|
56
|
+
code,
|
|
57
|
+
status,
|
|
58
|
+
start: task.startTime ?? this.startTimings[task.id],
|
|
59
|
+
end: task.endTime ?? Date.now(),
|
|
60
|
+
cacheable: task.cache === true,
|
|
57
61
|
});
|
|
62
|
+
delete this.startTimings[task.id];
|
|
58
63
|
}
|
|
59
64
|
const runs = [];
|
|
60
65
|
// Only check for flaky tasks among cacheable tasks
|
|
@@ -77,6 +77,14 @@ class PseudoIPCServer {
|
|
|
77
77
|
this.childReadyMap.get(childId)();
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
},
|
|
81
|
+
// Nothing resynchronizes a length-prefixed stream, so report it the
|
|
82
|
+
// same way a socket error is reported and let the peer see the close.
|
|
83
|
+
(err) => {
|
|
84
|
+
for (const childMessage of this.childMessages) {
|
|
85
|
+
childMessage.onError?.(err);
|
|
86
|
+
}
|
|
87
|
+
socket.destroy();
|
|
80
88
|
}));
|
|
81
89
|
socket.on('close', () => {
|
|
82
90
|
for (const childMessage of this.childMessages) {
|
|
@@ -91,15 +99,12 @@ class PseudoIPCServer {
|
|
|
91
99
|
}
|
|
92
100
|
sendMessageToChildren(message) {
|
|
93
101
|
this.sockets.forEach((socket) => {
|
|
94
|
-
|
|
95
|
-
// send EOT to indicate that the message has been fully written
|
|
96
|
-
socket.write(consume_messages_from_socket_1.MESSAGE_END_SEQ);
|
|
102
|
+
(0, socket_utils_1.sendMessage)(socket, { type: 'TO_CHILDREN_FROM_PARENT', message });
|
|
97
103
|
});
|
|
98
104
|
}
|
|
99
105
|
sendMessageToChild(id, message) {
|
|
100
106
|
this.sockets.forEach((socket) => {
|
|
101
|
-
|
|
102
|
-
socket.write(consume_messages_from_socket_1.MESSAGE_END_SEQ);
|
|
107
|
+
(0, socket_utils_1.sendMessage)(socket, { type: 'TO_CHILDREN_FROM_PARENT', id, message });
|
|
103
108
|
});
|
|
104
109
|
}
|
|
105
110
|
onMessageFromChildren(onMessage, onClose = () => { }, onError = (err) => { }) {
|
|
@@ -121,17 +126,13 @@ class PseudoIPCClient {
|
|
|
121
126
|
this.socket = (0, net_1.connect)(this.path);
|
|
122
127
|
}
|
|
123
128
|
sendMessageToParent(message) {
|
|
124
|
-
|
|
125
|
-
// send EOT to indicate that the message has been fully written
|
|
126
|
-
this.socket.write(consume_messages_from_socket_1.MESSAGE_END_SEQ);
|
|
129
|
+
(0, socket_utils_1.sendMessage)(this.socket, { type: 'TO_PARENT_FROM_CHILDREN', message });
|
|
127
130
|
}
|
|
128
131
|
notifyChildIsReady(id) {
|
|
129
|
-
|
|
132
|
+
(0, socket_utils_1.sendMessage)(this.socket, {
|
|
130
133
|
type: 'CHILD_READY',
|
|
131
134
|
message: id,
|
|
132
|
-
})
|
|
133
|
-
// send EOT to indicate that the message has been fully written
|
|
134
|
-
this.socket.write(consume_messages_from_socket_1.MESSAGE_END_SEQ);
|
|
135
|
+
});
|
|
135
136
|
}
|
|
136
137
|
onMessageFromParent(forkId, onMessage, onClose = () => { }, onError = (err) => { }) {
|
|
137
138
|
this.socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)(async (rawMessage) => {
|
|
@@ -144,7 +145,7 @@ class PseudoIPCClient {
|
|
|
144
145
|
onMessage(message);
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
|
-
}));
|
|
148
|
+
}, onError));
|
|
148
149
|
this.socket.on('close', onClose);
|
|
149
150
|
this.socket.on('error', onError);
|
|
150
151
|
return this;
|
|
@@ -82,6 +82,9 @@ class BatchProcess {
|
|
|
82
82
|
onOutput(cb) {
|
|
83
83
|
this.outputCallbacks.push(cb);
|
|
84
84
|
}
|
|
85
|
+
// Resolves on the CompleteBatchExecution message, not process exit: the
|
|
86
|
+
// child can outlive its results (open handles keep it alive), so callers
|
|
87
|
+
// must not treat a settled batch as an exited process.
|
|
85
88
|
async getResults() {
|
|
86
89
|
return Promise.race([
|
|
87
90
|
new Promise((_, rej) => {
|
|
@@ -3,6 +3,7 @@ import { RunningTasksService } from '../../native';
|
|
|
3
3
|
export declare class SharedRunningTask implements RunningTask {
|
|
4
4
|
private runningTasksService;
|
|
5
5
|
private exitCallbacks;
|
|
6
|
+
private detached;
|
|
6
7
|
constructor(runningTasksService: RunningTasksService, taskId: string);
|
|
7
8
|
getResults(): Promise<{
|
|
8
9
|
code: number;
|
|
@@ -5,7 +5,11 @@ class SharedRunningTask {
|
|
|
5
5
|
constructor(runningTasksService, taskId) {
|
|
6
6
|
this.runningTasksService = runningTasksService;
|
|
7
7
|
this.exitCallbacks = [];
|
|
8
|
+
this.detached = false;
|
|
8
9
|
this.waitForTaskToFinish(taskId).then(() => {
|
|
10
|
+
if (this.detached) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
// notify exit callbacks
|
|
10
14
|
this.exitCallbacks.forEach((cb) => cb(0));
|
|
11
15
|
});
|
|
@@ -13,8 +17,14 @@ class SharedRunningTask {
|
|
|
13
17
|
async getResults() {
|
|
14
18
|
throw new Error('Results cannot be retrieved from a shared task');
|
|
15
19
|
}
|
|
20
|
+
// Detaches from the shared task; the owning process keeps running it.
|
|
16
21
|
kill() {
|
|
22
|
+
if (this.detached) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.detached = true;
|
|
17
26
|
this.exitCallbacks.forEach((cb) => cb(0));
|
|
27
|
+
this.exitCallbacks = [];
|
|
18
28
|
}
|
|
19
29
|
onExit(cb) {
|
|
20
30
|
this.exitCallbacks.push(cb);
|
|
@@ -24,7 +34,8 @@ class SharedRunningTask {
|
|
|
24
34
|
// wait for the running task to finish
|
|
25
35
|
do {
|
|
26
36
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
27
|
-
} while (this.
|
|
37
|
+
} while (!this.detached &&
|
|
38
|
+
this.runningTasksService.getRunningTasks([taskId]).length);
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
41
|
exports.SharedRunningTask = SharedRunningTask;
|