nx 19.4.0-canary.20240626-3a2e8d4 → 19.4.0-canary.20240628-336d371
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/package.json +14 -14
- package/src/command-line/connect/connect-to-nx-cloud.d.ts +1 -1
- package/src/command-line/connect/connect-to-nx-cloud.js +16 -25
- package/src/command-line/graph/graph.d.ts +1 -0
- package/src/command-line/graph/graph.js +46 -2
- package/src/command-line/run/command-object.js +2 -1
- package/src/config/workspace-json-project-json.d.ts +8 -0
- package/src/core/graph/main.js +1 -1
- package/src/core/graph/styles.css +1 -1
- package/src/daemon/socket-utils.d.ts +1 -0
- package/src/daemon/socket-utils.js +6 -1
- package/src/executors/run-commands/run-commands.impl.d.ts +1 -1
- package/src/executors/run-commands/run-commands.impl.js +26 -15
- package/src/executors/run-commands/schema.json +14 -0
- package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.d.ts +1 -0
- package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +68 -33
- package/src/nx-cloud/generators/connect-to-nx-cloud/schema.json +5 -0
- package/src/nx-cloud/utilities/url-shorten.d.ts +2 -1
- package/src/nx-cloud/utilities/url-shorten.js +42 -14
- package/src/plugins/js/index.js +2 -0
- package/src/plugins/js/package-json/create-package-json.js +2 -2
- package/src/plugins/js/utils/register.js +38 -0
- package/src/plugins/project-json/build-nodes/package-json-next-to-project-json.js +9 -2
- package/src/plugins/target-defaults/target-defaults-plugin.d.ts +40 -0
- package/src/project-graph/plugins/internal-api.js +1 -1
- package/src/project-graph/plugins/isolation/index.d.ts +1 -1
- package/src/project-graph/plugins/isolation/index.js +3 -15
- package/src/project-graph/plugins/isolation/messaging.d.ts +6 -3
- package/src/project-graph/plugins/isolation/messaging.js +9 -3
- package/src/project-graph/plugins/isolation/plugin-pool.d.ts +1 -1
- package/src/project-graph/plugins/isolation/plugin-pool.js +126 -50
- package/src/project-graph/plugins/isolation/plugin-worker.js +128 -107
- package/src/tasks-runner/task-env.d.ts +13 -0
- package/src/tasks-runner/task-env.js +41 -26
- package/src/utils/git-utils.d.ts +1 -1
- package/src/utils/git-utils.js +13 -2
- package/src/utils/is-ci.js +1 -0
- package/src/utils/nx-cloud-utils.d.ts +1 -1
- package/src/utils/nx-cloud-utils.js +1 -1
@@ -7,7 +7,45 @@ const swcNodeInstalled = packageIsInstalled('@swc-node/register');
|
|
7
7
|
const tsNodeInstalled = packageIsInstalled('ts-node/register');
|
8
8
|
let ts;
|
9
9
|
let isTsEsmLoaderRegistered = false;
|
10
|
+
/**
|
11
|
+
* tsx is a utility to run TypeScript files in node which is growing in popularity:
|
12
|
+
* https://tsx.is
|
13
|
+
*
|
14
|
+
* Behind the scenes it is invoking node with relevant --require and --import flags.
|
15
|
+
*
|
16
|
+
* If the user is invoking Nx via a script which is being invoked via tsx, then we
|
17
|
+
* do not need to register any transpiler at all as the environment will have already
|
18
|
+
* been configured by tsx. In fact, registering a transpiler such as ts-node or swc
|
19
|
+
* in this case causes issues.
|
20
|
+
*
|
21
|
+
* Because node is being invoked by tsx, the tsx binary does not end up in the final
|
22
|
+
* process.argv and so we need to check a few possible things to account for usage
|
23
|
+
* via different package managers (e.g. pnpm does not set process._ to tsx, but rather
|
24
|
+
* pnpm itself, modern yarn does not set process._ at all etc.).
|
25
|
+
*/
|
26
|
+
const isInvokedByTsx = (() => {
|
27
|
+
if (process.env._?.endsWith(`${path_1.sep}tsx`)) {
|
28
|
+
return true;
|
29
|
+
}
|
30
|
+
const requireArgs = [];
|
31
|
+
const importArgs = [];
|
32
|
+
(process.execArgv ?? []).forEach((arg, i) => {
|
33
|
+
if (arg === '-r' || arg === '--require') {
|
34
|
+
requireArgs.push(process.execArgv[i + 1]);
|
35
|
+
}
|
36
|
+
if (arg === '--import') {
|
37
|
+
importArgs.push(process.execArgv[i + 1]);
|
38
|
+
}
|
39
|
+
});
|
40
|
+
const isTsxPath = (p) => p.includes(`${path_1.sep}tsx${path_1.sep}`);
|
41
|
+
return (requireArgs.some((a) => isTsxPath(a)) ||
|
42
|
+
importArgs.some((a) => isTsxPath(a)));
|
43
|
+
})();
|
10
44
|
function registerTsProject(path, configFilename) {
|
45
|
+
// See explanation alongside isInvokedByTsx declaration
|
46
|
+
if (isInvokedByTsx) {
|
47
|
+
return () => { };
|
48
|
+
}
|
11
49
|
const tsConfigPath = configFilename ? (0, path_1.join)(path, configFilename) : path;
|
12
50
|
const compilerOptions = readCompilerOptions(tsConfigPath);
|
13
51
|
const cleanupFunctions = [
|
@@ -5,6 +5,7 @@ const path_1 = require("path");
|
|
5
5
|
const fs_1 = require("fs");
|
6
6
|
const fileutils_1 = require("../../../utils/fileutils");
|
7
7
|
const package_json_1 = require("../../../utils/package-json");
|
8
|
+
const package_json_workspaces_1 = require("../../package-json-workspaces");
|
8
9
|
// TODO: Remove this one day, this should not need to be done.
|
9
10
|
exports.PackageJsonProjectsNextToProjectJsonPlugin = {
|
10
11
|
// Its not a problem if plugins happen to have same name, and this
|
@@ -27,8 +28,14 @@ exports.PackageJsonProjectsNextToProjectJsonPlugin = {
|
|
27
28
|
exports.default = exports.PackageJsonProjectsNextToProjectJsonPlugin;
|
28
29
|
function createProjectFromPackageJsonNextToProjectJson(projectJsonPath, workspaceRoot) {
|
29
30
|
const root = (0, path_1.dirname)(projectJsonPath);
|
30
|
-
const
|
31
|
-
|
31
|
+
const relativePackageJsonPath = (0, path_1.join)(root, 'package.json');
|
32
|
+
const packageJsonPath = (0, path_1.join)(workspaceRoot, relativePackageJsonPath);
|
33
|
+
const readJson = (f) => (0, fileutils_1.readJsonFile)((0, path_1.join)(workspaceRoot, f));
|
34
|
+
// Do not create projects for package.json files
|
35
|
+
// that are part of the package manager workspaces
|
36
|
+
// Those package.json files will be processed later on
|
37
|
+
const matcher = (0, package_json_workspaces_1.buildPackageJsonWorkspacesMatcher)(workspaceRoot, readJson);
|
38
|
+
if (!(0, fs_1.existsSync)(packageJsonPath) || matcher(relativePackageJsonPath)) {
|
32
39
|
return null;
|
33
40
|
}
|
34
41
|
try {
|
@@ -17,6 +17,14 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
17
17
|
[x: string]: any;
|
18
18
|
description?: string;
|
19
19
|
technologies?: string[];
|
20
|
+
nonAtomizedTarget?: string;
|
21
|
+
help?: {
|
22
|
+
command: string;
|
23
|
+
example: {
|
24
|
+
options?: Record<string, unknown>;
|
25
|
+
args?: string[];
|
26
|
+
};
|
27
|
+
};
|
20
28
|
};
|
21
29
|
executor?: undefined;
|
22
30
|
options?: undefined;
|
@@ -31,6 +39,14 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
31
39
|
[x: string]: any;
|
32
40
|
description?: string;
|
33
41
|
technologies?: string[];
|
42
|
+
nonAtomizedTarget?: string;
|
43
|
+
help?: {
|
44
|
+
command: string;
|
45
|
+
example: {
|
46
|
+
options?: Record<string, unknown>;
|
47
|
+
args?: string[];
|
48
|
+
};
|
49
|
+
};
|
34
50
|
};
|
35
51
|
command?: undefined;
|
36
52
|
} | {
|
@@ -44,6 +60,14 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
44
60
|
[x: string]: any;
|
45
61
|
description?: string;
|
46
62
|
technologies?: string[];
|
63
|
+
nonAtomizedTarget?: string;
|
64
|
+
help?: {
|
65
|
+
command: string;
|
66
|
+
example: {
|
67
|
+
options?: Record<string, unknown>;
|
68
|
+
args?: string[];
|
69
|
+
};
|
70
|
+
};
|
47
71
|
};
|
48
72
|
command?: undefined;
|
49
73
|
} | {
|
@@ -52,6 +76,14 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
52
76
|
[x: string]: any;
|
53
77
|
description?: string;
|
54
78
|
technologies?: string[];
|
79
|
+
nonAtomizedTarget?: string;
|
80
|
+
help?: {
|
81
|
+
command: string;
|
82
|
+
example: {
|
83
|
+
options?: Record<string, unknown>;
|
84
|
+
args?: string[];
|
85
|
+
};
|
86
|
+
};
|
55
87
|
};
|
56
88
|
command?: undefined;
|
57
89
|
options?: undefined;
|
@@ -66,6 +98,14 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
66
98
|
[x: string]: any;
|
67
99
|
description?: string;
|
68
100
|
technologies?: string[];
|
101
|
+
nonAtomizedTarget?: string;
|
102
|
+
help?: {
|
103
|
+
command: string;
|
104
|
+
example: {
|
105
|
+
options?: Record<string, unknown>;
|
106
|
+
args?: string[];
|
107
|
+
};
|
108
|
+
};
|
69
109
|
};
|
70
110
|
command?: undefined;
|
71
111
|
} | {
|
@@ -76,7 +76,7 @@ async function loadNxPlugins(plugins, root = workspace_root_1.workspaceRoot) {
|
|
76
76
|
plugins = await normalizePlugins(plugins, root);
|
77
77
|
const cleanupFunctions = [];
|
78
78
|
for (const plugin of plugins) {
|
79
|
-
const [loadedPluginPromise, cleanup] = loadingMethod(plugin, root);
|
79
|
+
const [loadedPluginPromise, cleanup] = await loadingMethod(plugin, root);
|
80
80
|
result.push(loadedPluginPromise);
|
81
81
|
cleanupFunctions.push(cleanup);
|
82
82
|
}
|
@@ -1,3 +1,3 @@
|
|
1
1
|
import { PluginConfiguration } from '../../../config/nx-json';
|
2
2
|
import { LoadedNxPlugin } from '../internal-api';
|
3
|
-
export declare function loadNxPluginInIsolation(plugin: PluginConfiguration, root?: string): readonly [Promise<LoadedNxPlugin>, () => void]
|
3
|
+
export declare function loadNxPluginInIsolation(plugin: PluginConfiguration, root?: string): Promise<readonly [Promise<LoadedNxPlugin>, () => void]>;
|
@@ -3,25 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loadNxPluginInIsolation = void 0;
|
4
4
|
const workspace_root_1 = require("../../../utils/workspace-root");
|
5
5
|
const plugin_pool_1 = require("./plugin-pool");
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
const remotePluginCache = new Map();
|
10
|
-
function loadNxPluginInIsolation(plugin, root = workspace_root_1.workspaceRoot) {
|
11
|
-
const cacheKey = JSON.stringify(plugin);
|
12
|
-
if (remotePluginCache.has(cacheKey)) {
|
13
|
-
return remotePluginCache.get(cacheKey);
|
14
|
-
}
|
15
|
-
const [loadingPlugin, cleanup] = (0, plugin_pool_1.loadRemoteNxPlugin)(plugin, root);
|
16
|
-
// We clean up plugin workers when Nx process completes.
|
17
|
-
const val = [
|
6
|
+
async function loadNxPluginInIsolation(plugin, root = workspace_root_1.workspaceRoot) {
|
7
|
+
const [loadingPlugin, cleanup] = await (0, plugin_pool_1.loadRemoteNxPlugin)(plugin, root);
|
8
|
+
return [
|
18
9
|
loadingPlugin,
|
19
10
|
() => {
|
20
11
|
cleanup();
|
21
|
-
remotePluginCache.delete(cacheKey);
|
22
12
|
},
|
23
13
|
];
|
24
|
-
remotePluginCache.set(cacheKey, val);
|
25
|
-
return val;
|
26
14
|
}
|
27
15
|
exports.loadNxPluginInIsolation = loadNxPluginInIsolation;
|
@@ -1,9 +1,11 @@
|
|
1
1
|
/// <reference types="node" />
|
2
|
+
/// <reference types="node" />
|
2
3
|
import { ProjectGraph, ProjectGraphProcessorContext } from '../../../config/project-graph';
|
3
4
|
import { PluginConfiguration } from '../../../config/nx-json';
|
4
|
-
import { CreateDependenciesContext, CreateMetadataContext,
|
5
|
+
import { CreateDependenciesContext, CreateMetadataContext, CreateNodesContextV2 } from '../public-api';
|
5
6
|
import { LoadedNxPlugin } from '../internal-api';
|
6
7
|
import { Serializable } from 'child_process';
|
8
|
+
import { Socket } from 'net';
|
7
9
|
export interface PluginWorkerLoadMessage {
|
8
10
|
type: 'load';
|
9
11
|
payload: {
|
@@ -31,7 +33,7 @@ export interface PluginWorkerCreateNodesMessage {
|
|
31
33
|
type: 'createNodes';
|
32
34
|
payload: {
|
33
35
|
configFiles: string[];
|
34
|
-
context:
|
36
|
+
context: CreateNodesContextV2;
|
35
37
|
tx: string;
|
36
38
|
};
|
37
39
|
}
|
@@ -112,9 +114,10 @@ export declare function isPluginWorkerMessage(message: Serializable): message is
|
|
112
114
|
export declare function isPluginWorkerResult(message: Serializable): message is PluginWorkerResult;
|
113
115
|
type MaybePromise<T> = T | Promise<T>;
|
114
116
|
type MessageHandlerReturn<T extends PluginWorkerMessage | PluginWorkerResult> = T extends PluginWorkerResult ? MaybePromise<PluginWorkerMessage | void> : MaybePromise<PluginWorkerResult | void>;
|
115
|
-
export declare function consumeMessage<T extends PluginWorkerMessage | PluginWorkerResult>(raw: T, handlers: {
|
117
|
+
export declare function consumeMessage<T extends PluginWorkerMessage | PluginWorkerResult>(socket: Socket, raw: T, handlers: {
|
116
118
|
[K in T['type']]: (payload: Extract<T, {
|
117
119
|
type: K;
|
118
120
|
}>['payload']) => MessageHandlerReturn<T>;
|
119
121
|
}): Promise<void>;
|
122
|
+
export declare function sendMessageOverSocket(socket: Socket, message: PluginWorkerMessage | PluginWorkerResult): void;
|
120
123
|
export {};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.consumeMessage = exports.isPluginWorkerResult = exports.isPluginWorkerMessage = void 0;
|
3
|
+
exports.sendMessageOverSocket = exports.consumeMessage = exports.isPluginWorkerResult = exports.isPluginWorkerMessage = void 0;
|
4
4
|
function isPluginWorkerMessage(message) {
|
5
5
|
return (typeof message === 'object' &&
|
6
6
|
'type' in message &&
|
@@ -10,6 +10,7 @@ function isPluginWorkerMessage(message) {
|
|
10
10
|
'createNodes',
|
11
11
|
'createDependencies',
|
12
12
|
'processProjectGraph',
|
13
|
+
'createMetadata',
|
13
14
|
].includes(message.type));
|
14
15
|
}
|
15
16
|
exports.isPluginWorkerMessage = isPluginWorkerMessage;
|
@@ -22,19 +23,24 @@ function isPluginWorkerResult(message) {
|
|
22
23
|
'createNodesResult',
|
23
24
|
'createDependenciesResult',
|
24
25
|
'processProjectGraphResult',
|
26
|
+
'createMetadataResult',
|
25
27
|
].includes(message.type));
|
26
28
|
}
|
27
29
|
exports.isPluginWorkerResult = isPluginWorkerResult;
|
28
30
|
// Takes a message and a map of handlers and calls the appropriate handler
|
29
31
|
// type safe and requires all handlers to be handled
|
30
|
-
async function consumeMessage(raw, handlers) {
|
32
|
+
async function consumeMessage(socket, raw, handlers) {
|
31
33
|
const message = raw;
|
32
34
|
const handler = handlers[message.type];
|
33
35
|
if (handler) {
|
34
36
|
const response = await handler(message.payload);
|
35
37
|
if (response) {
|
36
|
-
|
38
|
+
sendMessageOverSocket(socket, response);
|
37
39
|
}
|
38
40
|
}
|
39
41
|
}
|
40
42
|
exports.consumeMessage = consumeMessage;
|
43
|
+
function sendMessageOverSocket(socket, message) {
|
44
|
+
socket.write(JSON.stringify(message) + String.fromCodePoint(4));
|
45
|
+
}
|
46
|
+
exports.sendMessageOverSocket = sendMessageOverSocket;
|
@@ -1,3 +1,3 @@
|
|
1
1
|
import { PluginConfiguration } from '../../../config/nx-json';
|
2
2
|
import { LoadedNxPlugin } from '../internal-api';
|
3
|
-
export declare function loadRemoteNxPlugin(plugin: PluginConfiguration, root: string): [Promise<LoadedNxPlugin>, () => void]
|
3
|
+
export declare function loadRemoteNxPlugin(plugin: PluginConfiguration, root: string): Promise<[Promise<LoadedNxPlugin>, () => void]>;
|
@@ -3,55 +3,50 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loadRemoteNxPlugin = void 0;
|
4
4
|
const child_process_1 = require("child_process");
|
5
5
|
const path = require("path");
|
6
|
+
const net_1 = require("net");
|
6
7
|
// TODO (@AgentEnder): After scoped verbose logging is implemented, re-add verbose logs here.
|
7
8
|
// import { logger } from '../../utils/logger';
|
8
9
|
const internal_api_1 = require("../internal-api");
|
10
|
+
const socket_utils_1 = require("../../../daemon/socket-utils");
|
11
|
+
const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
|
12
|
+
const exit_codes_1 = require("../../../utils/exit-codes");
|
9
13
|
const messaging_1 = require("./messaging");
|
10
14
|
const cleanupFunctions = new Set();
|
11
15
|
const pluginNames = new Map();
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
...(isWorkerTypescript
|
21
|
-
? {
|
22
|
-
// Ensures that the worker uses the same tsconfig as the main process
|
23
|
-
TS_NODE_PROJECT: path.join(__dirname, '../../../../tsconfig.lib.json'),
|
24
|
-
}
|
25
|
-
: {}),
|
26
|
-
};
|
27
|
-
const worker = (0, child_process_1.fork)(workerPath, [], {
|
28
|
-
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
|
29
|
-
env,
|
30
|
-
execArgv: [
|
31
|
-
...process.execArgv,
|
32
|
-
// If the worker is typescript, we need to register ts-node
|
33
|
-
...(isWorkerTypescript ? ['-r', 'ts-node/register'] : []),
|
34
|
-
],
|
35
|
-
});
|
36
|
-
worker.send({ type: 'load', payload: { plugin, root } });
|
37
|
-
// logger.verbose(`[plugin-worker] started worker: ${worker.pid}`);
|
16
|
+
const MAX_MESSAGE_WAIT = 1000 * 60 * 5; // 5 minutes
|
17
|
+
const nxPluginWorkerCache = (global['nxPluginWorkerCache'] ??= new Map());
|
18
|
+
async function loadRemoteNxPlugin(plugin, root) {
|
19
|
+
const cacheKey = JSON.stringify({ plugin, root });
|
20
|
+
if (nxPluginWorkerCache.has(cacheKey)) {
|
21
|
+
return [nxPluginWorkerCache.get(cacheKey), () => { }];
|
22
|
+
}
|
23
|
+
const { worker, socket } = await startPluginWorker();
|
38
24
|
const pendingPromises = new Map();
|
39
25
|
const exitHandler = createWorkerExitHandler(worker, pendingPromises);
|
40
26
|
const cleanupFunction = () => {
|
41
27
|
worker.off('exit', exitHandler);
|
28
|
+
socket.destroy();
|
42
29
|
shutdownPluginWorker(worker);
|
30
|
+
nxPluginWorkerCache.delete(cacheKey);
|
43
31
|
};
|
44
32
|
cleanupFunctions.add(cleanupFunction);
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
})
|
50
|
-
(
|
51
|
-
|
52
|
-
|
53
|
-
},
|
54
|
-
|
33
|
+
const pluginPromise = new Promise((res, rej) => {
|
34
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
35
|
+
type: 'load',
|
36
|
+
payload: { plugin, root },
|
37
|
+
});
|
38
|
+
// logger.verbose(`[plugin-worker] started worker: ${worker.pid}`);
|
39
|
+
const loadTimeout = setTimeout(() => {
|
40
|
+
rej(new Error('Plugin worker timed out when loading plugin:' + plugin));
|
41
|
+
}, MAX_MESSAGE_WAIT);
|
42
|
+
socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)(createWorkerHandler(worker, pendingPromises, (val) => {
|
43
|
+
clearTimeout(loadTimeout);
|
44
|
+
res(val);
|
45
|
+
}, rej, socket)));
|
46
|
+
worker.on('exit', exitHandler);
|
47
|
+
});
|
48
|
+
nxPluginWorkerCache.set(cacheKey, pluginPromise);
|
49
|
+
return [pluginPromise, cleanupFunction];
|
55
50
|
}
|
56
51
|
exports.loadRemoteNxPlugin = loadRemoteNxPlugin;
|
57
52
|
function shutdownPluginWorker(worker) {
|
@@ -68,13 +63,15 @@ function shutdownPluginWorker(worker) {
|
|
68
63
|
* @param onloadError Rejecter for RemotePlugin promise
|
69
64
|
* @returns Function to handle messages from the worker
|
70
65
|
*/
|
71
|
-
function createWorkerHandler(worker, pending, onload, onloadError) {
|
66
|
+
function createWorkerHandler(worker, pending, onload, onloadError, socket) {
|
72
67
|
let pluginName;
|
73
|
-
|
68
|
+
let txId = 0;
|
69
|
+
return function (raw) {
|
70
|
+
const message = JSON.parse(raw);
|
74
71
|
if (!(0, messaging_1.isPluginWorkerResult)(message)) {
|
75
72
|
return;
|
76
73
|
}
|
77
|
-
return (0, messaging_1.consumeMessage)(message, {
|
74
|
+
return (0, messaging_1.consumeMessage)(socket, message, {
|
78
75
|
'load-result': (result) => {
|
79
76
|
if (result.success) {
|
80
77
|
const { name, createNodesPattern, include, exclude } = result;
|
@@ -88,9 +85,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
88
85
|
? [
|
89
86
|
createNodesPattern,
|
90
87
|
(configFiles, ctx) => {
|
91
|
-
const tx = pluginName + ':createNodes:' +
|
88
|
+
const tx = pluginName + worker.pid + ':createNodes:' + txId++;
|
92
89
|
return registerPendingPromise(tx, pending, () => {
|
93
|
-
|
90
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
94
91
|
type: 'createNodes',
|
95
92
|
payload: { configFiles, context: ctx, tx },
|
96
93
|
});
|
@@ -100,9 +97,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
100
97
|
: undefined,
|
101
98
|
createDependencies: result.hasCreateDependencies
|
102
99
|
? (ctx) => {
|
103
|
-
const tx = pluginName + ':createDependencies:' +
|
100
|
+
const tx = pluginName + worker.pid + ':createDependencies:' + txId++;
|
104
101
|
return registerPendingPromise(tx, pending, () => {
|
105
|
-
|
102
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
106
103
|
type: 'createDependencies',
|
107
104
|
payload: { context: ctx, tx },
|
108
105
|
});
|
@@ -111,9 +108,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
111
108
|
: undefined,
|
112
109
|
processProjectGraph: result.hasProcessProjectGraph
|
113
110
|
? (graph, ctx) => {
|
114
|
-
const tx = pluginName + ':processProjectGraph:' +
|
111
|
+
const tx = pluginName + worker.pid + ':processProjectGraph:' + txId++;
|
115
112
|
return registerPendingPromise(tx, pending, () => {
|
116
|
-
|
113
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
117
114
|
type: 'processProjectGraph',
|
118
115
|
payload: { graph, ctx, tx },
|
119
116
|
});
|
@@ -122,9 +119,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
122
119
|
: undefined,
|
123
120
|
createMetadata: result.hasCreateMetadata
|
124
121
|
? (graph, ctx) => {
|
125
|
-
const tx = pluginName + ':createMetadata:' +
|
122
|
+
const tx = pluginName + worker.pid + ':createMetadata:' + txId++;
|
126
123
|
return registerPendingPromise(tx, pending, () => {
|
127
|
-
|
124
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
128
125
|
type: 'createMetadata',
|
129
126
|
payload: { graph, context: ctx, tx },
|
130
127
|
});
|
@@ -183,19 +180,31 @@ function createWorkerExitHandler(worker, pendingPromises) {
|
|
183
180
|
}
|
184
181
|
};
|
185
182
|
}
|
186
|
-
|
183
|
+
let cleanedUp = false;
|
184
|
+
const exitHandler = () => {
|
187
185
|
for (const fn of cleanupFunctions) {
|
188
186
|
fn();
|
189
187
|
}
|
188
|
+
cleanedUp = true;
|
189
|
+
};
|
190
|
+
process.on('exit', exitHandler);
|
191
|
+
process.on('SIGINT', () => {
|
192
|
+
exitHandler();
|
193
|
+
process.exit((0, exit_codes_1.signalToCode)('SIGINT'));
|
190
194
|
});
|
195
|
+
process.on('SIGTERM', exitHandler);
|
191
196
|
function registerPendingPromise(tx, pending, callback) {
|
192
|
-
let resolver, rejector;
|
197
|
+
let resolver, rejector, timeout;
|
193
198
|
const promise = new Promise((res, rej) => {
|
194
|
-
resolver = res;
|
195
199
|
rejector = rej;
|
200
|
+
resolver = res;
|
201
|
+
timeout = setTimeout(() => {
|
202
|
+
rej(new Error(`Plugin worker timed out when processing message ${tx}`));
|
203
|
+
}, MAX_MESSAGE_WAIT);
|
196
204
|
callback();
|
197
205
|
}).finally(() => {
|
198
206
|
pending.delete(tx);
|
207
|
+
clearTimeout(timeout);
|
199
208
|
});
|
200
209
|
pending.set(tx, {
|
201
210
|
promise,
|
@@ -204,3 +213,70 @@ function registerPendingPromise(tx, pending, callback) {
|
|
204
213
|
});
|
205
214
|
return promise;
|
206
215
|
}
|
216
|
+
global.nxPluginWorkerCount ??= 0;
|
217
|
+
async function startPluginWorker() {
|
218
|
+
// this should only really be true when running unit tests within
|
219
|
+
// the Nx repo. We still need to start the worker in this case,
|
220
|
+
// but its typescript.
|
221
|
+
const isWorkerTypescript = path.extname(__filename) === '.ts';
|
222
|
+
const workerPath = path.join(__dirname, 'plugin-worker');
|
223
|
+
const env = {
|
224
|
+
...process.env,
|
225
|
+
...(isWorkerTypescript
|
226
|
+
? {
|
227
|
+
// Ensures that the worker uses the same tsconfig as the main process
|
228
|
+
TS_NODE_PROJECT: path.join(__dirname, '../../../../tsconfig.lib.json'),
|
229
|
+
}
|
230
|
+
: {}),
|
231
|
+
};
|
232
|
+
const ipcPath = (0, socket_utils_1.getPluginOsSocketPath)([process.pid, global.nxPluginWorkerCount++].join('-'));
|
233
|
+
const worker = (0, child_process_1.spawn)(process.execPath, [
|
234
|
+
...(isWorkerTypescript ? ['--require', 'ts-node/register'] : []),
|
235
|
+
workerPath,
|
236
|
+
ipcPath,
|
237
|
+
], {
|
238
|
+
stdio: process.stdout.isTTY ? 'inherit' : 'ignore',
|
239
|
+
env,
|
240
|
+
detached: true,
|
241
|
+
shell: false,
|
242
|
+
windowsHide: true,
|
243
|
+
});
|
244
|
+
worker.unref();
|
245
|
+
let attempts = 0;
|
246
|
+
return new Promise((resolve, reject) => {
|
247
|
+
const id = setInterval(async () => {
|
248
|
+
const socket = await isServerAvailable(ipcPath);
|
249
|
+
if (socket) {
|
250
|
+
socket.unref();
|
251
|
+
clearInterval(id);
|
252
|
+
resolve({
|
253
|
+
worker,
|
254
|
+
socket,
|
255
|
+
});
|
256
|
+
}
|
257
|
+
else if (attempts > 1000) {
|
258
|
+
// daemon fails to start, the process probably exited
|
259
|
+
// we print the logs and exit the client
|
260
|
+
reject('Failed to start plugin worker.');
|
261
|
+
}
|
262
|
+
else {
|
263
|
+
attempts++;
|
264
|
+
}
|
265
|
+
}, 10);
|
266
|
+
});
|
267
|
+
}
|
268
|
+
function isServerAvailable(ipcPath) {
|
269
|
+
return new Promise((resolve) => {
|
270
|
+
try {
|
271
|
+
const socket = (0, net_1.connect)(ipcPath, () => {
|
272
|
+
resolve(socket);
|
273
|
+
});
|
274
|
+
socket.once('error', () => {
|
275
|
+
resolve(false);
|
276
|
+
});
|
277
|
+
}
|
278
|
+
catch (err) {
|
279
|
+
resolve(false);
|
280
|
+
}
|
281
|
+
});
|
282
|
+
}
|