nx 23.3.0-beta.0 → 23.3.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/dist/src/command-line/format/format.js +2 -18
- package/dist/src/command-line/graph/graph.js +42 -3
- package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +0 -3
- package/dist/src/command-line/init/implementation/add-nx-to-nest.js +0 -3
- package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +0 -3
- package/dist/src/command-line/init/implementation/angular/index.js +0 -3
- package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +0 -3
- package/dist/src/command-line/init/implementation/format.js +2 -3
- package/dist/src/command-line/init/implementation/utils.d.ts +0 -1
- package/dist/src/command-line/init/implementation/utils.js +0 -8
- package/dist/src/command-line/init/init-v2.js +0 -3
- package/dist/src/command-line/migrate/agentic/definitions.js +75 -19
- package/dist/src/command-line/migrate/agentic/format-command.d.ts +14 -0
- package/dist/src/command-line/migrate/agentic/format-command.js +44 -0
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +3 -3
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +3 -3
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +27 -17
- package/dist/src/command-line/migrate/agentic/handoff.js +107 -24
- package/dist/src/command-line/migrate/agentic/instruction-files.d.ts +21 -0
- package/dist/src/command-line/migrate/agentic/instruction-files.js +36 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.d.ts +16 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.js +70 -0
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +4 -3
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +25 -1
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +57 -34
- package/dist/src/command-line/migrate/agentic/run-step.js +28 -4
- package/dist/src/command-line/migrate/agentic/runner.d.ts +17 -8
- package/dist/src/command-line/migrate/agentic/runner.js +110 -55
- package/dist/src/command-line/migrate/agentic/types.d.ts +23 -8
- package/dist/src/command-line/migrate/agentic/types.js +10 -7
- package/dist/src/command-line/migrate/migrate-commits.d.ts +1 -0
- package/dist/src/command-line/migrate/migrate-commits.js +9 -0
- package/dist/src/command-line/migrate/migrate-output.d.ts +3 -7
- package/dist/src/command-line/migrate/migrate-output.js +3 -7
- package/dist/src/command-line/migrate/migrate.js +21 -8
- package/dist/src/command-line/migrate/run/agent-output.d.ts +10 -0
- package/dist/src/command-line/migrate/run/agent-output.js +16 -2
- package/dist/src/command-line/migrate/run/agent-work-payload.d.ts +37 -0
- package/dist/src/command-line/migrate/run/agent-work-payload.js +142 -0
- package/dist/src/command-line/migrate/run/atomic-write.d.ts +8 -0
- package/dist/src/command-line/migrate/run/atomic-write.js +17 -0
- package/dist/src/command-line/migrate/run/index.d.ts +1 -1
- package/dist/src/command-line/migrate/run/issues.d.ts +106 -0
- package/dist/src/command-line/migrate/run/issues.js +980 -0
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +1 -0
- package/dist/src/command-line/migrate/run/orchestrator.js +662 -105
- package/dist/src/command-line/migrate/run/run-state.d.ts +43 -8
- package/dist/src/command-line/migrate/run/run-state.js +184 -20
- package/dist/src/command-line/migrate/run/runbook.d.ts +17 -0
- package/dist/src/command-line/migrate/run/runbook.js +97 -0
- package/dist/src/command-line/migrate/run/state-lock.d.ts +2 -2
- package/dist/src/command-line/migrate/run/state-lock.js +2 -2
- package/dist/src/command-line/migrate/run/state-machine.d.ts +5 -1
- package/dist/src/command-line/migrate/run/state-machine.js +27 -1
- package/dist/src/command-line/migrate/run/worker.js +206 -56
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +5 -11
- package/dist/src/command-line/release/publish.js +11 -4
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +3 -3
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +3 -3
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +2 -0
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.js +35 -0
- package/dist/src/command-line/report/report.d.ts +2 -0
- package/dist/src/command-line/report/report.js +28 -1
- package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
- package/dist/src/command-line/yargs-utils/shared-options.js +22 -4
- package/dist/src/config/workspace-json-project-json.d.ts +2 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/client/client.d.ts +6 -2
- package/dist/src/daemon/client/client.js +27 -11
- package/dist/src/daemon/message-types/get-plugin-capabilities.d.ts +5 -0
- package/dist/src/daemon/message-types/get-plugin-capabilities.js +11 -0
- package/dist/src/daemon/message-types/run-tasks-execution-hooks.d.ts +3 -2
- package/dist/src/daemon/server/handle-get-plugin-capabilities.d.ts +2 -0
- package/dist/src/daemon/server/handle-get-plugin-capabilities.js +13 -0
- package/dist/src/daemon/server/handle-hash-tasks.d.ts +8 -2
- package/dist/src/daemon/server/handle-hash-tasks.js +16 -3
- package/dist/src/daemon/server/handle-outputs-changes.d.ts +2 -2
- package/dist/src/daemon/server/handle-outputs-changes.js +21 -6
- package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +3 -2
- package/dist/src/daemon/server/outputs-tracking.d.ts +6 -0
- package/dist/src/daemon/server/outputs-tracking.js +18 -0
- package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +22 -7
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +100 -70
- package/dist/src/daemon/server/restart-checks.d.ts +22 -0
- package/dist/src/daemon/server/restart-checks.js +77 -0
- package/dist/src/daemon/server/server.js +33 -18
- package/dist/src/daemon/server/shutdown-utils.d.ts +0 -5
- package/dist/src/daemon/server/shutdown-utils.js +3 -26
- package/dist/src/hasher/hash-task.js +20 -16
- package/dist/src/hasher/native-task-hasher-impl.d.ts +8 -0
- package/dist/src/hasher/native-task-hasher-impl.js +46 -3
- package/dist/src/hasher/task-hasher.d.ts +9 -0
- package/dist/src/hasher/task-hasher.js +14 -0
- package/dist/src/native/index.d.ts +222 -21
- package/dist/src/native/index.js +3 -3
- package/dist/src/native/native-bindings.js +4 -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/lock-file/pnpm-parser.js +1 -1
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.d.ts +2 -0
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +22 -0
- package/dist/src/plugins/js/utils/register.js +2 -1
- package/dist/src/project-graph/affected/locators/project-glob-changes.js +19 -26
- package/dist/src/project-graph/error-types.d.ts +5 -0
- package/dist/src/project-graph/error-types.js +44 -5
- package/dist/src/project-graph/file-utils.d.ts +2 -0
- package/dist/src/project-graph/file-utils.js +6 -1
- package/dist/src/project-graph/nx-deps-cache.d.ts +5 -2
- package/dist/src/project-graph/nx-deps-cache.js +4 -4
- package/dist/src/project-graph/plugins/get-plugins.d.ts +3 -1
- package/dist/src/project-graph/plugins/get-plugins.js +60 -82
- package/dist/src/project-graph/plugins/in-process-loader.d.ts +1 -1
- package/dist/src/project-graph/plugins/in-process-loader.js +1 -4
- package/dist/src/project-graph/plugins/isolation/index.d.ts +1 -1
- package/dist/src/project-graph/plugins/isolation/index.js +3 -1
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +8 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +42 -6
- package/dist/src/project-graph/plugins/isolation/load-isolated-plugin.d.ts +10 -1
- package/dist/src/project-graph/plugins/isolation/load-isolated-plugin.js +67 -17
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +3 -2
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +5 -1
- package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +5 -2
- package/dist/src/project-graph/plugins/loaded-nx-plugin.js +14 -1
- package/dist/src/project-graph/plugins/nx-plugin-capabilities.d.ts +6 -0
- package/dist/src/project-graph/plugins/nx-plugin-capabilities.js +28 -0
- package/dist/src/project-graph/plugins/task-results-stub.d.ts +50 -0
- package/dist/src/project-graph/plugins/task-results-stub.js +111 -0
- package/dist/src/project-graph/plugins/tasks-execution-hooks.d.ts +3 -2
- package/dist/src/project-graph/plugins/tasks-execution-hooks.js +14 -1
- package/dist/src/project-graph/project-graph.js +43 -42
- package/dist/src/tasks-runner/batch/run-batch.js +15 -0
- package/dist/src/tasks-runner/cache.d.ts +35 -0
- package/dist/src/tasks-runner/cache.js +40 -0
- package/dist/src/tasks-runner/create-task-graph.js +1 -0
- package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
- package/dist/src/tasks-runner/forked-process-task-runner.d.ts +1 -1
- package/dist/src/tasks-runner/forked-process-task-runner.js +8 -7
- package/dist/src/tasks-runner/init-tasks-runner.js +5 -1
- package/dist/src/tasks-runner/is-tui-enabled.js +4 -3
- package/dist/src/tasks-runner/life-cycle.d.ts +7 -0
- package/dist/src/tasks-runner/life-cycle.js +7 -0
- package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.d.ts +35 -0
- package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.js +146 -0
- package/dist/src/tasks-runner/prune-task-graph.d.ts +17 -0
- package/dist/src/tasks-runner/prune-task-graph.js +45 -0
- package/dist/src/tasks-runner/run-command.js +14 -3
- package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +93 -19
- package/dist/src/tasks-runner/running-tasks/batch-process.js +184 -88
- package/dist/src/tasks-runner/task-orchestrator.d.ts +64 -2
- package/dist/src/tasks-runner/task-orchestrator.js +232 -46
- package/dist/src/tasks-runner/terminal-output-path.d.ts +11 -0
- package/dist/src/tasks-runner/terminal-output-path.js +18 -0
- package/dist/src/tasks-runner/utils.js +5 -2
- package/dist/src/utils/ab-testing.d.ts +4 -1
- package/dist/src/utils/ab-testing.js +11 -7
- package/dist/src/utils/chunkify.d.ts +1 -7
- package/dist/src/utils/chunkify.js +2 -8
- package/dist/src/utils/command-line-utils.d.ts +19 -0
- package/dist/src/utils/command-line-utils.js +10 -3
- package/dist/src/utils/formatters/index.d.ts +6 -0
- package/dist/src/utils/formatters/index.js +5 -0
- package/dist/src/utils/formatters/prettier.d.ts +0 -8
- package/dist/src/utils/formatters/prettier.js +4 -26
- package/dist/src/utils/handle-errors.js +5 -43
- package/dist/src/utils/installed-nx-version.js +2 -48
- package/dist/src/utils/min-release-age/packument.js +1 -1
- package/dist/src/utils/output.d.ts +22 -3
- package/dist/src/utils/output.js +28 -5
- package/dist/src/utils/package-json.d.ts +25 -0
- package/dist/src/utils/package-json.js +41 -6
- package/dist/src/utils/package-manager.d.ts +6 -0
- package/dist/src/utils/package-manager.js +10 -0
- package/dist/src/utils/plugins/plugin-capabilities.js +7 -2
- package/dist/src/utils/provenance.js +20 -4
- package/dist/src/utils/serialize-overrides-into-command-line.js +2 -4
- package/dist/src/utils/workspace-context.d.ts +47 -3
- package/dist/src/utils/workspace-context.js +162 -10
- package/package.json +12 -13
- package/schemas/nx-schema.json +4 -0
- package/schemas/project-schema.json +4 -0
- package/dist/src/daemon/server/file-watching/route-workspace-changes.d.ts +0 -9
- package/dist/src/daemon/server/file-watching/route-workspace-changes.js +0 -76
- package/dist/src/daemon/server/watcher.d.ts +0 -18
- package/dist/src/daemon/server/watcher.js +0 -126
|
@@ -20,6 +20,7 @@ const installation_directory_1 = require("../../../utils/installation-directory"
|
|
|
20
20
|
const is_sandbox_1 = require("../../../utils/is-sandbox");
|
|
21
21
|
const logger_2 = require("../../../utils/logger");
|
|
22
22
|
const progress_topics_1 = require("../../../utils/progress-topics");
|
|
23
|
+
const task_results_stub_1 = require("../task-results-stub");
|
|
23
24
|
const wait_for_socket_connection_1 = require("../../../utils/wait-for-socket-connection");
|
|
24
25
|
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
25
26
|
const resolve_plugin_1 = require("../resolve-plugin");
|
|
@@ -43,9 +44,16 @@ class IsolatedPlugin {
|
|
|
43
44
|
const moduleName = typeof plugin === 'string' ? plugin : plugin.plugin;
|
|
44
45
|
const { name, pluginPath, shouldRegisterTSTranspiler } = await (0, resolve_plugin_1.resolveNxPlugin)(moduleName, root, (0, installation_directory_1.getNxRequirePaths)(root));
|
|
45
46
|
const instance = new IsolatedPlugin(plugin, root, name, pluginPath, shouldRegisterTSTranspiler, index);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
try {
|
|
48
|
+
const loadResult = await instance.spawnAndConnect();
|
|
49
|
+
instance.setupHooks(loadResult);
|
|
50
|
+
return instance;
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
// A timed-out worker is still running, and the caller is about to drop this instance.
|
|
54
|
+
instance.shutdown();
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
49
57
|
}
|
|
50
58
|
constructor(plugin, root, name, pluginPath, shouldRegisterTSTranspiler, index) {
|
|
51
59
|
this.index = index;
|
|
@@ -53,6 +61,7 @@ class IsolatedPlugin {
|
|
|
53
61
|
this.worker = null;
|
|
54
62
|
this.socket = null;
|
|
55
63
|
this._alive = false;
|
|
64
|
+
this._released = false;
|
|
56
65
|
this._connectPromise = null;
|
|
57
66
|
this.txId = 0;
|
|
58
67
|
this.pendingCount = 0;
|
|
@@ -90,6 +99,15 @@ class IsolatedPlugin {
|
|
|
90
99
|
this.pluginPath = pluginPath;
|
|
91
100
|
this.shouldRegisterTSTranspiler = shouldRegisterTSTranspiler;
|
|
92
101
|
}
|
|
102
|
+
capabilities() {
|
|
103
|
+
return {
|
|
104
|
+
createNodesPattern: this.createNodes?.[0],
|
|
105
|
+
hasCreateDependencies: !!this.createDependencies,
|
|
106
|
+
hasCreateMetadata: !!this.createMetadata,
|
|
107
|
+
hasPreTasksExecution: !!this.preTasksExecution,
|
|
108
|
+
hasPostTasksExecution: !!this.postTasksExecution,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
93
111
|
async spawnAndConnect() {
|
|
94
112
|
const { worker, socket } = await startPluginWorker(this.name);
|
|
95
113
|
this.worker = worker;
|
|
@@ -214,7 +232,15 @@ class IsolatedPlugin {
|
|
|
214
232
|
const shutdown = (hookName) => this.shutdownIfInactive(hookName);
|
|
215
233
|
const wrap = (hook, hookFn) => this.lifecycle.wrapHook(hook, async (...args) => {
|
|
216
234
|
await this.ensureAlive();
|
|
217
|
-
|
|
235
|
+
try {
|
|
236
|
+
return await hookFn(...args);
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
// A released plugin still answers, then shuts its worker straight back down.
|
|
240
|
+
if (this._released) {
|
|
241
|
+
shutdown(hook);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
218
244
|
}, () => shutdown(hook));
|
|
219
245
|
if (loadResult.createNodesPattern) {
|
|
220
246
|
this.createNodes = [
|
|
@@ -268,7 +294,7 @@ class IsolatedPlugin {
|
|
|
268
294
|
if (loadResult.hasPostTasksExecution) {
|
|
269
295
|
this.postTasksExecution = wrap('postTasksExecution', async (context) => {
|
|
270
296
|
const result = await this.sendRequest('postTasksExecution', {
|
|
271
|
-
context,
|
|
297
|
+
context: (0, task_results_stub_1.stubTerminalOutputs)(context),
|
|
272
298
|
});
|
|
273
299
|
if (result.success === false) {
|
|
274
300
|
throw result.error;
|
|
@@ -338,8 +364,18 @@ class IsolatedPlugin {
|
|
|
338
364
|
this.shutdownIfInactive(lastCompletedHook);
|
|
339
365
|
}
|
|
340
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Like `shutdown`, but for good: a later hook call still answers, then shuts
|
|
369
|
+
* the worker back down instead of leaving a respawned one running.
|
|
370
|
+
*/
|
|
371
|
+
dispose() {
|
|
372
|
+
this._released = true;
|
|
373
|
+
this.shutdown();
|
|
374
|
+
}
|
|
341
375
|
shutdown() {
|
|
342
|
-
|
|
376
|
+
// Not `_alive`: that is only set once the worker answers a load, so gating
|
|
377
|
+
// on it would leave a worker that never got that far running.
|
|
378
|
+
if (!this.worker && !this.socket)
|
|
343
379
|
return;
|
|
344
380
|
this._alive = false;
|
|
345
381
|
this._connectPromise = null;
|
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
import { PluginConfiguration } from '../../../config/nx-json';
|
|
2
2
|
import type { LoadedNxPlugin } from '../loaded-nx-plugin';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Declares a loader's plugins and disposes any no loader wants. Call before loading.
|
|
5
|
+
* Anything that makes a running worker unfit for reuse must be in its cache key.
|
|
6
|
+
*/
|
|
7
|
+
export declare function wantPlugins(loader: string, plugins: Array<{
|
|
8
|
+
plugin: PluginConfiguration;
|
|
9
|
+
index?: number;
|
|
10
|
+
}>, root: string): void;
|
|
11
|
+
export declare function disposeIsolatedPlugins(): void;
|
|
12
|
+
export declare function loadIsolatedNxPlugin(plugin: PluginConfiguration, root: string, index?: number): Promise<LoadedNxPlugin>;
|
|
@@ -1,24 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wantPlugins = wantPlugins;
|
|
4
|
+
exports.disposeIsolatedPlugins = disposeIsolatedPlugins;
|
|
3
5
|
exports.loadIsolatedNxPlugin = loadIsolatedNxPlugin;
|
|
4
6
|
const isolated_plugin_1 = require("./isolated-plugin");
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const loadedPlugins = (global['nxLoadedPlugins'] ??= new Map());
|
|
8
|
+
/**
|
|
9
|
+
* Keys each loader last asked for; a sweep keeps their union. Global like
|
|
10
|
+
* `loadedPlugins`, or one copy of Nx would sweep another's workers.
|
|
11
|
+
*/
|
|
12
|
+
const wantedBy = (global['nxWantedPlugins'] ??=
|
|
13
|
+
new Map());
|
|
14
|
+
/**
|
|
15
|
+
* Declares a loader's plugins and disposes any no loader wants. Call before loading.
|
|
16
|
+
* Anything that makes a running worker unfit for reuse must be in its cache key.
|
|
17
|
+
*/
|
|
18
|
+
function wantPlugins(loader, plugins, root) {
|
|
19
|
+
wantedBy.set(loader, new Set(plugins.map(({ plugin, index }) => getCacheKey(plugin, root, index))));
|
|
20
|
+
sweep();
|
|
21
|
+
}
|
|
22
|
+
function disposeIsolatedPlugins() {
|
|
23
|
+
wantedBy.clear();
|
|
24
|
+
sweep();
|
|
25
|
+
}
|
|
26
|
+
function loadIsolatedNxPlugin(plugin, root, index) {
|
|
27
|
+
const cacheKey = getCacheKey(plugin, root, index);
|
|
28
|
+
return (loadedPlugins.get(cacheKey) ??
|
|
29
|
+
register(cacheKey, isolated_plugin_1.IsolatedPlugin.load(plugin, root, index)));
|
|
30
|
+
}
|
|
31
|
+
function register(cacheKey, loading) {
|
|
32
|
+
const entry = loading.then((plugin) => {
|
|
33
|
+
// Swept while loading: nothing names it any more.
|
|
34
|
+
if (!isWanted(cacheKey)) {
|
|
35
|
+
plugin.dispose();
|
|
36
|
+
}
|
|
37
|
+
return plugin;
|
|
38
|
+
}, (err) => {
|
|
39
|
+
forget(cacheKey, entry);
|
|
14
40
|
throw err;
|
|
15
41
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
42
|
+
if (isWanted(cacheKey)) {
|
|
43
|
+
loadedPlugins.set(cacheKey, entry);
|
|
44
|
+
}
|
|
45
|
+
return entry;
|
|
46
|
+
}
|
|
47
|
+
function sweep() {
|
|
48
|
+
for (const [cacheKey, entry] of [...loadedPlugins]) {
|
|
49
|
+
if (isWanted(cacheKey)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
loadedPlugins.delete(cacheKey);
|
|
53
|
+
entry.then((plugin) => plugin.dispose(),
|
|
54
|
+
// No worker to dispose; the caller reports the rejection.
|
|
55
|
+
() => { });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function isWanted(cacheKey) {
|
|
59
|
+
for (const keys of wantedBy.values()) {
|
|
60
|
+
if (keys.has(cacheKey)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
/** Includes the index: a plugin carries its nx.json position, which exclusions and errors point at. */
|
|
67
|
+
function getCacheKey(plugin, root, index) {
|
|
68
|
+
return JSON.stringify({ plugin, root, index });
|
|
69
|
+
}
|
|
70
|
+
function forget(cacheKey, entry) {
|
|
71
|
+
if (loadedPlugins.get(cacheKey) === entry) {
|
|
72
|
+
loadedPlugins.delete(cacheKey);
|
|
73
|
+
}
|
|
24
74
|
}
|
|
@@ -3,7 +3,8 @@ import type { Socket } from 'net';
|
|
|
3
3
|
import type { PluginConfiguration } from '../../../config/nx-json';
|
|
4
4
|
import type { ProjectGraph } from '../../../config/project-graph';
|
|
5
5
|
import type { LoadedNxPlugin } from '../loaded-nx-plugin';
|
|
6
|
-
import type {
|
|
6
|
+
import type { MaybeStubbedPostTasksExecutionContext } from '../task-results-stub';
|
|
7
|
+
import type { CreateDependenciesContext, CreateMetadataContext, CreateNodesContext, PreTasksExecutionContext } from '../public-api';
|
|
7
8
|
import type { AllMessages, AllResults, DefineMessages, Handlers, ResultOf, WithResult } from './message-types';
|
|
8
9
|
/**
|
|
9
10
|
* All plugin worker message definitions in one place.
|
|
@@ -88,7 +89,7 @@ type PluginMessageDefs = DefineMessages<{
|
|
|
88
89
|
};
|
|
89
90
|
postTasksExecution: {
|
|
90
91
|
payload: {
|
|
91
|
-
context:
|
|
92
|
+
context: MaybeStubbedPostTasksExecutionContext;
|
|
92
93
|
};
|
|
93
94
|
result: {
|
|
94
95
|
success: true;
|
|
@@ -42,6 +42,10 @@ if (!socketPath || !expectedPluginName || !hostWorkspaceRoot) {
|
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
44
44
|
const CONNECT_TIMEOUT_MS = 30_000;
|
|
45
|
+
// Runs in this worker's own loop from the moment it accepts, while the host is
|
|
46
|
+
// loading every plugin at once and may sit in a synchronous workspace walk for
|
|
47
|
+
// over ten seconds. A host that has died closes the socket; 'end' covers that.
|
|
48
|
+
const LOAD_TIMEOUT_MS = 60_000;
|
|
45
49
|
let connectErrorTimeout = setErrorTimeout(CONNECT_TIMEOUT_MS, `The plugin worker for ${expectedPluginName} is exiting as it was not connected to within ${CONNECT_TIMEOUT_MS / 1000} seconds. ` +
|
|
46
50
|
'Plugin workers expect to receive a socket connection from the parent process shortly after being started. ' +
|
|
47
51
|
'If you are seeing this issue, please report it to the Nx team at https://github.com/nrwl/nx/issues.');
|
|
@@ -55,7 +59,7 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
55
59
|
// This handles cases where the host process was killed
|
|
56
60
|
// after the worker connected but before the worker was
|
|
57
61
|
// instructed to load the plugin.
|
|
58
|
-
let loadErrorTimeout = setErrorTimeout(
|
|
62
|
+
let loadErrorTimeout = setErrorTimeout(LOAD_TIMEOUT_MS, `Plugin Worker for ${expectedPluginName} is exiting as it did not receive a load message within ${LOAD_TIMEOUT_MS / 1000} seconds of connecting. ` +
|
|
59
63
|
'This likely indicates that the host process was terminated before the worker could be instructed to load the plugin. ' +
|
|
60
64
|
'If you are seeing this issue, please report it to the Nx team at https://github.com/nrwl/nx/issues.');
|
|
61
65
|
socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)((raw) => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ProjectGraph } from '../../config/project-graph';
|
|
2
2
|
import { type PluginConfiguration } from '../../config/nx-json';
|
|
3
3
|
import type { RawProjectGraphDependency } from '../project-graph-builder';
|
|
4
|
-
import type { CreateDependenciesContext, CreateMetadataContext, CreateNodesContext, CreateNodesResult, NxPlugin,
|
|
4
|
+
import type { CreateDependenciesContext, CreateMetadataContext, CreateNodesContext, CreateNodesResult, NxPlugin, PreTasksExecutionContext, ProjectsMetadata } from './public-api';
|
|
5
|
+
import { type MaybeStubbedPostTasksExecutionContext } from './task-results-stub';
|
|
6
|
+
import type { NxPluginCapabilities } from './nx-plugin-capabilities';
|
|
5
7
|
/**
|
|
6
8
|
* NOTE: Avoid using `import type` with this class. It causes issues with
|
|
7
9
|
* jest's module resolution when running tests in projects that import
|
|
@@ -17,7 +19,7 @@ export declare class LoadedNxPlugin {
|
|
|
17
19
|
readonly createDependencies?: (context: CreateDependenciesContext) => Promise<RawProjectGraphDependency[]>;
|
|
18
20
|
readonly createMetadata?: (graph: ProjectGraph, context: CreateMetadataContext) => Promise<ProjectsMetadata>;
|
|
19
21
|
readonly preTasksExecution?: (context: PreTasksExecutionContext) => Promise<NodeJS.ProcessEnv>;
|
|
20
|
-
readonly postTasksExecution?: (context:
|
|
22
|
+
readonly postTasksExecution?: (context: MaybeStubbedPostTasksExecutionContext) => Promise<void>;
|
|
21
23
|
readonly options?: unknown;
|
|
22
24
|
readonly include?: string[];
|
|
23
25
|
readonly exclude?: string[];
|
|
@@ -38,4 +40,5 @@ export declare class LoadedNxPlugin {
|
|
|
38
40
|
*/
|
|
39
41
|
setWorkerEnv?(env: Record<string, string>): Promise<void>;
|
|
40
42
|
constructor(plugin: NxPlugin, pluginDefinition: PluginConfiguration, index?: number);
|
|
43
|
+
capabilities(): NxPluginCapabilities;
|
|
41
44
|
}
|
|
@@ -5,6 +5,7 @@ const analytics_1 = require("../../analytics");
|
|
|
5
5
|
const error_types_1 = require("../error-types");
|
|
6
6
|
const enabled_1 = require("./isolation/enabled");
|
|
7
7
|
const client_1 = require("../../daemon/client/client");
|
|
8
|
+
const task_results_stub_1 = require("./task-results-stub");
|
|
8
9
|
/**
|
|
9
10
|
* NOTE: Avoid using `import type` with this class. It causes issues with
|
|
10
11
|
* jest's module resolution when running tests in projects that import
|
|
@@ -97,8 +98,20 @@ class LoadedNxPlugin {
|
|
|
97
98
|
};
|
|
98
99
|
}
|
|
99
100
|
if (plugin.postTasksExecution) {
|
|
100
|
-
|
|
101
|
+
// The single rehydration point: every transport hands its context
|
|
102
|
+
// straight here, so a context bound for an isolated plugin stays stubbed
|
|
103
|
+
// across that second hop rather than being read and re-stubbed.
|
|
104
|
+
this.postTasksExecution = async (context) => plugin.postTasksExecution(this.options, await (0, task_results_stub_1.rehydrateTerminalOutputs)(context));
|
|
101
105
|
}
|
|
102
106
|
}
|
|
107
|
+
capabilities() {
|
|
108
|
+
return {
|
|
109
|
+
createNodesPattern: this.createNodes?.[0],
|
|
110
|
+
hasCreateDependencies: !!this.createDependencies,
|
|
111
|
+
hasCreateMetadata: !!this.createMetadata,
|
|
112
|
+
hasPreTasksExecution: !!this.preTasksExecution,
|
|
113
|
+
hasPostTasksExecution: !!this.postTasksExecution,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
103
116
|
}
|
|
104
117
|
exports.LoadedNxPlugin = LoadedNxPlugin;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type CachedPluginCapabilities, NxPluginCapabilities as NxPluginCapabilitiesStore } from '../../native';
|
|
2
|
+
export type NxPluginCapabilities = CachedPluginCapabilities;
|
|
3
|
+
export declare function getNxPluginCapabilitiesStore(): NxPluginCapabilitiesStore | null;
|
|
4
|
+
export declare function noteGraphReadFromCache(computedAt: number | undefined): void;
|
|
5
|
+
/** Null means "load them", never "no plugin registers anything". */
|
|
6
|
+
export declare function capabilitiesOfNxPluginsReadFromCache(): NxPluginCapabilities[] | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getNxPluginCapabilitiesStore = getNxPluginCapabilitiesStore;
|
|
4
|
+
exports.noteGraphReadFromCache = noteGraphReadFromCache;
|
|
5
|
+
exports.capabilitiesOfNxPluginsReadFromCache = capabilitiesOfNxPluginsReadFromCache;
|
|
6
|
+
const native_1 = require("../../native");
|
|
7
|
+
const db_connection_1 = require("../../utils/db-connection");
|
|
8
|
+
let store;
|
|
9
|
+
function getNxPluginCapabilitiesStore() {
|
|
10
|
+
// The database is not part of the WASM build.
|
|
11
|
+
if (native_1.IS_WASM) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
// Checkout-local: the rows describe this checkout's graph.
|
|
15
|
+
store ??= new native_1.NxPluginCapabilities((0, db_connection_1.getLocalDbConnection)());
|
|
16
|
+
return store;
|
|
17
|
+
}
|
|
18
|
+
let computedAtOfGraphReadFromCache;
|
|
19
|
+
function noteGraphReadFromCache(computedAt) {
|
|
20
|
+
computedAtOfGraphReadFromCache = computedAt;
|
|
21
|
+
}
|
|
22
|
+
/** Null means "load them", never "no plugin registers anything". */
|
|
23
|
+
function capabilitiesOfNxPluginsReadFromCache() {
|
|
24
|
+
if (computedAtOfGraphReadFromCache === undefined) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return (getNxPluginCapabilitiesStore()?.get(computedAtOfGraphReadFromCache) ?? null);
|
|
28
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PostTasksExecutionContext } from './public-api';
|
|
2
|
+
/**
|
|
3
|
+
* A {@link PostTasksExecutionContext} whose terminal outputs have been lifted
|
|
4
|
+
* out of `taskResults` and into a map of the paths holding them, so it can
|
|
5
|
+
* cross a process boundary.
|
|
6
|
+
*
|
|
7
|
+
* `taskResults` carries every task's full output inline, so the sender copies
|
|
8
|
+
* every one of those strings into the serialized payload and pushes the whole
|
|
9
|
+
* thing across the socket, once per boundary. The strings themselves exist
|
|
10
|
+
* either way: the runner puts them on each result as its task completes, and
|
|
11
|
+
* the receiver reads them back eagerly. The bytes are
|
|
12
|
+
* already on disk at `<cacheDir>/terminalOutputs/<hash>`, so transports carry
|
|
13
|
+
* paths and each receiver reads them back before any plugin sees the context.
|
|
14
|
+
*
|
|
15
|
+
* This is no longer a crash fix. The fallback used to be
|
|
16
|
+
* `v8.serialize(...).toString('binary')`, which put an oversized payload back
|
|
17
|
+
* through V8's maximum string length after `JSON.stringify` had already hit it,
|
|
18
|
+
* so both arms threw. `serialize` returns bytes now and the ceiling is gone;
|
|
19
|
+
* what is left is the cost of moving the payload at all.
|
|
20
|
+
*
|
|
21
|
+
* The paths live here rather than in `terminalOutput` so that field never
|
|
22
|
+
* holds two meanings: a stubbed result carries no `terminalOutput` at all, so
|
|
23
|
+
* a transport that forgets to rehydrate hands a plugin a missing value rather
|
|
24
|
+
* than a filename shaped exactly like real output. The map is required, so a
|
|
25
|
+
* stubbed context cannot typecheck without one.
|
|
26
|
+
*/
|
|
27
|
+
export type StubbedPostTasksExecutionContext = PostTasksExecutionContext & {
|
|
28
|
+
/**
|
|
29
|
+
* Path to the terminal output of each stubbed task, keyed by task id. Out of
|
|
30
|
+
* band because a task can print anything, so no in-band marker is safe from
|
|
31
|
+
* a task that prints it.
|
|
32
|
+
*/
|
|
33
|
+
stubbedTerminalOutputs: Record<string, string>;
|
|
34
|
+
};
|
|
35
|
+
export type MaybeStubbedPostTasksExecutionContext = PostTasksExecutionContext | StubbedPostTasksExecutionContext;
|
|
36
|
+
/**
|
|
37
|
+
* Swaps outputs out for paths ahead of a transport. Idempotent, so a context
|
|
38
|
+
* already stubbed for the daemon can be passed on to a plugin worker as-is.
|
|
39
|
+
*/
|
|
40
|
+
export declare function stubTerminalOutputs(context: MaybeStubbedPostTasksExecutionContext): MaybeStubbedPostTasksExecutionContext;
|
|
41
|
+
/**
|
|
42
|
+
* Reads the outputs back. A no-op on a context that was never stubbed, which
|
|
43
|
+
* is every in-process plugin run without the daemon.
|
|
44
|
+
*
|
|
45
|
+
* The reads are issued together rather than awaited one at a time: a run has
|
|
46
|
+
* one file per task, they are independent, and this sits between the tasks
|
|
47
|
+
* finishing and the first plugin seeing the results - so serializing it would
|
|
48
|
+
* add up every file's latency in front of that.
|
|
49
|
+
*/
|
|
50
|
+
export declare function rehydrateTerminalOutputs(context: MaybeStubbedPostTasksExecutionContext): Promise<PostTasksExecutionContext>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stubTerminalOutputs = stubTerminalOutputs;
|
|
4
|
+
exports.rehydrateTerminalOutputs = rehydrateTerminalOutputs;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const promises_1 = require("node:fs/promises");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const terminal_output_path_1 = require("../../tasks-runner/terminal-output-path");
|
|
9
|
+
const logger_1 = require("../../utils/logger");
|
|
10
|
+
function isStubbed(context) {
|
|
11
|
+
return !!context.stubbedTerminalOutputs;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Swaps outputs out for paths ahead of a transport. Idempotent, so a context
|
|
15
|
+
* already stubbed for the daemon can be passed on to a plugin worker as-is.
|
|
16
|
+
*/
|
|
17
|
+
function stubTerminalOutputs(context) {
|
|
18
|
+
if (!context.taskResults) {
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
21
|
+
const stubbedTerminalOutputs = {
|
|
22
|
+
...(isStubbed(context) ? context.stubbedTerminalOutputs : {}),
|
|
23
|
+
};
|
|
24
|
+
const taskResults = {};
|
|
25
|
+
let swapped = false;
|
|
26
|
+
for (const [id, result] of Object.entries(context.taskResults)) {
|
|
27
|
+
const path = id in stubbedTerminalOutputs ? null : stubbablePath(result);
|
|
28
|
+
if (path) {
|
|
29
|
+
const { terminalOutput, ...withoutOutput } = result;
|
|
30
|
+
stubbedTerminalOutputs[id] = path;
|
|
31
|
+
taskResults[id] = withoutOutput;
|
|
32
|
+
swapped = true;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
taskResults[id] = result;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Nothing to swap: hand back the context as it came, so a run with no
|
|
39
|
+
// outputs on disk sends exactly what it sent before. Covers the
|
|
40
|
+
// already-stubbed case too, which keeps its own map by staying untouched.
|
|
41
|
+
if (!swapped) {
|
|
42
|
+
return context;
|
|
43
|
+
}
|
|
44
|
+
return { ...context, taskResults, stubbedTerminalOutputs };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Reads the outputs back. A no-op on a context that was never stubbed, which
|
|
48
|
+
* is every in-process plugin run without the daemon.
|
|
49
|
+
*
|
|
50
|
+
* The reads are issued together rather than awaited one at a time: a run has
|
|
51
|
+
* one file per task, they are independent, and this sits between the tasks
|
|
52
|
+
* finishing and the first plugin seeing the results - so serializing it would
|
|
53
|
+
* add up every file's latency in front of that.
|
|
54
|
+
*/
|
|
55
|
+
async function rehydrateTerminalOutputs(context) {
|
|
56
|
+
if (!isStubbed(context)) {
|
|
57
|
+
return context;
|
|
58
|
+
}
|
|
59
|
+
const taskResults = { ...context.taskResults };
|
|
60
|
+
const reads = Object.entries(context.stubbedTerminalOutputs)
|
|
61
|
+
// A stub whose result is gone has nothing to attach the bytes to, so it is
|
|
62
|
+
// dropped before the read rather than after it.
|
|
63
|
+
.filter(([id]) => taskResults[id])
|
|
64
|
+
.map(async ([id, path]) => {
|
|
65
|
+
const terminalOutput = await readTerminalOutput(path);
|
|
66
|
+
taskResults[id] = { ...taskResults[id], terminalOutput };
|
|
67
|
+
});
|
|
68
|
+
await Promise.all(reads);
|
|
69
|
+
const { stubbedTerminalOutputs, ...rehydrated } = {
|
|
70
|
+
...context,
|
|
71
|
+
taskResults,
|
|
72
|
+
};
|
|
73
|
+
return rehydrated;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The path to swap in, or null to leave the output inline. A task that failed
|
|
77
|
+
* before it was hashed has no path, and a task that never ran has no file —
|
|
78
|
+
* both keep whatever they already carry rather than losing it.
|
|
79
|
+
*/
|
|
80
|
+
function stubbablePath(result) {
|
|
81
|
+
if (result.terminalOutput === undefined) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const hash = result.task?.hash;
|
|
85
|
+
if (!hash) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
// `getCustomHasher` lets a plugin return any string, and this one reaches a
|
|
89
|
+
// read, so the sink is kept inside the cache dir here rather than trusted to
|
|
90
|
+
// the hasher. A hash that needed stripping resolves to a file that does not
|
|
91
|
+
// exist, which falls back to sending the output inline.
|
|
92
|
+
const path = (0, terminal_output_path_1.terminalOutputPathForHash)((0, node_path_1.basename)(hash));
|
|
93
|
+
return (0, node_fs_1.existsSync)(path) ? path : null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A path that no longer resolves means the bytes are gone. Saying so with
|
|
97
|
+
* `undefined` — already a legal value, skipped tasks have it — beats leaving
|
|
98
|
+
* the path, which would have plugins treat a filename as the output.
|
|
99
|
+
*/
|
|
100
|
+
async function readTerminalOutput(path) {
|
|
101
|
+
if (!path) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await (0, promises_1.readFile)(path, 'utf-8');
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
logger_1.logger.warn(`Nx could not read a task's terminal output from ${path}: ${e.message}`);
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PreTasksExecutionContext } from './public-api';
|
|
2
|
+
import { type MaybeStubbedPostTasksExecutionContext } from './task-results-stub';
|
|
2
3
|
export declare function runPreTasksExecution(pluginContext: PreTasksExecutionContext): Promise<NodeJS.ProcessEnv[]>;
|
|
3
|
-
export declare function runPostTasksExecution(context:
|
|
4
|
+
export declare function runPostTasksExecution(context: MaybeStubbedPostTasksExecutionContext): Promise<void>;
|
|
@@ -7,8 +7,18 @@ const get_plugins_1 = require("./get-plugins");
|
|
|
7
7
|
const is_on_daemon_1 = require("../../daemon/is-on-daemon");
|
|
8
8
|
const client_1 = require("../../daemon/client/client");
|
|
9
9
|
const workspace_root_1 = require("../../utils/workspace-root");
|
|
10
|
+
const task_results_stub_1 = require("./task-results-stub");
|
|
11
|
+
const nx_plugin_capabilities_1 = require("./nx-plugin-capabilities");
|
|
12
|
+
/** True only when the cached graph's recorded rows show no plugin has `hook`; unknown is false. */
|
|
13
|
+
function knownThatNoPluginRegisters(hook) {
|
|
14
|
+
const recorded = (0, nx_plugin_capabilities_1.capabilitiesOfNxPluginsReadFromCache)();
|
|
15
|
+
return !!recorded && !recorded.some((capabilities) => capabilities[hook]);
|
|
16
|
+
}
|
|
10
17
|
async function runPreTasksExecution(pluginContext) {
|
|
11
18
|
if ((0, is_on_daemon_1.isOnDaemon)() || !(0, client_1.isDaemonEnabled)()) {
|
|
19
|
+
if (knownThatNoPluginRegisters('hasPreTasksExecution')) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
12
22
|
performance.mark(`preTasksExecution:start`);
|
|
13
23
|
const plugins = await (0, get_plugins_1.getPlugins)((0, nx_json_1.readNxJson)(pluginContext.workspaceRoot), pluginContext.workspaceRoot);
|
|
14
24
|
const envs = await Promise.all(plugins
|
|
@@ -44,6 +54,9 @@ function applyProcessEnvs(envs) {
|
|
|
44
54
|
}
|
|
45
55
|
async function runPostTasksExecution(context) {
|
|
46
56
|
if ((0, is_on_daemon_1.isOnDaemon)() || !(0, client_1.isDaemonEnabled)()) {
|
|
57
|
+
if (knownThatNoPluginRegisters('hasPostTasksExecution')) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
47
60
|
performance.mark(`postTasksExecution:start`);
|
|
48
61
|
const plugins = await (0, get_plugins_1.getPlugins)((0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot));
|
|
49
62
|
await Promise.all(plugins
|
|
@@ -62,6 +75,6 @@ async function runPostTasksExecution(context) {
|
|
|
62
75
|
performance.measure(`postTasksExecution`, `postTasksExecution:start`, `postTasksExecution:end`);
|
|
63
76
|
}
|
|
64
77
|
else {
|
|
65
|
-
await client_1.daemonClient.runPostTasksExecution(context);
|
|
78
|
+
await client_1.daemonClient.runPostTasksExecution((0, task_results_stub_1.stubTerminalOutputs)(context));
|
|
66
79
|
}
|
|
67
80
|
}
|