nx 23.1.1 → 23.1.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/release/changelog-renderer/index.js +4 -5
- package/dist/release/index.d.ts +1 -1
- package/dist/src/command-line/add/add.js +5 -0
- package/dist/src/command-line/affected/affected.js +1 -0
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
- package/dist/src/command-line/import/import.js +1 -2
- package/dist/src/command-line/init/init-v2.js +4 -5
- package/dist/src/command-line/migrate/migrate.d.ts +4 -0
- package/dist/src/command-line/migrate/migrate.js +89 -26
- package/dist/src/command-line/release/changelog.js +5 -0
- package/dist/src/command-line/release/index.d.ts +1 -1
- package/dist/src/command-line/release/utils/git.js +1 -1
- package/dist/src/command-line/release/utils/release-graph.d.ts +17 -0
- package/dist/src/command-line/release/utils/release-graph.js +71 -19
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +6 -0
- package/dist/src/command-line/release/version/release-group-processor.d.ts +24 -0
- package/dist/src/command-line/release/version/release-group-processor.js +80 -6
- package/dist/src/command-line/release/version/version-actions.d.ts +27 -2
- package/dist/src/command-line/release/version/version-actions.js +9 -1
- package/dist/src/command-line/release/version.js +9 -5
- package/dist/src/command-line/reset/reset.js +6 -1
- package/dist/src/command-line/run/run-one.js +1 -0
- package/dist/src/command-line/run-many/run-many.js +1 -0
- package/dist/src/command-line/watch/watch.js +13 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +52 -1
- package/dist/src/daemon/client/client.js +160 -36
- package/dist/src/daemon/client/daemon-environment.d.ts +17 -0
- package/dist/src/daemon/client/daemon-environment.js +110 -2
- package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
- package/dist/src/daemon/message-types/daemon-message.d.ts +32 -0
- package/dist/src/daemon/message-types/daemon-message.js +25 -0
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +5 -0
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +16 -0
- package/dist/src/daemon/server/latest-nx.js +5 -2
- package/dist/src/daemon/server/server.js +24 -0
- package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
- package/dist/src/daemon/server/shutdown-utils.js +11 -4
- package/dist/src/daemon/socket-utils.d.ts +1 -0
- package/dist/src/daemon/socket-utils.js +24 -3
- package/dist/src/daemon/tmp-dir.d.ts +43 -3
- package/dist/src/daemon/tmp-dir.js +437 -15
- package/dist/src/devkit-internals.d.ts +93 -3
- package/dist/src/devkit-internals.js +192 -5
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
- package/dist/src/generators/tree.d.ts +4 -2
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
- package/dist/src/native/index.d.ts +10 -0
- package/dist/src/native/index.js +71 -28
- package/dist/src/native/native-bindings.js +1 -0
- package/dist/src/native/native-file-cache-location.d.ts +17 -1
- package/dist/src/native/native-file-cache-location.js +86 -12
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/utilities/get-cloud-options.js +5 -2
- package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
- package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +60 -0
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +14 -8
- package/dist/src/plugins/js/project-graph/affected/npm-packages.js +51 -20
- package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +2 -0
- package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
- package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
- package/dist/src/project-graph/error-types.d.ts +12 -1
- package/dist/src/project-graph/error-types.js +27 -2
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +18 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
- package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
- package/dist/src/project-graph/project-graph.js +20 -0
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +5 -1
- package/dist/src/project-graph/utils/project-configuration/target-normalization.js +133 -3
- package/dist/src/tasks-runner/task-env.js +5 -2
- package/dist/src/tasks-runner/utils.js +2 -6
- package/dist/src/utils/assert-workspace-validity.js +7 -2
- package/dist/src/utils/fileutils.d.ts +5 -0
- package/dist/src/utils/fileutils.js +7 -2
- package/dist/src/utils/git-worktrees.d.ts +30 -0
- package/dist/src/utils/git-worktrees.js +212 -0
- package/dist/src/utils/ignore.d.ts +128 -1
- package/dist/src/utils/ignore.js +182 -12
- package/dist/src/utils/long-running-target.d.ts +10 -0
- package/dist/src/utils/long-running-target.js +19 -0
- package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
- package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
- package/dist/src/utils/min-release-age/packument.js +1 -1
- package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
- package/dist/src/utils/nx-tmp-dir.js +40 -0
- package/dist/src/utils/owned-private-dir.d.ts +132 -0
- package/dist/src/utils/owned-private-dir.js +323 -0
- package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
- package/dist/src/utils/package-manager-config/bunfig.js +49 -0
- package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
- package/dist/src/utils/package-manager-config/npmrc.js +124 -0
- package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
- package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
- package/dist/src/utils/package-manager.d.ts +21 -2
- package/dist/src/utils/package-manager.js +244 -47
- package/dist/src/utils/provenance.js +6 -14
- package/dist/src/utils/registry-config/bun.d.ts +2 -0
- package/dist/src/utils/registry-config/bun.js +257 -0
- package/dist/src/utils/registry-config/index.d.ts +13 -0
- package/dist/src/utils/registry-config/index.js +102 -0
- package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
- package/dist/src/utils/registry-config/pnpm.js +1551 -0
- package/dist/src/utils/registry-config/utils.d.ts +195 -0
- package/dist/src/utils/registry-config/utils.js +496 -0
- package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-berry.js +612 -0
- package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
- package/dist/src/utils/safe-spawn.d.ts +24 -0
- package/dist/src/utils/safe-spawn.js +104 -0
- package/dist/src/utils/shell-quoting.d.ts +15 -0
- package/dist/src/utils/shell-quoting.js +35 -0
- package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
- package/dist/src/utils/wait-for-socket-connection.js +14 -8
- package/migrations.json +6 -0
- package/package.json +12 -12
- package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
- package/dist/src/utils/min-release-age/npmrc.js +0 -45
|
@@ -8,6 +8,7 @@ const semver_1 = require("semver");
|
|
|
8
8
|
const find_project_for_path_1 = require("../../../../project-graph/utils/find-project-for-path");
|
|
9
9
|
const fileutils_1 = require("../../../../utils/fileutils");
|
|
10
10
|
const get_package_name_from_import_path_1 = require("../../../../utils/get-package-name-from-import-path");
|
|
11
|
+
const path_1 = require("../../../../utils/path");
|
|
11
12
|
const workspace_root_1 = require("../../../../utils/workspace-root");
|
|
12
13
|
const packages_1 = require("../../utils/packages");
|
|
13
14
|
const resolve_relative_to_dir_1 = require("../../utils/resolve-relative-to-dir");
|
|
@@ -346,6 +347,7 @@ class TargetProjectLocator {
|
|
|
346
347
|
}));
|
|
347
348
|
}
|
|
348
349
|
findProjectOfResolvedModule(resolvedModule) {
|
|
350
|
+
resolvedModule = (0, path_1.normalizePath)(resolvedModule);
|
|
349
351
|
if (resolvedModule.startsWith('node_modules/') ||
|
|
350
352
|
resolvedModule.includes('/node_modules/')) {
|
|
351
353
|
return undefined;
|
|
@@ -7,5 +7,5 @@ export interface AffectedProjectGraphContext {
|
|
|
7
7
|
touchedProjects: string[];
|
|
8
8
|
}
|
|
9
9
|
export interface TouchedProjectLocator<T extends Change = Change> {
|
|
10
|
-
(fileChanges: FileChange<T>[], projectGraphNodes?: Record<string, ProjectGraphProjectNode>, nxJson?: NxJsonConfiguration<any>, packageJson?: any, projectGraph?: ProjectGraph): string[] | Promise<string[]>;
|
|
10
|
+
(fileChanges: FileChange<T>[], projectGraphNodes?: Record<string, ProjectGraphProjectNode>, nxJson?: NxJsonConfiguration<any>, packageJson?: any, projectGraph?: ProjectGraph, projectDeletionAffectsAllProjects?: boolean): string[] | Promise<string[]>;
|
|
11
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { FileChange } from '../file-utils';
|
|
2
2
|
import { NxJsonConfiguration } from '../../config/nx-json';
|
|
3
3
|
import { ProjectGraph } from '../../config/project-graph';
|
|
4
|
-
export declare function filterAffected(graph: ProjectGraph, touchedFiles: FileChange[], nxJson?: NxJsonConfiguration, packageJson?: any): Promise<ProjectGraph>;
|
|
4
|
+
export declare function filterAffected(graph: ProjectGraph, touchedFiles: FileChange[], nxJson?: NxJsonConfiguration, packageJson?: any, projectDeletionAffectsAllProjects?: boolean): Promise<ProjectGraph>;
|
|
@@ -7,7 +7,7 @@ const touched_projects_1 = require("../../plugins/js/project-graph/affected/touc
|
|
|
7
7
|
const operators_1 = require("../operators");
|
|
8
8
|
const configuration_1 = require("../../config/configuration");
|
|
9
9
|
const project_glob_changes_1 = require("./locators/project-glob-changes");
|
|
10
|
-
async function filterAffected(graph, touchedFiles, nxJson = (0, configuration_1.readNxJson)(), packageJson = (0, file_utils_1.readPackageJson)()) {
|
|
10
|
+
async function filterAffected(graph, touchedFiles, nxJson = (0, configuration_1.readNxJson)(), packageJson = (0, file_utils_1.readPackageJson)(), projectDeletionAffectsAllProjects = true) {
|
|
11
11
|
// Additional affected logic should be in this array.
|
|
12
12
|
const touchedProjectLocators = [
|
|
13
13
|
workspace_projects_1.getTouchedProjects,
|
|
@@ -18,7 +18,7 @@ async function filterAffected(graph, touchedFiles, nxJson = (0, configuration_1.
|
|
|
18
18
|
const touchedProjects = [];
|
|
19
19
|
for (const locator of touchedProjectLocators) {
|
|
20
20
|
performance.mark(locator.name + ':start');
|
|
21
|
-
const projects = await locator(touchedFiles, graph.nodes, nxJson, packageJson, graph);
|
|
21
|
+
const projects = await locator(touchedFiles, graph.nodes, nxJson, packageJson, graph, projectDeletionAffectsAllProjects);
|
|
22
22
|
performance.mark(locator.name + ':end');
|
|
23
23
|
performance.measure(locator.name, locator.name + ':start', locator.name + ':end');
|
|
24
24
|
touchedProjects.push(...projects);
|
|
@@ -9,7 +9,7 @@ const fs_1 = require("fs");
|
|
|
9
9
|
const retrieve_workspace_files_1 = require("../../utils/retrieve-workspace-files");
|
|
10
10
|
const globs_1 = require("../../../utils/globs");
|
|
11
11
|
const get_plugins_1 = require("../../plugins/get-plugins");
|
|
12
|
-
const getTouchedProjectsFromProjectGlobChanges = async (touchedFiles, projectGraphNodes) => {
|
|
12
|
+
const getTouchedProjectsFromProjectGlobChanges = async (touchedFiles, projectGraphNodes, _nxJson, _packageJson, _projectGraph, projectDeletionAffectsAllProjects = true) => {
|
|
13
13
|
const globPattern = await (async () => {
|
|
14
14
|
// TODO: We need a quicker way to get patterns that should not
|
|
15
15
|
// require starting up plugin workers
|
|
@@ -33,7 +33,10 @@ const getTouchedProjectsFromProjectGlobChanges = async (touchedFiles, projectGra
|
|
|
33
33
|
// If the file no longer exists on disk, then it was deleted
|
|
34
34
|
if (!(0, fs_1.existsSync)((0, path_1.join)(workspace_root_1.workspaceRoot, touchedFile.file))) {
|
|
35
35
|
// If any project has been deleted, we must assume all projects were affected
|
|
36
|
-
|
|
36
|
+
if (projectDeletionAffectsAllProjects) {
|
|
37
|
+
return Object.keys(projectGraphNodes);
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
37
40
|
}
|
|
38
41
|
// Modified project config files are under a project's root, and implicitly
|
|
39
42
|
// mark it as affected. Thus, we don't need to handle it here.
|
|
@@ -3,6 +3,7 @@ import { ProjectConfiguration } from '../config/workspace-json-project-json';
|
|
|
3
3
|
import { CreateNodesFunction } from './plugins/public-api';
|
|
4
4
|
import { ConfigurationResult } from './utils/project-configuration-utils';
|
|
5
5
|
import type { ConfigurationSourceMaps } from './utils/project-configuration/source-maps';
|
|
6
|
+
import type { WorktreeConflictAdvice } from '../utils/git-worktrees';
|
|
6
7
|
export type ProjectGraphErrorTypes = AggregateCreateNodesError | MergeNodesError | CreateMetadataError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | ProcessDependenciesError | WorkspaceValidityError;
|
|
7
8
|
export declare class StaleProjectGraphCacheError extends Error {
|
|
8
9
|
constructor();
|
|
@@ -29,7 +30,17 @@ export declare class ProjectGraphError extends Error {
|
|
|
29
30
|
export declare class MultipleProjectsWithSameNameError extends Error {
|
|
30
31
|
conflicts: Map<string, string[]>;
|
|
31
32
|
projects: Record<string, ProjectConfiguration>;
|
|
32
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Set when some of the duplicates come from git worktrees nested in the
|
|
35
|
+
* workspace, which is a different fix than renaming them.
|
|
36
|
+
*/
|
|
37
|
+
worktreeAdvice?: WorktreeConflictAdvice;
|
|
38
|
+
constructor(conflicts: Map<string, string[]>, projects: Record<string, ProjectConfiguration>,
|
|
39
|
+
/**
|
|
40
|
+
* Set when some of the duplicates come from git worktrees nested in the
|
|
41
|
+
* workspace, which is a different fix than renaming them.
|
|
42
|
+
*/
|
|
43
|
+
worktreeAdvice?: WorktreeConflictAdvice);
|
|
33
44
|
}
|
|
34
45
|
export declare class ProjectWithExistingNameError extends Error {
|
|
35
46
|
projectName: string;
|
|
@@ -92,19 +92,44 @@ class ProjectGraphError extends Error {
|
|
|
92
92
|
exports.ProjectGraphError = ProjectGraphError;
|
|
93
93
|
_ProjectGraphError_partialProjectGraph = new WeakMap(), _ProjectGraphError_partialSourceMaps = new WeakMap();
|
|
94
94
|
class MultipleProjectsWithSameNameError extends Error {
|
|
95
|
-
constructor(conflicts, projects
|
|
95
|
+
constructor(conflicts, projects,
|
|
96
|
+
/**
|
|
97
|
+
* Set when some of the duplicates come from git worktrees nested in the
|
|
98
|
+
* workspace, which is a different fix than renaming them.
|
|
99
|
+
*/
|
|
100
|
+
worktreeAdvice) {
|
|
96
101
|
super([
|
|
97
102
|
`The following projects are defined in multiple locations:`,
|
|
98
103
|
...Array.from(conflicts.entries()).map(([project, roots]) => [`- ${project}: `, ...roots.map((r) => ` - ${r}`)].join('\n')),
|
|
99
104
|
'',
|
|
100
|
-
|
|
105
|
+
...(worktreeAdvice?.ignoreTargets.length
|
|
106
|
+
? [
|
|
107
|
+
'Some of these are inside git worktrees nested in this workspace. A worktree is a full checkout, so every project in it collides with the one it was checked out from.',
|
|
108
|
+
'',
|
|
109
|
+
// Which `.gitignore` matters: a leading slash anchors to the
|
|
110
|
+
// directory holding the file, and these paths are relative to
|
|
111
|
+
// the workspace, which is not always the repository root.
|
|
112
|
+
'To fix those, add the following to the .gitignore in the workspace root:',
|
|
113
|
+
...worktreeAdvice.ignoreTargets.map((target) => ` ${target}`),
|
|
114
|
+
// Ignoring the worktrees settles only the duplicates they
|
|
115
|
+
// explain; anything left is an ordinary name collision and still
|
|
116
|
+
// needs the ordinary answer.
|
|
117
|
+
...(worktreeAdvice.explainsAllConflicts
|
|
118
|
+
? []
|
|
119
|
+
: ['', `The rest are not from worktrees. ${RENAME_ADVICE}`]),
|
|
120
|
+
]
|
|
121
|
+
: [
|
|
122
|
+
`To fix this, ${RENAME_ADVICE[0].toLowerCase()}${RENAME_ADVICE.slice(1)}`,
|
|
123
|
+
]),
|
|
101
124
|
].join('\n'));
|
|
102
125
|
this.conflicts = conflicts;
|
|
103
126
|
this.projects = projects;
|
|
127
|
+
this.worktreeAdvice = worktreeAdvice;
|
|
104
128
|
this.name = this.constructor.name;
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
exports.MultipleProjectsWithSameNameError = MultipleProjectsWithSameNameError;
|
|
132
|
+
const RENAME_ADVICE = "Set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.";
|
|
108
133
|
class ProjectWithExistingNameError extends Error {
|
|
109
134
|
constructor(projectName, projectRoot) {
|
|
110
135
|
super(`The project "${projectName}" is defined in multiple locations.`);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.IsolatedPlugin = void 0;
|
|
4
|
+
exports.getPluginWorkerSocketId = getPluginWorkerSocketId;
|
|
4
5
|
const child_process_1 = require("child_process");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
5
7
|
const net_1 = require("net");
|
|
6
8
|
const path = require("path");
|
|
7
9
|
const logger_1 = require("../../../daemon/logger");
|
|
@@ -12,6 +14,7 @@ const installation_directory_1 = require("../../../utils/installation-directory"
|
|
|
12
14
|
const logger_2 = require("../../../utils/logger");
|
|
13
15
|
const progress_topics_1 = require("../../../utils/progress-topics");
|
|
14
16
|
const wait_for_socket_connection_1 = require("../../../utils/wait-for-socket-connection");
|
|
17
|
+
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
15
18
|
const resolve_plugin_1 = require("../resolve-plugin");
|
|
16
19
|
const messaging_1 = require("./messaging");
|
|
17
20
|
const plugin_lifecycle_manager_1 = require("./plugin-lifecycle-manager");
|
|
@@ -336,6 +339,10 @@ class IsolatedPlugin {
|
|
|
336
339
|
exports.IsolatedPlugin = IsolatedPlugin;
|
|
337
340
|
// --- Worker Spawning Utilities ---
|
|
338
341
|
global.nxPluginWorkerCount ??= 0;
|
|
342
|
+
function getPluginWorkerSocketId() {
|
|
343
|
+
const counter = global.nxPluginWorkerCount++;
|
|
344
|
+
return `${process.pid}-${counter.toString(36)}-${(0, crypto_1.randomBytes)(4).toString('hex')}`;
|
|
345
|
+
}
|
|
339
346
|
async function startPluginWorker(name) {
|
|
340
347
|
performance.mark(`start-plugin-worker:${name}`);
|
|
341
348
|
const isWorkerTypescript = path.extname(__filename) === '.ts';
|
|
@@ -352,7 +359,14 @@ async function startPluginWorker(name) {
|
|
|
352
359
|
}
|
|
353
360
|
: {}),
|
|
354
361
|
};
|
|
355
|
-
|
|
362
|
+
// Keep the host pid readable for diagnostics, the counter collision-free
|
|
363
|
+
// within this process, and entropy for a reused pid across process runs. The
|
|
364
|
+
// suffix is a base36 counter plus four random bytes: eleven characters after
|
|
365
|
+
// the pid until the counter passes 36, and fixed-length where the
|
|
366
|
+
// `performance.now()` stamp it replaced varied with process uptime. That
|
|
367
|
+
// keeps the socket path inside the 95-character budget `assertValidSocketPath`
|
|
368
|
+
// enforces.
|
|
369
|
+
const ipcPath = (0, socket_utils_1.getPluginOsSocketPath)(getPluginWorkerSocketId());
|
|
356
370
|
const worker = (0, child_process_1.spawn)(process.execPath, [
|
|
357
371
|
// Spawn the worker with the same resolve conditions Nx uses for plugin
|
|
358
372
|
// entries so the plugin's transitive workspace imports resolve to source.
|
|
@@ -361,6 +375,9 @@ async function startPluginWorker(name) {
|
|
|
361
375
|
workerPath,
|
|
362
376
|
ipcPath,
|
|
363
377
|
name,
|
|
378
|
+
// The host's root. The worker validates against this rather than re-resolving,
|
|
379
|
+
// so the two agree by construction.
|
|
380
|
+
workspace_root_1.workspaceRoot,
|
|
364
381
|
], {
|
|
365
382
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
366
383
|
env,
|
|
@@ -107,10 +107,23 @@ type PluginMessageDefs = DefineMessages<{
|
|
|
107
107
|
};
|
|
108
108
|
};
|
|
109
109
|
}>;
|
|
110
|
+
/**
|
|
111
|
+
* The sender's workspace root. Only the *worker* asserts on it, for host→worker
|
|
112
|
+
* messages; results and notifications carry the field but the host does not
|
|
113
|
+
* check it. The daemon channel is checked in the same one direction — the
|
|
114
|
+
* server validates inbound client messages and the client does not validate
|
|
115
|
+
* responses — so the two are alike rather than one being weaker.
|
|
116
|
+
* Optional and stamped centrally in `sendMessageOverSocket`, so the many
|
|
117
|
+
* message constructors need not set it; undefined is treated as not foreign,
|
|
118
|
+
* which is why this detects a shared socket directory rather than an attacker.
|
|
119
|
+
*/
|
|
120
|
+
export type WorkspaceStampedMessage = {
|
|
121
|
+
workspaceRoot?: string;
|
|
122
|
+
};
|
|
110
123
|
/** Union of all plugin worker message types */
|
|
111
|
-
export type PluginWorkerMessage = AllMessages<PluginMessageDefs
|
|
124
|
+
export type PluginWorkerMessage = AllMessages<PluginMessageDefs> & WorkspaceStampedMessage;
|
|
112
125
|
/** Union of all plugin worker result types */
|
|
113
|
-
export type PluginWorkerResult = AllResults<PluginMessageDefs
|
|
126
|
+
export type PluginWorkerResult = AllResults<PluginMessageDefs> & WorkspaceStampedMessage;
|
|
114
127
|
/** Result type for the load message */
|
|
115
128
|
export type PluginWorkerLoadResult = ResultOf<PluginMessageDefs, 'load'>;
|
|
116
129
|
/**
|
|
@@ -122,7 +135,7 @@ export type PluginWorkerEmitLogNotification = {
|
|
|
122
135
|
type: 'emitLog';
|
|
123
136
|
level: 'log' | 'warn' | 'error';
|
|
124
137
|
message: string;
|
|
125
|
-
};
|
|
138
|
+
} & WorkspaceStampedMessage;
|
|
126
139
|
export type PluginWorkerNotification = PluginWorkerEmitLogNotification;
|
|
127
140
|
export declare function isPluginWorkerNotification(message: Serializable): message is PluginWorkerNotification;
|
|
128
141
|
export declare function isPluginWorkerMessage(message: Serializable): message is PluginWorkerMessage;
|
|
@@ -7,6 +7,7 @@ exports.consumeMessage = consumeMessage;
|
|
|
7
7
|
exports.sendMessageOverSocket = sendMessageOverSocket;
|
|
8
8
|
const socket_utils_1 = require("../../../daemon/socket-utils");
|
|
9
9
|
const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
|
|
10
|
+
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
10
11
|
const NOTIFICATION_TYPES = [
|
|
11
12
|
'emitLog',
|
|
12
13
|
];
|
|
@@ -81,6 +82,8 @@ async function consumeMessage(socket, raw, handlers) {
|
|
|
81
82
|
* Sends a message over the socket with proper formatting.
|
|
82
83
|
*/
|
|
83
84
|
function sendMessageOverSocket(socket, message) {
|
|
85
|
+
// Stamped here rather than in each constructor, mirroring the daemon.
|
|
86
|
+
message.workspaceRoot = workspace_root_1.workspaceRoot;
|
|
84
87
|
socket.write((0, socket_utils_1.serialize)(message));
|
|
85
88
|
socket.write(consume_messages_from_socket_1.MESSAGE_END_SEQ);
|
|
86
89
|
}
|
|
@@ -7,6 +7,7 @@ node_perf_hooks_1.performance.mark(`plugin worker ${process.pid} code loading --
|
|
|
7
7
|
const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
|
|
8
8
|
const logger_1 = require("../../../utils/logger");
|
|
9
9
|
const serializable_error_1 = require("../../../utils/serializable-error");
|
|
10
|
+
const daemon_message_1 = require("../../../daemon/message-types/daemon-message");
|
|
10
11
|
const messaging_1 = require("./messaging");
|
|
11
12
|
const worker_streaming_1 = require("./worker-streaming");
|
|
12
13
|
const fs_1 = require("fs");
|
|
@@ -23,6 +24,19 @@ global.NX_PLUGIN_WORKER = true;
|
|
|
23
24
|
let plugin;
|
|
24
25
|
const socketPath = process.argv[2];
|
|
25
26
|
const expectedPluginName = process.argv[3];
|
|
27
|
+
// The host's root, passed explicitly rather than re-resolved: a host that set
|
|
28
|
+
// its root at runtime would resolve a different one here and drop every
|
|
29
|
+
// legitimate message as foreign.
|
|
30
|
+
const hostWorkspaceRoot = process.argv[4];
|
|
31
|
+
// Positional, so inserting an argument host-side shifts all of them — and the
|
|
32
|
+
// symptom is silent and total: an undefined hostWorkspaceRoot makes every
|
|
33
|
+
// message look foreign while the host waits out its plugin timeout.
|
|
34
|
+
if (!socketPath || !expectedPluginName || !hostWorkspaceRoot) {
|
|
35
|
+
console.error(`[plugin-worker] started with an incomplete argument list ` +
|
|
36
|
+
`(socketPath=${socketPath}, pluginName=${expectedPluginName}, hostWorkspaceRoot=${hostWorkspaceRoot}). ` +
|
|
37
|
+
`This is an Nx bug — please report it at https://github.com/nrwl/nx/issues.`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
26
40
|
const CONNECT_TIMEOUT_MS = 30_000;
|
|
27
41
|
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. ` +
|
|
28
42
|
'Plugin workers expect to receive a socket connection from the parent process shortly after being started. ' +
|
|
@@ -45,6 +59,16 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
45
59
|
if (!(0, messaging_1.isPluginWorkerMessage)(message)) {
|
|
46
60
|
return;
|
|
47
61
|
}
|
|
62
|
+
// Same check the daemon applies to its own socket. Dropped rather than thrown:
|
|
63
|
+
// a stray foreign message must not kill a worker serving its host. The daemon
|
|
64
|
+
// has a response channel and surfaces it to the client instead.
|
|
65
|
+
try {
|
|
66
|
+
(0, daemon_message_1.assertNotForeignWorkspaceMessage)(message, hostWorkspaceRoot, `The Nx plugin worker "${expectedPluginName}" (pid: ${process.pid})`);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
logger_1.logger.verbose(`[plugin-worker] ignored a "${message.type}" message: ${e instanceof Error ? e.message : e}`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
48
72
|
return (0, messaging_1.consumeMessage)(socket, message, {
|
|
49
73
|
load: async ({ plugin: pluginConfiguration, root, name, pluginPath, shouldRegisterTSTranspiler, }) => {
|
|
50
74
|
loadErrorTimeout?.clear();
|
|
@@ -104,8 +128,24 @@ const server = (0, net_1.createServer)((socket) => {
|
|
|
104
128
|
process.exit(0);
|
|
105
129
|
});
|
|
106
130
|
});
|
|
107
|
-
server.
|
|
108
|
-
|
|
131
|
+
server.on('error', (err) => {
|
|
132
|
+
// Without this the host only sees "exited before the connection was
|
|
133
|
+
// established"; the errno distinguishes a denied bind from EADDRINUSE.
|
|
134
|
+
console.error(`[plugin-worker] "${expectedPluginName}" (pid: ${process.pid}) failed to listen on ${socketPath}: ${err.message}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
});
|
|
137
|
+
// A worker killed without its 'end' handler leaves the socket behind. Colliding
|
|
138
|
+
// takes a recycled host pid landing on the same counter *and* the same 4 random
|
|
139
|
+
// bytes; the random component is what this unlink rests on, and on Windows it is
|
|
140
|
+
// the only barrier, since the endpoint is a namespace object and the mode never
|
|
141
|
+
// applies. A failed bind surfaces through the error handler above.
|
|
142
|
+
try {
|
|
143
|
+
(0, fs_1.unlinkSync)(socketPath);
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
server.listen(socketPath, () => {
|
|
147
|
+
logger_1.logger.verbose(`[plugin-worker] "${expectedPluginName}" (pid: ${process.pid}) listening on ${socketPath}`);
|
|
148
|
+
});
|
|
109
149
|
async function withErrorHandling(cb) {
|
|
110
150
|
try {
|
|
111
151
|
const result = await cb();
|
|
@@ -136,15 +176,25 @@ function setErrorTimeout(timeoutMs, errorMessage) {
|
|
|
136
176
|
},
|
|
137
177
|
};
|
|
138
178
|
}
|
|
139
|
-
const
|
|
179
|
+
const cleanup = () => {
|
|
140
180
|
server.close();
|
|
141
181
|
try {
|
|
142
182
|
(0, fs_1.unlinkSync)(socketPath);
|
|
143
183
|
}
|
|
144
184
|
catch (e) { }
|
|
145
|
-
process.exit(exitCode);
|
|
146
185
|
};
|
|
147
|
-
const events = ['SIGINT', 'SIGTERM', 'SIGQUIT'
|
|
148
|
-
events.forEach((event) => process.once(event,
|
|
149
|
-
|
|
150
|
-
process.
|
|
186
|
+
const events = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
|
|
187
|
+
events.forEach((event) => process.once(event, () => {
|
|
188
|
+
cleanup();
|
|
189
|
+
process.exit(0);
|
|
190
|
+
}));
|
|
191
|
+
// Cleanup only: process.exit() here would override the real exit code.
|
|
192
|
+
process.once('exit', cleanup);
|
|
193
|
+
const fatalHandler = (error) => {
|
|
194
|
+
// Registering this handler suppresses Node's default reporting.
|
|
195
|
+
console.error(error);
|
|
196
|
+
cleanup();
|
|
197
|
+
process.exit(1);
|
|
198
|
+
};
|
|
199
|
+
process.once('uncaughtException', fatalHandler);
|
|
200
|
+
process.once('unhandledRejection', fatalHandler);
|
|
@@ -354,6 +354,26 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
|
|
|
354
354
|
(0, tmp_dir_1.markDaemonAsDisabled)(e.message);
|
|
355
355
|
return buildProjectGraphAndSourceMapsWithoutDaemon();
|
|
356
356
|
}
|
|
357
|
+
if (e.daemonPermissionError) {
|
|
358
|
+
// Deliberately not disabled: unlike the inotify limit above, a socket
|
|
359
|
+
// owned by someone else stops being there when it is removed or the
|
|
360
|
+
// machine reboots, and disabling until `nx reset` would outlive the
|
|
361
|
+
// cause and hide the fix from anyone who followed the advice.
|
|
362
|
+
// The first line carries the errno, and it is the one token that tells
|
|
363
|
+
// the two causes apart: EACCES is a socket owned by someone else (delete
|
|
364
|
+
// it), EPERM is a sandbox refusing the connect syscall (allow unix
|
|
365
|
+
// sockets under the Nx socket root). The message hedges between exactly
|
|
366
|
+
// those two because it cannot tell them apart, and this branch does not
|
|
367
|
+
// call writeDaemonLogs, so dropping the line loses the errno for good.
|
|
368
|
+
const [summary, ...details] = e.message.split('\n');
|
|
369
|
+
output_1.output.note({
|
|
370
|
+
title: `${summary} Continuing without the daemon.`,
|
|
371
|
+
// The blank line after the summary separates paragraphs when the
|
|
372
|
+
// message is printed as one blob; as bodyLines it is a leading gap.
|
|
373
|
+
bodyLines: details[0] === '' ? details.slice(1) : details,
|
|
374
|
+
});
|
|
375
|
+
return buildProjectGraphAndSourceMapsWithoutDaemon();
|
|
376
|
+
}
|
|
357
377
|
if (e.internalDaemonError) {
|
|
358
378
|
const errorLogFile = (0, tmp_dir_1.writeDaemonLogs)(e.message);
|
|
359
379
|
output_1.output.warn({
|
|
@@ -7,6 +7,8 @@ exports.createRootMap = createRootMap;
|
|
|
7
7
|
const error_types_1 = require("../../error-types");
|
|
8
8
|
const target_merging_1 = require("./target-merging");
|
|
9
9
|
const target_normalization_1 = require("./target-normalization");
|
|
10
|
+
const git_worktrees_1 = require("../../../utils/git-worktrees");
|
|
11
|
+
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
10
12
|
const name_substitution_manager_1 = require("./name-substitution-manager");
|
|
11
13
|
const source_maps_1 = require("./source-maps");
|
|
12
14
|
const minimatch_1 = require("minimatch");
|
|
@@ -194,7 +196,9 @@ function readProjectConfigurationsFromRootMap(projectRootMap) {
|
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
198
|
if (conflicts.size > 0) {
|
|
197
|
-
|
|
199
|
+
// Only on the way to throwing, so a workspace without duplicates never
|
|
200
|
+
// pays for reading git's worktree registry.
|
|
201
|
+
throw new error_types_1.MultipleProjectsWithSameNameError(conflicts, projects, (0, git_worktrees_1.analyzeWorktreeConflicts)(workspace_root_1.workspaceRoot, conflicts) ?? undefined);
|
|
198
202
|
}
|
|
199
203
|
if (projectRootsWithNoName.length > 0) {
|
|
200
204
|
throw new error_types_1.ProjectsWithNoNameError(projectRootsWithNoName, projects);
|
|
@@ -5,10 +5,13 @@ exports.normalizeTarget = normalizeTarget;
|
|
|
5
5
|
exports.validateAndNormalizeProjectRootMap = validateAndNormalizeProjectRootMap;
|
|
6
6
|
const executor_utils_1 = require("../../../command-line/run/executor-utils");
|
|
7
7
|
const fileutils_1 = require("../../../utils/fileutils");
|
|
8
|
+
const long_running_target_1 = require("../../../utils/long-running-target");
|
|
9
|
+
const output_1 = require("../../../utils/output");
|
|
8
10
|
const to_project_name_1 = require("../../../config/to-project-name");
|
|
9
11
|
const error_types_1 = require("../../error-types");
|
|
10
12
|
const target_merging_1 = require("./target-merging");
|
|
11
13
|
const node_fs_1 = require("node:fs");
|
|
14
|
+
const git_worktrees_1 = require("../../../utils/git-worktrees");
|
|
12
15
|
const path_1 = require("path");
|
|
13
16
|
function validateProject(project,
|
|
14
17
|
// name -> project
|
|
@@ -65,15 +68,137 @@ function normalizeTarget(target, project, workspaceRoot, projectsMap, errorMsgKe
|
|
|
65
68
|
}
|
|
66
69
|
return target;
|
|
67
70
|
}
|
|
71
|
+
// TODO(v24): remove the legacy target-name cache fallback and its warning.
|
|
72
|
+
// Removal needs a second mechanism for plugin-inferred targets, which the
|
|
73
|
+
// accompanying migration cannot reach.
|
|
74
|
+
/**
|
|
75
|
+
* Whether `target` is cacheable only by way of the legacy name-based fallback:
|
|
76
|
+
* the exact target-name key of `targetDefaults` declares `cache: true`, but an
|
|
77
|
+
* executor key won target-default resolution instead, so the merged target never
|
|
78
|
+
* received it.
|
|
79
|
+
*
|
|
80
|
+
* A `true` result means the user's `cache: true` silently lost, so this doubles
|
|
81
|
+
* as the condition for warning them that the name key is being shadowed.
|
|
82
|
+
*/
|
|
83
|
+
function isLegacyCachedTarget(targetName, targetDefaults, target) {
|
|
84
|
+
// Resolution already decided `cache`, so the name key isn't shadowed.
|
|
85
|
+
if (target.cache !== undefined) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (isLongRunningTarget(targetName, target)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
// Restricted to shadowing, which is narrower than what pre-23 restored: that
|
|
92
|
+
// derivation matched on target name alone, so a name key dropped as
|
|
93
|
+
// incompatible (its entry declared a foreign executor) was cacheable too.
|
|
94
|
+
// Restoring that as well would mean writing `cache` with no key to name in
|
|
95
|
+
// the warning, and no migration able to retire it. Deliberately not covered.
|
|
96
|
+
if (!findShadowingTargetDefaultKey(targetDefaults, target)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return declaresCacheTrue(targetDefaults?.[targetName]);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Whether the name key declares `cache: true` on an entry that always applies.
|
|
103
|
+
*
|
|
104
|
+
* Filters are deliberately not evaluated. They cannot express a pre-23 config
|
|
105
|
+
* (the filtered array shape postdates the behavior being restored), and a
|
|
106
|
+
* filtered entry declaring `cache` may or may not apply to this project — so
|
|
107
|
+
* rather than guess, a filtered `cache` declares the value unknowable and
|
|
108
|
+
* nothing is restored. Among unfiltered entries the last wins, matching the
|
|
109
|
+
* in-key merge order.
|
|
110
|
+
*/
|
|
111
|
+
function declaresCacheTrue(value) {
|
|
112
|
+
if (!value) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
116
|
+
let declared;
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
// `nx.json` is hand-edited; a null or scalar entry would throw here.
|
|
119
|
+
if (!entry || typeof entry !== 'object')
|
|
120
|
+
continue;
|
|
121
|
+
if (entry.cache === undefined)
|
|
122
|
+
continue;
|
|
123
|
+
if (entry.filter)
|
|
124
|
+
return false;
|
|
125
|
+
declared = entry.cache;
|
|
126
|
+
}
|
|
127
|
+
return declared === true;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The normalization-time half of the pre-23 `longRunningTask` guard, which kept
|
|
131
|
+
* `cacheableOperations` from ever making these cacheable. Its remaining clause
|
|
132
|
+
* — `task.overrides['watch']` — is a runtime invocation override with no target
|
|
133
|
+
* equivalent, so it has no counterpart here.
|
|
134
|
+
*/
|
|
135
|
+
function isLongRunningTarget(targetName, target) {
|
|
136
|
+
return !!target.continuous || (0, long_running_target_1.isLongRunningTargetName)(targetName);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The `targetDefaults` key that beat the target-name key for `target`. Only an
|
|
140
|
+
* executor key can: key precedence puts the exact target name ahead of every
|
|
141
|
+
* glob, so nothing else outranks it. Undefined when the name key lost for
|
|
142
|
+
* another reason (e.g. its entry declared a foreign executor and was dropped as
|
|
143
|
+
* incompatible) — see {@link isLegacyCachedTarget} for why that case is left
|
|
144
|
+
* alone even though pre-23 restored it.
|
|
145
|
+
*
|
|
146
|
+
* `hasOwnProperty` rather than a lookup: an executor named `__proto__` resolves
|
|
147
|
+
* through the prototype chain to a truthy object, which would report a key the
|
|
148
|
+
* user never wrote.
|
|
149
|
+
*/
|
|
150
|
+
function findShadowingTargetDefaultKey(targetDefaults, target) {
|
|
151
|
+
return target.executor &&
|
|
152
|
+
targetDefaults &&
|
|
153
|
+
Object.prototype.hasOwnProperty.call(targetDefaults, target.executor)
|
|
154
|
+
? target.executor
|
|
155
|
+
: undefined;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Emits a single grouped warning for every (shadowing key, target-name key)
|
|
159
|
+
* pair that relied on the deprecated fallback. Grouping matters because the
|
|
160
|
+
* same pair recurs in every affected project — a per-target warning would
|
|
161
|
+
* print hundreds of identical lines in a large workspace.
|
|
162
|
+
*/
|
|
163
|
+
function warnAboutLegacyCachedTargets(legacyCacheReads) {
|
|
164
|
+
if (legacyCacheReads.size === 0) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const bodyLines = [];
|
|
168
|
+
for (const [shadowingKey, targetKeys] of legacyCacheReads) {
|
|
169
|
+
for (const targetKey of targetKeys) {
|
|
170
|
+
bodyLines.push(` - "${shadowingKey}" does not set "cache", so it was read from "${targetKey}"`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
bodyLines.push('', 'An executor key applies to every target that resolves through it, so exclude any continuous target before setting "cache" on one — a target that is both cacheable and continuous is rejected.', 'Target defaults resolve to a single key rather than merging, so an executor key hides the target name key entirely.', 'Set "cache" on the executor key to keep these targets cacheable — reading it from the target name key is deprecated and will be removed in Nx 24.');
|
|
174
|
+
output_1.output.warn({
|
|
175
|
+
title: 'Some targets are only cacheable through a deprecated fallback.',
|
|
176
|
+
bodyLines,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
68
179
|
function normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot,
|
|
69
180
|
/**
|
|
70
181
|
* Project configurations keyed by project name
|
|
71
182
|
*/
|
|
72
|
-
projects
|
|
183
|
+
projects,
|
|
184
|
+
/**
|
|
185
|
+
* Shadowing `targetDefaults` key -> target name keys its `cache` was read
|
|
186
|
+
* from. Accumulated across projects so the deprecation warns once per pair.
|
|
187
|
+
*/
|
|
188
|
+
legacyCacheReads) {
|
|
73
189
|
const targetErrorMessage = [];
|
|
74
190
|
for (const targetName in project.targets) {
|
|
75
191
|
project.targets[targetName] = normalizeTarget(project.targets[targetName], project, workspaceRoot, projects, [project.root, targetName].join(':'));
|
|
76
192
|
const target = project.targets[targetName];
|
|
193
|
+
const targetDefaults = nxJsonConfiguration.targetDefaults;
|
|
194
|
+
if (isLegacyCachedTarget(targetName, targetDefaults, target)) {
|
|
195
|
+
target.cache = true;
|
|
196
|
+
// Always defined: `isLegacyCachedTarget` returns false without it.
|
|
197
|
+
const shadowingKey = findShadowingTargetDefaultKey(targetDefaults, target);
|
|
198
|
+
const targetKeys = legacyCacheReads.get(shadowingKey) ?? new Set();
|
|
199
|
+
targetKeys.add(targetName);
|
|
200
|
+
legacyCacheReads.set(shadowingKey, targetKeys);
|
|
201
|
+
}
|
|
77
202
|
if (
|
|
78
203
|
// If the target has no executor or command, it doesn't do anything
|
|
79
204
|
!target.executor &&
|
|
@@ -106,6 +231,7 @@ function validateAndNormalizeProjectRootMap(workspaceRoot, projectRootMap, nxJso
|
|
|
106
231
|
const conflicts = new Map();
|
|
107
232
|
const projectRootsWithNoName = [];
|
|
108
233
|
const validityErrors = [];
|
|
234
|
+
const legacyCacheReads = new Map();
|
|
109
235
|
for (const root in projectRootMap) {
|
|
110
236
|
const project = projectRootMap[root];
|
|
111
237
|
// We're setting `// targets` as a comment `targets` is empty due to Project Crystal.
|
|
@@ -155,7 +281,7 @@ function validateAndNormalizeProjectRootMap(workspaceRoot, projectRootMap, nxJso
|
|
|
155
281
|
for (const root in projectRootMap) {
|
|
156
282
|
const project = projectRootMap[root];
|
|
157
283
|
try {
|
|
158
|
-
normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot, projects);
|
|
284
|
+
normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot, projects, legacyCacheReads);
|
|
159
285
|
}
|
|
160
286
|
catch (e) {
|
|
161
287
|
if (e instanceof error_types_1.WorkspaceValidityError) {
|
|
@@ -166,9 +292,13 @@ function validateAndNormalizeProjectRootMap(workspaceRoot, projectRootMap, nxJso
|
|
|
166
292
|
}
|
|
167
293
|
}
|
|
168
294
|
}
|
|
295
|
+
warnAboutLegacyCachedTargets(legacyCacheReads);
|
|
169
296
|
const errors = [];
|
|
170
297
|
if (conflicts.size > 0) {
|
|
171
|
-
|
|
298
|
+
// Only on the way to throwing, so a workspace without duplicates never
|
|
299
|
+
// pays for reading git's worktree registry.
|
|
300
|
+
const worktreeAdvice = (0, git_worktrees_1.analyzeWorktreeConflicts)(workspaceRoot, conflicts);
|
|
301
|
+
errors.push(new error_types_1.MultipleProjectsWithSameNameError(conflicts, projects, worktreeAdvice ?? undefined));
|
|
172
302
|
}
|
|
173
303
|
if (projectRootsWithNoName.length > 0) {
|
|
174
304
|
errors.push(new error_types_1.ProjectsWithNoNameError(projectRootsWithNoName, projects));
|
|
@@ -166,10 +166,13 @@ function getOwnerTargetForTask(task, graph) {
|
|
|
166
166
|
for (const targets of Object.values(project.data.metadata.targetGroups)) {
|
|
167
167
|
if (targets.includes(task.target.target)) {
|
|
168
168
|
for (const target of targets) {
|
|
169
|
-
|
|
169
|
+
// a target group can name a target the project does not define —
|
|
170
|
+
// inference plugins build the group and the targets separately, so
|
|
171
|
+
// the two can disagree. skip the phantom rather than dereferencing it.
|
|
172
|
+
if (project.data.targets[target]?.metadata?.nonAtomizedTarget) {
|
|
170
173
|
return [
|
|
171
174
|
target,
|
|
172
|
-
project.data.targets[target]
|
|
175
|
+
project.data.targets[target]?.metadata?.nonAtomizedTarget,
|
|
173
176
|
];
|
|
174
177
|
}
|
|
175
178
|
}
|
|
@@ -36,6 +36,7 @@ const fileutils_1 = require("../utils/fileutils");
|
|
|
36
36
|
const find_matching_projects_1 = require("../utils/find-matching-projects");
|
|
37
37
|
const legacy_depends_on_warning_1 = require("./legacy-depends-on-warning");
|
|
38
38
|
const globs_1 = require("../utils/globs");
|
|
39
|
+
const long_running_target_1 = require("../utils/long-running-target");
|
|
39
40
|
const path_1 = require("../utils/path");
|
|
40
41
|
const serialize_overrides_into_command_line_1 = require("../utils/serialize-overrides-into-command-line");
|
|
41
42
|
const split_target_1 = require("../utils/split-target");
|
|
@@ -429,14 +430,9 @@ function isCacheableTask(task) {
|
|
|
429
430
|
return task.cache;
|
|
430
431
|
}
|
|
431
432
|
function longRunningTask(task) {
|
|
432
|
-
const t = task.target.target;
|
|
433
433
|
return (task.continuous ||
|
|
434
434
|
(!!task.overrides['watch'] && task.overrides['watch'] !== 'false') ||
|
|
435
|
-
|
|
436
|
-
t.endsWith('-watch') ||
|
|
437
|
-
t === 'serve' ||
|
|
438
|
-
t === 'dev' ||
|
|
439
|
-
t === 'start');
|
|
435
|
+
(0, long_running_target_1.isLongRunningTargetName)(task.target.target));
|
|
440
436
|
}
|
|
441
437
|
// TODO: vsavkin remove when nx-cloud doesn't depend on it
|
|
442
438
|
function unparse(options) {
|