nx 19.4.0-beta.0 → 19.4.0-beta.2
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/release/changelog-renderer/index.d.ts +6 -0
- package/release/changelog-renderer/index.js +1 -1
- 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 +12 -1
- package/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +5 -6
- package/src/command-line/release/changelog.js +6 -1
- package/src/command-line/release/config/config.js +3 -0
- package/src/command-line/run/command-object.js +2 -1
- package/src/config/workspace-json-project-json.d.ts +2 -0
- package/src/core/graph/main.js +1 -1
- package/src/core/graph/styles.css +1 -1
- package/src/daemon/client/client.d.ts +5 -0
- package/src/daemon/client/client.js +14 -0
- package/src/daemon/message-types/task-history.d.ts +13 -0
- package/src/daemon/message-types/task-history.js +19 -0
- package/src/daemon/server/handle-get-task-history.d.ts +4 -0
- package/src/daemon/server/handle-get-task-history.js +12 -0
- package/src/daemon/server/handle-write-task-runs-to-history.d.ts +5 -0
- package/src/daemon/server/handle-write-task-runs-to-history.js +12 -0
- package/src/daemon/server/plugins.js +12 -2
- package/src/daemon/server/server.js +9 -0
- 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.js +29 -20
- 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 +47 -11
- package/src/plugins/package-json-workspaces/create-nodes.js +12 -7
- package/src/plugins/project-json/build-nodes/package-json-next-to-project-json.js +10 -2
- package/src/plugins/target-defaults/target-defaults-plugin.d.ts +5 -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 +8 -13
- 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 +123 -43
- package/src/project-graph/plugins/isolation/plugin-worker.js +128 -107
- package/src/project-graph/project-graph.js +7 -1
- package/src/project-graph/utils/normalize-project-nodes.d.ts +1 -5
- package/src/project-graph/utils/normalize-project-nodes.js +2 -17
- package/src/project-graph/utils/project-configuration-utils.js +14 -3
- package/src/project-graph/utils/retrieve-workspace-files.d.ts +3 -3
- package/src/tasks-runner/default-tasks-runner.js +2 -2
- package/src/tasks-runner/life-cycle.d.ts +10 -10
- package/src/tasks-runner/life-cycle.js +10 -10
- package/src/tasks-runner/life-cycles/task-history-life-cycle.d.ts +9 -0
- package/src/tasks-runner/life-cycles/task-history-life-cycle.js +54 -0
- package/src/tasks-runner/run-command.js +6 -0
- package/src/tasks-runner/task-env.d.ts +13 -0
- package/src/tasks-runner/task-env.js +41 -26
- package/src/tasks-runner/task-orchestrator.js +4 -4
- package/src/utils/git-utils.d.ts +1 -1
- package/src/utils/git-utils.js +13 -2
- package/src/utils/nx-cloud-utils.d.ts +1 -1
- package/src/utils/nx-cloud-utils.js +1 -1
- package/src/utils/package-json.d.ts +3 -0
- package/src/utils/package-json.js +15 -3
- package/src/utils/serialize-target.d.ts +1 -0
- package/src/utils/serialize-target.js +7 -0
- package/src/utils/task-history.d.ts +8 -0
- package/src/utils/task-history.js +97 -0
@@ -1,16 +1,16 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.shortenedCloudUrl = void 0;
|
3
|
+
exports.repoUsesGithub = exports.shortenedCloudUrl = void 0;
|
4
4
|
const devkit_exports_1 = require("../../devkit-exports");
|
5
5
|
const git_utils_1 = require("../../utils/git-utils");
|
6
|
-
|
6
|
+
const semver_1 = require("semver");
|
7
|
+
async function shortenedCloudUrl(installationSource, accessToken, usesGithub) {
|
7
8
|
const githubSlug = (0, git_utils_1.getGithubSlugOrNull)();
|
8
9
|
const apiUrl = removeTrailingSlash(process.env.NX_CLOUD_API || process.env.NRWL_API || `https://cloud.nx.app`);
|
9
|
-
const
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
installationSupportsGitHub);
|
10
|
+
const version = await getNxCloudVersion(apiUrl);
|
11
|
+
if (version && (0, semver_1.lt)(truncateToSemver(version), '2406.11.5')) {
|
12
|
+
return apiUrl;
|
13
|
+
}
|
14
14
|
const source = getSource(installationSource);
|
15
15
|
try {
|
16
16
|
const response = await require('axios').post(`${apiUrl}/nx-cloud/onboarding`, {
|
@@ -27,10 +27,20 @@ async function shortenedCloudUrl(installationSource, accessToken, github) {
|
|
27
27
|
catch (e) {
|
28
28
|
devkit_exports_1.logger.verbose(`Failed to shorten Nx Cloud URL.
|
29
29
|
${e}`);
|
30
|
-
return getURLifShortenFailed(usesGithub, githubSlug, apiUrl,
|
30
|
+
return getURLifShortenFailed(usesGithub, githubSlug, apiUrl, source, accessToken);
|
31
31
|
}
|
32
32
|
}
|
33
33
|
exports.shortenedCloudUrl = shortenedCloudUrl;
|
34
|
+
async function repoUsesGithub(github) {
|
35
|
+
const githubSlug = (0, git_utils_1.getGithubSlugOrNull)();
|
36
|
+
const apiUrl = removeTrailingSlash(process.env.NX_CLOUD_API || process.env.NRWL_API || `https://cloud.nx.app`);
|
37
|
+
const installationSupportsGitHub = await getInstallationSupportsGitHub(apiUrl);
|
38
|
+
return ((githubSlug || github) &&
|
39
|
+
(apiUrl.includes('cloud.nx.app') ||
|
40
|
+
apiUrl.includes('eu.nx.app') ||
|
41
|
+
installationSupportsGitHub));
|
42
|
+
}
|
43
|
+
exports.repoUsesGithub = repoUsesGithub;
|
34
44
|
function removeTrailingSlash(apiUrl) {
|
35
45
|
return apiUrl[apiUrl.length - 1] === '/' ? apiUrl.slice(0, -1) : apiUrl;
|
36
46
|
}
|
@@ -48,13 +58,13 @@ function getSource(installationSource) {
|
|
48
58
|
return 'other';
|
49
59
|
}
|
50
60
|
}
|
51
|
-
function getURLifShortenFailed(usesGithub, githubSlug, apiUrl,
|
61
|
+
function getURLifShortenFailed(usesGithub, githubSlug, apiUrl, source, accessToken) {
|
52
62
|
if (usesGithub) {
|
53
63
|
if (githubSlug) {
|
54
|
-
return `${apiUrl}/setup/connect-workspace/
|
64
|
+
return `${apiUrl}/setup/connect-workspace/github/connect?name=${encodeURIComponent(githubSlug)}&source=${source}`;
|
55
65
|
}
|
56
66
|
else {
|
57
|
-
return `${apiUrl}/setup/connect-workspace/
|
67
|
+
return `${apiUrl}/setup/connect-workspace/github/select&source=${source}`;
|
58
68
|
}
|
59
69
|
}
|
60
70
|
return `${apiUrl}/setup/connect-workspace/manual?accessToken=${accessToken}&source=${source}`;
|
@@ -75,3 +85,29 @@ async function getInstallationSupportsGitHub(apiUrl) {
|
|
75
85
|
return false;
|
76
86
|
}
|
77
87
|
}
|
88
|
+
async function getNxCloudVersion(apiUrl) {
|
89
|
+
try {
|
90
|
+
const response = await require('axios').get(`${apiUrl}/version`, {
|
91
|
+
responseType: 'document',
|
92
|
+
});
|
93
|
+
const version = extractVersion(response.data);
|
94
|
+
if (!version) {
|
95
|
+
throw new Error('Failed to extract version from response.');
|
96
|
+
}
|
97
|
+
return version;
|
98
|
+
}
|
99
|
+
catch (e) {
|
100
|
+
devkit_exports_1.logger.verbose(`Failed to get version of Nx Cloud.
|
101
|
+
${e}`);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
function extractVersion(htmlString) {
|
105
|
+
// The pattern assumes 'Version' is inside an h1 tag and the version number is the next span's content
|
106
|
+
const regex = /<h1[^>]*>Version<\/h1>\s*<div[^>]*><div[^>]*><div[^>]*><span[^>]*>([^<]+)<\/span>/;
|
107
|
+
const match = htmlString.match(regex);
|
108
|
+
return match ? match[1].trim() : null;
|
109
|
+
}
|
110
|
+
function truncateToSemver(versionString) {
|
111
|
+
// version may be something like 2406.13.5.hotfix2
|
112
|
+
return versionString.split(/[\.-]/).slice(0, 3).join('.');
|
113
|
+
}
|
@@ -73,20 +73,25 @@ function buildProjectConfigurationFromPackageJson(packageJson, workspaceRoot, pa
|
|
73
73
|
throw new Error('Nx requires the root package.json to specify a name if it is being used as an Nx project.');
|
74
74
|
}
|
75
75
|
let name = packageJson.name ?? (0, to_project_name_1.toProjectName)(normalizedPath);
|
76
|
-
const
|
77
|
-
nxJson?.workspaceLayout?.appsDir &&
|
78
|
-
projectRoot.startsWith(nxJson.workspaceLayout.appsDir)
|
79
|
-
? 'application'
|
80
|
-
: 'library';
|
81
|
-
return {
|
76
|
+
const projectConfiguration = {
|
82
77
|
root: projectRoot,
|
83
78
|
sourceRoot: projectRoot,
|
84
79
|
name,
|
85
|
-
projectType,
|
86
80
|
...packageJson.nx,
|
87
81
|
targets: (0, package_json_1.readTargetsFromPackageJson)(packageJson),
|
82
|
+
tags: (0, package_json_1.getTagsFromPackageJson)(packageJson),
|
88
83
|
metadata: (0, package_json_1.getMetadataFromPackageJson)(packageJson),
|
89
84
|
};
|
85
|
+
if (nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir &&
|
86
|
+
nxJson?.workspaceLayout?.appsDir &&
|
87
|
+
projectRoot.startsWith(nxJson.workspaceLayout.appsDir)) {
|
88
|
+
projectConfiguration.projectType = 'application';
|
89
|
+
}
|
90
|
+
else if (typeof nxJson?.workspaceLayout?.libsDir !== 'undefined' &&
|
91
|
+
projectRoot.startsWith(nxJson.workspaceLayout.libsDir)) {
|
92
|
+
projectConfiguration.projectType = 'library';
|
93
|
+
}
|
94
|
+
return projectConfiguration;
|
90
95
|
}
|
91
96
|
exports.buildProjectConfigurationFromPackageJson = buildProjectConfigurationFromPackageJson;
|
92
97
|
/**
|
@@ -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 {
|
@@ -40,6 +47,7 @@ function createProjectFromPackageJsonNextToProjectJson(projectJsonPath, workspac
|
|
40
47
|
root,
|
41
48
|
targets: (0, package_json_1.readTargetsFromPackageJson)(packageJson),
|
42
49
|
metadata: (0, package_json_1.getMetadataFromPackageJson)(packageJson),
|
50
|
+
tags: (0, package_json_1.getTagsFromPackageJson)(packageJson),
|
43
51
|
};
|
44
52
|
}
|
45
53
|
catch (e) {
|
@@ -17,6 +17,7 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
17
17
|
[x: string]: any;
|
18
18
|
description?: string;
|
19
19
|
technologies?: string[];
|
20
|
+
nonAtomizedTarget?: string;
|
20
21
|
};
|
21
22
|
executor?: undefined;
|
22
23
|
options?: undefined;
|
@@ -31,6 +32,7 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
31
32
|
[x: string]: any;
|
32
33
|
description?: string;
|
33
34
|
technologies?: string[];
|
35
|
+
nonAtomizedTarget?: string;
|
34
36
|
};
|
35
37
|
command?: undefined;
|
36
38
|
} | {
|
@@ -44,6 +46,7 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
44
46
|
[x: string]: any;
|
45
47
|
description?: string;
|
46
48
|
technologies?: string[];
|
49
|
+
nonAtomizedTarget?: string;
|
47
50
|
};
|
48
51
|
command?: undefined;
|
49
52
|
} | {
|
@@ -52,6 +55,7 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
52
55
|
[x: string]: any;
|
53
56
|
description?: string;
|
54
57
|
technologies?: string[];
|
58
|
+
nonAtomizedTarget?: string;
|
55
59
|
};
|
56
60
|
command?: undefined;
|
57
61
|
options?: undefined;
|
@@ -66,6 +70,7 @@ export declare function getTargetInfo(target: string, projectJsonTargets: Record
|
|
66
70
|
[x: string]: any;
|
67
71
|
description?: string;
|
68
72
|
technologies?: string[];
|
73
|
+
nonAtomizedTarget?: string;
|
69
74
|
};
|
70
75
|
command?: undefined;
|
71
76
|
} | {
|
@@ -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): [Promise<LoadedNxPlugin>, () => void]
|
3
|
+
export declare function loadNxPluginInIsolation(plugin: PluginConfiguration, root?: string): Promise<readonly [Promise<LoadedNxPlugin>, () => void]>;
|
@@ -3,18 +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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
}
|
15
|
-
const loadingPlugin = (0, plugin_pool_1.loadRemoteNxPlugin)(plugin, root);
|
16
|
-
remotePluginCache.set(cacheKey, loadingPlugin);
|
17
|
-
// We clean up plugin workers when Nx process completes.
|
18
|
-
return [loadingPlugin, () => { }];
|
6
|
+
async function loadNxPluginInIsolation(plugin, root = workspace_root_1.workspaceRoot) {
|
7
|
+
const [loadingPlugin, cleanup] = await (0, plugin_pool_1.loadRemoteNxPlugin)(plugin, root);
|
8
|
+
return [
|
9
|
+
loadingPlugin,
|
10
|
+
() => {
|
11
|
+
cleanup();
|
12
|
+
},
|
13
|
+
];
|
19
14
|
}
|
20
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>;
|
3
|
+
export declare function loadRemoteNxPlugin(plugin: PluginConfiguration, root: string): Promise<[Promise<LoadedNxPlugin>, () => void]>;
|
@@ -6,46 +6,46 @@ const path = require("path");
|
|
6
6
|
// TODO (@AgentEnder): After scoped verbose logging is implemented, re-add verbose logs here.
|
7
7
|
// import { logger } from '../../utils/logger';
|
8
8
|
const internal_api_1 = require("../internal-api");
|
9
|
+
const socket_utils_1 = require("../../../daemon/socket-utils");
|
10
|
+
const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
|
9
11
|
const messaging_1 = require("./messaging");
|
12
|
+
const net_1 = require("net");
|
10
13
|
const cleanupFunctions = new Set();
|
11
14
|
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}`);
|
15
|
+
const MAX_MESSAGE_WAIT = 1000 * 60 * 5; // 5 minutes
|
16
|
+
const nxPluginWorkerCache = (global['nxPluginWorkerCache'] ??= new Map());
|
17
|
+
async function loadRemoteNxPlugin(plugin, root) {
|
18
|
+
const cacheKey = JSON.stringify({ plugin, root });
|
19
|
+
if (nxPluginWorkerCache.has(cacheKey)) {
|
20
|
+
return [nxPluginWorkerCache.get(cacheKey), () => { }];
|
21
|
+
}
|
22
|
+
const { worker, socket } = await startPluginWorker();
|
38
23
|
const pendingPromises = new Map();
|
39
24
|
const exitHandler = createWorkerExitHandler(worker, pendingPromises);
|
40
25
|
const cleanupFunction = () => {
|
41
26
|
worker.off('exit', exitHandler);
|
27
|
+
socket.destroy();
|
42
28
|
shutdownPluginWorker(worker);
|
29
|
+
nxPluginWorkerCache.delete(cacheKey);
|
43
30
|
};
|
44
31
|
cleanupFunctions.add(cleanupFunction);
|
45
|
-
|
46
|
-
|
32
|
+
const pluginPromise = new Promise((res, rej) => {
|
33
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
34
|
+
type: 'load',
|
35
|
+
payload: { plugin, root },
|
36
|
+
});
|
37
|
+
// logger.verbose(`[plugin-worker] started worker: ${worker.pid}`);
|
38
|
+
const loadTimeout = setTimeout(() => {
|
39
|
+
rej(new Error('Plugin worker timed out when loading plugin:' + plugin));
|
40
|
+
}, MAX_MESSAGE_WAIT);
|
41
|
+
socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)(createWorkerHandler(worker, pendingPromises, (val) => {
|
42
|
+
clearTimeout(loadTimeout);
|
43
|
+
res(val);
|
44
|
+
}, rej, socket)));
|
47
45
|
worker.on('exit', exitHandler);
|
48
46
|
});
|
47
|
+
nxPluginWorkerCache.set(cacheKey, pluginPromise);
|
48
|
+
return [pluginPromise, cleanupFunction];
|
49
49
|
}
|
50
50
|
exports.loadRemoteNxPlugin = loadRemoteNxPlugin;
|
51
51
|
function shutdownPluginWorker(worker) {
|
@@ -62,13 +62,15 @@ function shutdownPluginWorker(worker) {
|
|
62
62
|
* @param onloadError Rejecter for RemotePlugin promise
|
63
63
|
* @returns Function to handle messages from the worker
|
64
64
|
*/
|
65
|
-
function createWorkerHandler(worker, pending, onload, onloadError) {
|
65
|
+
function createWorkerHandler(worker, pending, onload, onloadError, socket) {
|
66
66
|
let pluginName;
|
67
|
-
|
67
|
+
let txId = 0;
|
68
|
+
return function (raw) {
|
69
|
+
const message = JSON.parse(raw);
|
68
70
|
if (!(0, messaging_1.isPluginWorkerResult)(message)) {
|
69
71
|
return;
|
70
72
|
}
|
71
|
-
return (0, messaging_1.consumeMessage)(message, {
|
73
|
+
return (0, messaging_1.consumeMessage)(socket, message, {
|
72
74
|
'load-result': (result) => {
|
73
75
|
if (result.success) {
|
74
76
|
const { name, createNodesPattern, include, exclude } = result;
|
@@ -82,9 +84,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
82
84
|
? [
|
83
85
|
createNodesPattern,
|
84
86
|
(configFiles, ctx) => {
|
85
|
-
const tx = pluginName + ':createNodes:' +
|
87
|
+
const tx = pluginName + worker.pid + ':createNodes:' + txId++;
|
86
88
|
return registerPendingPromise(tx, pending, () => {
|
87
|
-
|
89
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
88
90
|
type: 'createNodes',
|
89
91
|
payload: { configFiles, context: ctx, tx },
|
90
92
|
});
|
@@ -94,9 +96,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
94
96
|
: undefined,
|
95
97
|
createDependencies: result.hasCreateDependencies
|
96
98
|
? (ctx) => {
|
97
|
-
const tx = pluginName + ':createDependencies:' +
|
99
|
+
const tx = pluginName + worker.pid + ':createDependencies:' + txId++;
|
98
100
|
return registerPendingPromise(tx, pending, () => {
|
99
|
-
|
101
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
100
102
|
type: 'createDependencies',
|
101
103
|
payload: { context: ctx, tx },
|
102
104
|
});
|
@@ -105,9 +107,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
105
107
|
: undefined,
|
106
108
|
processProjectGraph: result.hasProcessProjectGraph
|
107
109
|
? (graph, ctx) => {
|
108
|
-
const tx = pluginName + ':processProjectGraph:' +
|
110
|
+
const tx = pluginName + worker.pid + ':processProjectGraph:' + txId++;
|
109
111
|
return registerPendingPromise(tx, pending, () => {
|
110
|
-
|
112
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
111
113
|
type: 'processProjectGraph',
|
112
114
|
payload: { graph, ctx, tx },
|
113
115
|
});
|
@@ -116,9 +118,9 @@ function createWorkerHandler(worker, pending, onload, onloadError) {
|
|
116
118
|
: undefined,
|
117
119
|
createMetadata: result.hasCreateMetadata
|
118
120
|
? (graph, ctx) => {
|
119
|
-
const tx = pluginName + ':createMetadata:' +
|
121
|
+
const tx = pluginName + worker.pid + ':createMetadata:' + txId++;
|
120
122
|
return registerPendingPromise(tx, pending, () => {
|
121
|
-
|
123
|
+
(0, messaging_1.sendMessageOverSocket)(socket, {
|
122
124
|
type: 'createMetadata',
|
123
125
|
payload: { graph, context: ctx, tx },
|
124
126
|
});
|
@@ -177,19 +179,30 @@ function createWorkerExitHandler(worker, pendingPromises) {
|
|
177
179
|
}
|
178
180
|
};
|
179
181
|
}
|
180
|
-
|
182
|
+
let cleanedUp = false;
|
183
|
+
const exitHandler = () => {
|
184
|
+
if (cleanedUp)
|
185
|
+
return;
|
181
186
|
for (const fn of cleanupFunctions) {
|
182
187
|
fn();
|
183
188
|
}
|
184
|
-
|
189
|
+
cleanedUp = true;
|
190
|
+
};
|
191
|
+
process.on('exit', exitHandler);
|
192
|
+
process.on('SIGINT', exitHandler);
|
193
|
+
process.on('SIGTERM', exitHandler);
|
185
194
|
function registerPendingPromise(tx, pending, callback) {
|
186
|
-
let resolver, rejector;
|
195
|
+
let resolver, rejector, timeout;
|
187
196
|
const promise = new Promise((res, rej) => {
|
188
|
-
resolver = res;
|
189
197
|
rejector = rej;
|
198
|
+
resolver = res;
|
199
|
+
timeout = setTimeout(() => {
|
200
|
+
rej(new Error(`Plugin worker timed out when processing message ${tx}`));
|
201
|
+
}, MAX_MESSAGE_WAIT);
|
190
202
|
callback();
|
191
203
|
}).finally(() => {
|
192
204
|
pending.delete(tx);
|
205
|
+
clearTimeout(timeout);
|
193
206
|
});
|
194
207
|
pending.set(tx, {
|
195
208
|
promise,
|
@@ -198,3 +211,70 @@ function registerPendingPromise(tx, pending, callback) {
|
|
198
211
|
});
|
199
212
|
return promise;
|
200
213
|
}
|
214
|
+
global.nxPluginWorkerCount ??= 0;
|
215
|
+
async function startPluginWorker() {
|
216
|
+
// this should only really be true when running unit tests within
|
217
|
+
// the Nx repo. We still need to start the worker in this case,
|
218
|
+
// but its typescript.
|
219
|
+
const isWorkerTypescript = path.extname(__filename) === '.ts';
|
220
|
+
const workerPath = path.join(__dirname, 'plugin-worker');
|
221
|
+
const env = {
|
222
|
+
...process.env,
|
223
|
+
...(isWorkerTypescript
|
224
|
+
? {
|
225
|
+
// Ensures that the worker uses the same tsconfig as the main process
|
226
|
+
TS_NODE_PROJECT: path.join(__dirname, '../../../../tsconfig.lib.json'),
|
227
|
+
}
|
228
|
+
: {}),
|
229
|
+
};
|
230
|
+
const ipcPath = (0, socket_utils_1.getPluginOsSocketPath)([process.pid, global.nxPluginWorkerCount++].join('-'));
|
231
|
+
const worker = (0, child_process_1.fork)(workerPath, [ipcPath], {
|
232
|
+
stdio: process.stdout.isTTY ? 'inherit' : 'ignore',
|
233
|
+
env,
|
234
|
+
execArgv: [
|
235
|
+
...process.execArgv,
|
236
|
+
// If the worker is typescript, we need to register ts-node
|
237
|
+
...(isWorkerTypescript ? ['-r', 'ts-node/register'] : []),
|
238
|
+
],
|
239
|
+
detached: true,
|
240
|
+
});
|
241
|
+
worker.disconnect();
|
242
|
+
worker.unref();
|
243
|
+
let attempts = 0;
|
244
|
+
return new Promise((resolve, reject) => {
|
245
|
+
const id = setInterval(async () => {
|
246
|
+
const socket = await isServerAvailable(ipcPath);
|
247
|
+
if (socket) {
|
248
|
+
socket.unref();
|
249
|
+
clearInterval(id);
|
250
|
+
resolve({
|
251
|
+
worker,
|
252
|
+
socket,
|
253
|
+
});
|
254
|
+
}
|
255
|
+
else if (attempts > 1000) {
|
256
|
+
// daemon fails to start, the process probably exited
|
257
|
+
// we print the logs and exit the client
|
258
|
+
reject('Failed to start plugin worker.');
|
259
|
+
}
|
260
|
+
else {
|
261
|
+
attempts++;
|
262
|
+
}
|
263
|
+
}, 10);
|
264
|
+
});
|
265
|
+
}
|
266
|
+
function isServerAvailable(ipcPath) {
|
267
|
+
return new Promise((resolve) => {
|
268
|
+
try {
|
269
|
+
const socket = (0, net_1.connect)(ipcPath, () => {
|
270
|
+
resolve(socket);
|
271
|
+
});
|
272
|
+
socket.once('error', () => {
|
273
|
+
resolve(false);
|
274
|
+
});
|
275
|
+
}
|
276
|
+
catch (err) {
|
277
|
+
resolve(false);
|
278
|
+
}
|
279
|
+
});
|
280
|
+
}
|