nx 22.7.1 → 22.7.3
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/bin/nx.d.ts +1 -0
- package/dist/bin/nx.js +3 -0
- package/dist/src/ai/clone-ai-config-repo.js +20 -3
- package/dist/src/analytics/analytics.js +10 -1
- package/dist/src/command-line/format/format.js +15 -5
- package/dist/src/command-line/graph/graph.js +0 -1
- package/dist/src/command-line/release/config/use-legacy-versioning.d.ts +2 -0
- package/dist/src/command-line/release/config/use-legacy-versioning.js +8 -0
- package/dist/src/command-line/report/report.js +0 -1
- package/dist/src/config/misc-interfaces.d.ts +6 -0
- package/dist/src/config/schema-utils.js +2 -1
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.css +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/server/latest-nx.js +2 -0
- package/dist/src/daemon/server/start.d.ts +1 -1
- package/dist/src/daemon/server/start.js +2 -0
- 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/npm-parser.js +37 -19
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +51 -4
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +12 -4
- package/dist/src/plugins/js/utils/typescript.d.ts +7 -0
- package/dist/src/plugins/js/utils/typescript.js +39 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +2 -0
- package/dist/src/project-graph/plugins/resolve-plugin.d.ts +7 -4
- package/dist/src/project-graph/plugins/resolve-plugin.js +152 -33
- package/dist/src/project-graph/plugins/utils.js +13 -7
- package/dist/src/tasks-runner/life-cycles/task-history-life-cycle-old.js +13 -2
- package/dist/src/tasks-runner/life-cycles/task-history-life-cycle.js +16 -5
- package/dist/src/tasks-runner/life-cycles/tui-summary-life-cycle.js +11 -2
- package/dist/src/tasks-runner/run-command.js +8 -1
- package/dist/src/tasks-runner/task-orchestrator.js +20 -4
- package/dist/src/tasks-runner/tasks-schedule.js +3 -3
- package/dist/src/utils/ab-testing.js +12 -0
- package/dist/src/utils/compile-cache.d.ts +24 -0
- package/dist/src/utils/compile-cache.js +49 -0
- package/dist/src/utils/enable-compile-cache.d.ts +1 -0
- package/dist/src/utils/enable-compile-cache.js +7 -0
- package/dist/src/utils/has-nx-js-plugin.d.ts +9 -0
- package/dist/src/utils/has-nx-js-plugin.js +24 -0
- package/dist/src/utils/logger.d.ts +12 -1
- package/dist/src/utils/logger.js +57 -36
- package/dist/src/utils/nx-key.d.ts +0 -1
- package/dist/src/utils/nx-key.js +20 -23
- package/dist/src/utils/output.d.ts +3 -2
- package/dist/src/utils/output.js +29 -28
- package/dist/src/utils/package-json.js +2 -13
- package/dist/src/utils/perf-logging.js +3 -1
- package/package.json +22 -15
|
@@ -6,28 +6,59 @@ exports.getPluginPathAndName = getPluginPathAndName;
|
|
|
6
6
|
const tslib_1 = require("tslib");
|
|
7
7
|
const path = tslib_1.__importStar(require("node:path"));
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
|
+
const resolve_exports_1 = require("resolve.exports");
|
|
9
10
|
const packages_1 = require("../../plugins/js/utils/packages");
|
|
11
|
+
const typescript_1 = require("../../plugins/js/utils/typescript");
|
|
10
12
|
const fileutils_1 = require("../../utils/fileutils");
|
|
11
13
|
const logger_1 = require("../../utils/logger");
|
|
12
14
|
const path_1 = require("../../utils/path");
|
|
13
15
|
const workspace_root_1 = require("../../utils/workspace-root");
|
|
14
16
|
const find_project_for_path_1 = require("../utils/find-project-for-path");
|
|
15
17
|
const retrieve_workspace_files_1 = require("../utils/retrieve-workspace-files");
|
|
18
|
+
const TS_SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.cts', '.mts']);
|
|
16
19
|
let projectsWithoutInference;
|
|
17
20
|
let projectsWithoutInferencePromise = null;
|
|
18
21
|
async function resolveNxPlugin(moduleName, root, paths) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
// Default plugins (see `getDefaultPlugins` in `get-plugins.ts`) are passed
|
|
23
|
+
// as absolute file paths to compiled bundles inside `nx` itself; they are
|
|
24
|
+
// never workspace-local. Skip the project load entirely for them to avoid
|
|
25
|
+
// recursing through `retrieveProjectConfigurationsWithoutPluginInference`,
|
|
26
|
+
// which itself triggers default-plugin loading.
|
|
27
|
+
if (!path.isAbsolute(moduleName)) {
|
|
28
|
+
let resolvedFromNode;
|
|
29
|
+
try {
|
|
30
|
+
resolvedFromNode = require.resolve(moduleName, { paths });
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
33
|
+
// Load projects if Node couldn't resolve (so the local fallback can run)
|
|
34
|
+
// OR if Node resolved to a workspace-internal path (a symlinked workspace
|
|
35
|
+
// package whose source-first lookup should win over the symlinked dist).
|
|
36
|
+
if (!resolvedFromNode ||
|
|
37
|
+
isWorkspaceLocalResolution(resolvedFromNode, root)) {
|
|
38
|
+
projectsWithoutInferencePromise ??=
|
|
39
|
+
(0, retrieve_workspace_files_1.retrieveProjectConfigurationsWithoutPluginInference)(root);
|
|
40
|
+
projectsWithoutInference ??= await projectsWithoutInferencePromise;
|
|
41
|
+
}
|
|
27
42
|
}
|
|
28
43
|
const { pluginPath, name, shouldRegisterTSTranspiler } = getPluginPathAndName(moduleName, paths, projectsWithoutInference, root);
|
|
29
44
|
return { pluginPath, name, shouldRegisterTSTranspiler };
|
|
30
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Distinguishes a symlinked workspace package (where `require.resolve`
|
|
48
|
+
* follows the package-manager symlink into the workspace source tree) from
|
|
49
|
+
* a truly-installed dependency under `node_modules/`. The former needs the
|
|
50
|
+
* source-first lookup to bypass the dist that Node would otherwise return.
|
|
51
|
+
*/
|
|
52
|
+
function isWorkspaceLocalResolution(resolvedPath, root) {
|
|
53
|
+
const normalizedRoot = path.normalize(root);
|
|
54
|
+
const normalizedPath = path.normalize(resolvedPath);
|
|
55
|
+
return (normalizedPath.startsWith(normalizedRoot + path.sep) &&
|
|
56
|
+
!normalizedPath.includes(path.sep + 'node_modules' + path.sep));
|
|
57
|
+
}
|
|
58
|
+
function isPackageResolutionError(e) {
|
|
59
|
+
const code = e.code;
|
|
60
|
+
return (code === 'MODULE_NOT_FOUND' || code === 'ERR_PACKAGE_PATH_NOT_EXPORTED');
|
|
61
|
+
}
|
|
31
62
|
function readPluginMainFromProjectConfiguration(plugin) {
|
|
32
63
|
const { main } = Object.values(plugin.targets).find((x) => [
|
|
33
64
|
'@nx/js:tsc',
|
|
@@ -46,31 +77,44 @@ function resolveLocalNxPlugin(importPath, projects, root = workspace_root_1.work
|
|
|
46
77
|
}
|
|
47
78
|
function getPluginPathAndName(moduleName, paths, projects, root) {
|
|
48
79
|
let pluginPath;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
80
|
+
// Resolve local workspace plugins from source first so the workspace's
|
|
81
|
+
// `customConditions`/`development` exports condition wins over the built
|
|
82
|
+
// `dist` artifact that Node's resolver would otherwise pick up via the
|
|
83
|
+
// `default` condition (Node ignores TypeScript custom conditions). Skipped
|
|
84
|
+
// when `projects` weren't loaded — the caller already determined that the
|
|
85
|
+
// import isn't a workspace package.
|
|
86
|
+
const localPlugin = projects
|
|
87
|
+
? resolveLocalNxPlugin(moduleName, projects, root)
|
|
88
|
+
: null;
|
|
89
|
+
if (localPlugin) {
|
|
90
|
+
pluginPath = tryResolveLocalPluginFromSource(moduleName, localPlugin, root);
|
|
91
|
+
if (!pluginPath && getSubpathOfLocalPackage(moduleName, localPlugin)) {
|
|
92
|
+
throwUnresolvableLocalPluginError(moduleName, localPlugin, root);
|
|
93
|
+
}
|
|
56
94
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
95
|
+
if (!pluginPath) {
|
|
96
|
+
try {
|
|
97
|
+
pluginPath = require.resolve(moduleName, { paths });
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
if (localPlugin && isPackageResolutionError(e)) {
|
|
101
|
+
throwUnresolvableLocalPluginError(moduleName, localPlugin, root);
|
|
64
102
|
}
|
|
65
|
-
|
|
66
|
-
logger_1.logger.error(`Plugin listed in \`nx.json\` not found: ${moduleName}`);
|
|
103
|
+
if (e.code !== 'MODULE_NOT_FOUND') {
|
|
67
104
|
throw e;
|
|
68
105
|
}
|
|
69
|
-
|
|
70
|
-
|
|
106
|
+
if (localPlugin) {
|
|
107
|
+
throwUnresolvableLocalPluginError(moduleName, localPlugin, root);
|
|
108
|
+
}
|
|
109
|
+
logger_1.logger.error(`Plugin listed in \`nx.json\` not found: ${moduleName}`);
|
|
71
110
|
throw e;
|
|
72
111
|
}
|
|
73
112
|
}
|
|
113
|
+
const ext = path.extname(pluginPath);
|
|
114
|
+
// Directory paths fall through to Node's `package.json` `main` resolution
|
|
115
|
+
// which may land on a TS file; only opt out of TS transpiler registration
|
|
116
|
+
// when the resolved path is unambiguously JS.
|
|
117
|
+
const shouldRegisterTSTranspiler = ext === '' || TS_SOURCE_EXTENSIONS.has(ext);
|
|
74
118
|
const packageJsonPath = path.join(pluginPath, 'package.json');
|
|
75
119
|
const { name } = !['.ts', '.js'].some((x) => path.extname(moduleName) === x) && // Not trying to point to a ts or js file
|
|
76
120
|
(0, node_fs_1.existsSync)(packageJsonPath) // plugin has a package.json
|
|
@@ -78,12 +122,84 @@ function getPluginPathAndName(moduleName, paths, projects, root) {
|
|
|
78
122
|
: { name: moduleName };
|
|
79
123
|
return { pluginPath, name, shouldRegisterTSTranspiler };
|
|
80
124
|
}
|
|
125
|
+
function getSubpathOfLocalPackage(moduleName, plugin) {
|
|
126
|
+
const packageName = plugin.projectConfig.metadata?.js?.packageName;
|
|
127
|
+
if (!packageName || !moduleName.startsWith(packageName + '/')) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return '.' + moduleName.slice(packageName.length);
|
|
131
|
+
}
|
|
132
|
+
function tryResolveLocalPluginFromSource(moduleName, plugin, root) {
|
|
133
|
+
if (plugin.resolvedFile) {
|
|
134
|
+
return plugin.resolvedFile;
|
|
135
|
+
}
|
|
136
|
+
const subpath = getSubpathOfLocalPackage(moduleName, plugin);
|
|
137
|
+
if (subpath) {
|
|
138
|
+
return resolveSubpathFromExports(plugin.projectConfig, plugin.path, subpath, root);
|
|
139
|
+
}
|
|
140
|
+
const main = readPluginMainFromProjectConfiguration(plugin.projectConfig);
|
|
141
|
+
return main ? path.join(root, main) : null;
|
|
142
|
+
}
|
|
143
|
+
function throwUnresolvableLocalPluginError(moduleName, plugin, root) {
|
|
144
|
+
const subpath = getSubpathOfLocalPackage(moduleName, plugin);
|
|
145
|
+
const packageName = plugin.projectConfig.metadata?.js?.packageName;
|
|
146
|
+
if (subpath) {
|
|
147
|
+
throw new Error(`Unable to resolve local plugin "${moduleName}". The import targets ` +
|
|
148
|
+
`the subpath "${subpath}" of the local package "${packageName}", but ` +
|
|
149
|
+
`the package's "exports" map has no resolvable entry for "${subpath}", ` +
|
|
150
|
+
`or none of the matched paths exist on disk. Check the "exports" field ` +
|
|
151
|
+
`in "${path.relative(root, path.join(plugin.path, 'package.json'))}" ` +
|
|
152
|
+
`and ensure the source file referenced by "${subpath}" exists.`);
|
|
153
|
+
}
|
|
154
|
+
throw new Error(`Unable to resolve local plugin "${moduleName}". The local package ` +
|
|
155
|
+
`"${packageName ?? moduleName}" does not declare a build target with ` +
|
|
156
|
+
`a "main" source path, and Node could not resolve it either.`);
|
|
157
|
+
}
|
|
158
|
+
function resolveSubpathFromExports(projectConfig, projectPath, subpath, root) {
|
|
159
|
+
const packageExports = projectConfig.metadata?.js?.packageExports;
|
|
160
|
+
if (!packageExports) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const pkg = {
|
|
164
|
+
name: projectConfig.metadata.js.packageName,
|
|
165
|
+
exports: packageExports,
|
|
166
|
+
};
|
|
167
|
+
try {
|
|
168
|
+
const matches = (0, resolve_exports_1.resolve)(pkg, subpath, {
|
|
169
|
+
conditions: (0, typescript_1.getRootTsConfigResolveExportsConditions)(root),
|
|
170
|
+
});
|
|
171
|
+
if (!matches || !matches.length) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
for (const match of matches) {
|
|
175
|
+
const candidate = path.join(projectPath, match);
|
|
176
|
+
if ((0, node_fs_1.existsSync)(candidate)) {
|
|
177
|
+
return candidate;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
logger_1.logger.verbose(`Failed to resolve subpath "${subpath}" of local plugin via package.json exports`, e);
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
81
186
|
function lookupLocalPlugin(importPath, projects, root = workspace_root_1.workspaceRoot) {
|
|
82
|
-
const
|
|
83
|
-
if (!
|
|
187
|
+
const match = findNxProjectForImportPath(importPath, projects, root);
|
|
188
|
+
if (!match) {
|
|
84
189
|
return null;
|
|
85
190
|
}
|
|
86
|
-
|
|
191
|
+
let resolvedFile;
|
|
192
|
+
if (match.tsPathFile) {
|
|
193
|
+
const candidate = path.join(root, match.tsPathFile);
|
|
194
|
+
if (path.extname(candidate) && (0, node_fs_1.existsSync)(candidate)) {
|
|
195
|
+
resolvedFile = candidate;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
path: path.join(root, match.projectConfig.root),
|
|
200
|
+
projectConfig: match.projectConfig,
|
|
201
|
+
resolvedFile,
|
|
202
|
+
};
|
|
87
203
|
}
|
|
88
204
|
let packageEntryPointsToProjectMap;
|
|
89
205
|
let wildcardEntryPointsToProjectMap;
|
|
@@ -101,7 +217,10 @@ function findNxProjectForImportPath(importPath, projects, root = workspace_root_
|
|
|
101
217
|
for (const tsConfigPath of possibleTsPaths) {
|
|
102
218
|
const nxProject = (0, find_project_for_path_1.findProjectForPath)(tsConfigPath, projectRootMappings);
|
|
103
219
|
if (nxProject) {
|
|
104
|
-
return
|
|
220
|
+
return {
|
|
221
|
+
projectConfig: projectNameMap.get(nxProject),
|
|
222
|
+
tsPathFile: tsConfigPath,
|
|
223
|
+
};
|
|
105
224
|
}
|
|
106
225
|
}
|
|
107
226
|
}
|
|
@@ -112,14 +231,14 @@ function findNxProjectForImportPath(importPath, projects, root = workspace_root_
|
|
|
112
231
|
} = (0, packages_1.getWorkspacePackagesMetadata)(projects));
|
|
113
232
|
}
|
|
114
233
|
if (packageEntryPointsToProjectMap[importPath]) {
|
|
115
|
-
return packageEntryPointsToProjectMap[importPath];
|
|
234
|
+
return { projectConfig: packageEntryPointsToProjectMap[importPath] };
|
|
116
235
|
}
|
|
117
236
|
const project = (0, packages_1.matchImportToWildcardEntryPointsToProjectMap)(wildcardEntryPointsToProjectMap, importPath);
|
|
118
237
|
if (project) {
|
|
119
|
-
return project;
|
|
238
|
+
return { projectConfig: project };
|
|
120
239
|
}
|
|
121
240
|
logger_1.logger.verbose('Unable to find local plugin', possibleTsPaths, projectRootMappings);
|
|
122
|
-
|
|
241
|
+
return null;
|
|
123
242
|
}
|
|
124
243
|
let tsconfigPaths;
|
|
125
244
|
function readTsConfigPaths(root = workspace_root_1.workspaceRoot) {
|
|
@@ -3,22 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createNodesFromFiles = createNodesFromFiles;
|
|
4
4
|
const error_types_1 = require("../error-types");
|
|
5
5
|
async function createNodesFromFiles(createNodes, configFiles, options, context) {
|
|
6
|
-
const
|
|
7
|
-
const errors = [];
|
|
8
|
-
await Promise.all(configFiles.map(async (file, idx) => {
|
|
6
|
+
const settled = await Promise.all(configFiles.map(async (file, idx) => {
|
|
9
7
|
try {
|
|
10
8
|
const value = await createNodes(file, options, {
|
|
11
9
|
...context,
|
|
12
10
|
configFiles,
|
|
13
11
|
}, idx);
|
|
14
|
-
|
|
15
|
-
results.push([file, value]);
|
|
16
|
-
}
|
|
12
|
+
return value ? { kind: 'value', file, value } : { kind: 'empty' };
|
|
17
13
|
}
|
|
18
14
|
catch (e) {
|
|
19
|
-
|
|
15
|
+
return { kind: 'error', file, error: e };
|
|
20
16
|
}
|
|
21
17
|
}));
|
|
18
|
+
const results = [];
|
|
19
|
+
const errors = [];
|
|
20
|
+
for (const entry of settled) {
|
|
21
|
+
if (entry.kind === 'value') {
|
|
22
|
+
results.push([entry.file, entry.value]);
|
|
23
|
+
}
|
|
24
|
+
else if (entry.kind === 'error') {
|
|
25
|
+
errors.push([entry.file, entry.error]);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
22
28
|
if (errors.length > 0) {
|
|
23
29
|
throw new error_types_1.AggregateCreateNodesError(errors, results);
|
|
24
30
|
}
|
|
@@ -61,11 +61,22 @@ class LegacyTaskHistoryLifeCycle {
|
|
|
61
61
|
}
|
|
62
62
|
printFlakyTasksMessage() {
|
|
63
63
|
if (this.flakyTasks?.length > 0) {
|
|
64
|
+
const MAX_VISIBLE_FLAKY = 5;
|
|
65
|
+
const visibleFlaky = this.flakyTasks.length > MAX_VISIBLE_FLAKY + 1
|
|
66
|
+
? this.flakyTasks.slice(0, MAX_VISIBLE_FLAKY)
|
|
67
|
+
: this.flakyTasks;
|
|
68
|
+
const hiddenCount = this.flakyTasks.length - visibleFlaky.length;
|
|
69
|
+
const flakyRows = visibleFlaky.map((t) => ` ${t}`);
|
|
70
|
+
if (hiddenCount > 0) {
|
|
71
|
+
flakyRows.push(` ${hiddenCount} more...`);
|
|
72
|
+
}
|
|
64
73
|
output_1.output.warn({
|
|
65
|
-
title: `Nx detected ${this.flakyTasks.length === 1
|
|
74
|
+
title: `Nx detected ${this.flakyTasks.length === 1
|
|
75
|
+
? 'a flaky task'
|
|
76
|
+
: `${this.flakyTasks.length} flaky tasks`}`,
|
|
66
77
|
bodyLines: [
|
|
67
78
|
,
|
|
68
|
-
...
|
|
79
|
+
...flakyRows,
|
|
69
80
|
...((0, nx_cloud_utils_1.isNxCloudUsed)((0, nx_json_1.readNxJson)())
|
|
70
81
|
? []
|
|
71
82
|
: [
|
|
@@ -79,14 +79,25 @@ class TaskHistoryLifeCycle {
|
|
|
79
79
|
}
|
|
80
80
|
printFlakyTasksMessage() {
|
|
81
81
|
if (this.flakyTasks?.length > 0) {
|
|
82
|
+
const MAX_VISIBLE_FLAKY = 5;
|
|
83
|
+
const visibleFlaky = this.flakyTasks.length > MAX_VISIBLE_FLAKY + 1
|
|
84
|
+
? this.flakyTasks.slice(0, MAX_VISIBLE_FLAKY)
|
|
85
|
+
: this.flakyTasks;
|
|
86
|
+
const hiddenCount = this.flakyTasks.length - visibleFlaky.length;
|
|
87
|
+
const flakyRows = visibleFlaky.map((hash) => {
|
|
88
|
+
const taskRun = this.taskRuns.get(hash);
|
|
89
|
+
return ` ${(0, serialize_target_1.serializeTarget)(taskRun.target.project, taskRun.target.target, taskRun.target.configuration)}`;
|
|
90
|
+
});
|
|
91
|
+
if (hiddenCount > 0) {
|
|
92
|
+
flakyRows.push(` ${hiddenCount} more...`);
|
|
93
|
+
}
|
|
82
94
|
output_1.output.warn({
|
|
83
|
-
title: `Nx detected ${this.flakyTasks.length === 1
|
|
95
|
+
title: `Nx detected ${this.flakyTasks.length === 1
|
|
96
|
+
? 'a flaky task'
|
|
97
|
+
: `${this.flakyTasks.length} flaky tasks`}`,
|
|
84
98
|
bodyLines: [
|
|
85
99
|
,
|
|
86
|
-
...
|
|
87
|
-
const taskRun = this.taskRuns.get(hash);
|
|
88
|
-
return ` ${(0, serialize_target_1.serializeTarget)(taskRun.target.project, taskRun.target.target, taskRun.target.configuration)}`;
|
|
89
|
-
}),
|
|
100
|
+
...flakyRows,
|
|
90
101
|
...((0, nx_cloud_utils_1.isNxCloudUsed)((0, nx_json_1.readNxJson)())
|
|
91
102
|
? []
|
|
92
103
|
: [
|
|
@@ -67,6 +67,12 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, taskGraph, args,
|
|
|
67
67
|
displayStoppedTasks.add(taskId);
|
|
68
68
|
inProgressTasks.delete(taskId);
|
|
69
69
|
}
|
|
70
|
+
else if (taskStatus === 2 /* NativeTaskStatus.Skipped */) {
|
|
71
|
+
// Skipped tasks don't get an endTasks() call; clear them here so the run
|
|
72
|
+
// summary doesn't treat them as still-in-progress and report Cancelled.
|
|
73
|
+
tasksToTaskStatus[taskId] = 'skipped';
|
|
74
|
+
inProgressTasks.delete(taskId);
|
|
75
|
+
}
|
|
70
76
|
};
|
|
71
77
|
lifeCycle.endTasks = (taskResults) => {
|
|
72
78
|
for (const { task, status, terminalOutput } of taskResults) {
|
|
@@ -132,10 +138,13 @@ function getTuiTerminalSummaryLifeCycle({ projectNames, tasks, taskGraph, args,
|
|
|
132
138
|
(0, task_history_life_cycle_1.getTasksHistoryLifeCycle)().printFlakyTasksMessage();
|
|
133
139
|
};
|
|
134
140
|
const printRunOneSummary = ({ failure, cancelled, }) => {
|
|
135
|
-
//
|
|
136
|
-
// above the summary, since run-one should print all task results.
|
|
141
|
+
// Print task outputs in completion order above the summary.
|
|
137
142
|
for (const taskId of taskIdsInTheOrderTheyStart) {
|
|
138
143
|
const taskStatus = tasksToTaskStatus[taskId];
|
|
144
|
+
// Skipped tasks never ran; don't print a misleading `> nx run` header.
|
|
145
|
+
if (taskStatus === 'skipped') {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
139
148
|
const terminalOutput = getTerminalOutput(taskId);
|
|
140
149
|
output_1.output.logCommandOutput(taskId, taskStatus, terminalOutput);
|
|
141
150
|
}
|
|
@@ -22,6 +22,7 @@ const project_graph_1 = require("../project-graph/project-graph");
|
|
|
22
22
|
const handle_errors_1 = require("../utils/handle-errors");
|
|
23
23
|
const is_ci_1 = require("../utils/is-ci");
|
|
24
24
|
const nx_cloud_utils_1 = require("../utils/nx-cloud-utils");
|
|
25
|
+
const logger_1 = require("../utils/logger");
|
|
25
26
|
const nx_key_1 = require("../utils/nx-key");
|
|
26
27
|
const output_1 = require("../utils/output");
|
|
27
28
|
const sync_generators_1 = require("../utils/sync-generators");
|
|
@@ -324,6 +325,10 @@ async function runCommand(projectsToRun, currentProjectGraph, { nxJson }, nxArgs
|
|
|
324
325
|
return status;
|
|
325
326
|
}
|
|
326
327
|
async function runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }, nxArgs, overrides, initiatingProject, extraTargetDependencies, extraOptions) {
|
|
328
|
+
// Kick off the license lookup in the background so it overlaps with task
|
|
329
|
+
// execution. The log itself is deferred to the print site below so it
|
|
330
|
+
// never lands in the middle of task output.
|
|
331
|
+
const nxKeyPromise = (0, nx_key_1.getNxKeyInformation)().catch(() => null);
|
|
327
332
|
const projectNames = projectsToRun.map((t) => t.name);
|
|
328
333
|
const projectNameSet = new Set(projectNames);
|
|
329
334
|
const { projectGraph, taskGraph } = await ensureWorkspaceIsInSyncAndGetGraphs(currentProjectGraph, nxJson, projectNames, nxArgs, overrides, extraTargetDependencies, extraOptions);
|
|
@@ -348,7 +353,9 @@ async function runCommandForTasks(projectsToRun, currentProjectGraph, { nxJson }
|
|
|
348
353
|
printSummary();
|
|
349
354
|
}
|
|
350
355
|
await printConfigureAiAgentsDisclaimer();
|
|
351
|
-
await
|
|
356
|
+
const nxKey = await nxKeyPromise;
|
|
357
|
+
if (nxKey)
|
|
358
|
+
logger_1.logger.log((0, nx_key_1.createNxKeyLicenseeInformation)(nxKey));
|
|
352
359
|
return {
|
|
353
360
|
taskResults,
|
|
354
361
|
completed: didCommandComplete(tasks, taskResults),
|
|
@@ -29,6 +29,14 @@ const task_env_1 = require("./task-env");
|
|
|
29
29
|
const tasks_schedule_1 = require("./tasks-schedule");
|
|
30
30
|
const utils_1 = require("./utils");
|
|
31
31
|
const shared_running_task_1 = require("./running-tasks/shared-running-task");
|
|
32
|
+
/**
|
|
33
|
+
* Resolve a batch executor's per-task result to a TaskStatus. Prefers an
|
|
34
|
+
* explicit `status` from the executor; falls back to the `success` boolean
|
|
35
|
+
* for executors that pre-date the `status` field.
|
|
36
|
+
*/
|
|
37
|
+
function resolveBatchTaskStatus(result) {
|
|
38
|
+
return result.status ?? (result.success ? 'success' : 'failure');
|
|
39
|
+
}
|
|
32
40
|
class TaskOrchestrator {
|
|
33
41
|
// endregion internal state
|
|
34
42
|
constructor(hasher, initiatingProject, initiatingTasks, projectGraph, taskGraph, nxJson, options, bail, daemon, outputStyle, taskGraphForHashing = taskGraph) {
|
|
@@ -498,11 +506,18 @@ class TaskOrchestrator {
|
|
|
498
506
|
// Heavy operations (caching, scheduling, complete) happen at batch-end in postRunSteps
|
|
499
507
|
batchProcess.onTaskResults((taskId, result) => {
|
|
500
508
|
const task = this.taskGraph.tasks[taskId];
|
|
501
|
-
const status = result
|
|
502
|
-
|
|
509
|
+
const status = resolveBatchTaskStatus(result);
|
|
510
|
+
// Append before print so printTaskTerminalOutput finds the PTY already
|
|
511
|
+
// populated and no-ops; reversing the order writes terminalOutput twice.
|
|
503
512
|
if (result.terminalOutput) {
|
|
504
513
|
this.options.lifeCycle.appendTaskOutput(taskId, result.terminalOutput, false);
|
|
505
514
|
}
|
|
515
|
+
// Skipped tasks didn't run, so they have no terminal output and don't
|
|
516
|
+
// need a per-task PTY — calling printTaskTerminalOutput would otherwise
|
|
517
|
+
// allocate one just to write a cursor-hide escape.
|
|
518
|
+
if (status !== 'skipped') {
|
|
519
|
+
this.options.lifeCycle.printTaskTerminalOutput(task, status, result.terminalOutput ?? '');
|
|
520
|
+
}
|
|
506
521
|
task.startTime = result.startTime;
|
|
507
522
|
task.endTime = result.endTime;
|
|
508
523
|
if (result.startTime && result.endTime) {
|
|
@@ -516,10 +531,11 @@ class TaskOrchestrator {
|
|
|
516
531
|
const task = this.taskGraph.tasks[taskId];
|
|
517
532
|
task.startTime = result.startTime;
|
|
518
533
|
task.endTime = result.endTime;
|
|
534
|
+
const status = resolveBatchTaskStatus(result);
|
|
519
535
|
return {
|
|
520
|
-
code:
|
|
536
|
+
code: status === 'success' ? 0 : 1,
|
|
521
537
|
task,
|
|
522
|
-
status
|
|
538
|
+
status,
|
|
523
539
|
terminalOutput: result.terminalOutput,
|
|
524
540
|
};
|
|
525
541
|
});
|
|
@@ -223,9 +223,9 @@ class TasksSchedule {
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
canBatchTaskBeScheduled(task, batchTaskGraph) {
|
|
226
|
-
// task self needs to
|
|
226
|
+
// task self needs to support parallelism (undefined defaults to parallel)
|
|
227
227
|
// all deps have either completed or belong to the same batch
|
|
228
|
-
return (task.parallelism
|
|
228
|
+
return (task.parallelism !== false &&
|
|
229
229
|
this.taskGraph.dependencies[task.id].every((id) => this.completedTasks.has(id) || !!batchTaskGraph?.tasks[id]));
|
|
230
230
|
}
|
|
231
231
|
canBeScheduled(taskId) {
|
|
@@ -248,7 +248,7 @@ class TasksSchedule {
|
|
|
248
248
|
}
|
|
249
249
|
else {
|
|
250
250
|
// if all running tasks support parallelism, can only schedule task with parallelism
|
|
251
|
-
return this.taskGraph.tasks[taskId].parallelism
|
|
251
|
+
return this.taskGraph.tasks[taskId].parallelism !== false;
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
getEstimatedTaskTimings() {
|
|
@@ -21,6 +21,18 @@ const messageOptions = {
|
|
|
21
21
|
],
|
|
22
22
|
footer: '\nFree for small teams. Remote caching and task distribution. 2-minute setup: https://nx.dev/nx-cloud',
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
code: 'cloud-self-healing-remote-cache',
|
|
26
|
+
message: `Would you like to enable AI-powered Self-Healing CI and Remote Caching?`,
|
|
27
|
+
initial: 0,
|
|
28
|
+
choices: [
|
|
29
|
+
{ value: 'yes', name: 'Yes' },
|
|
30
|
+
{ value: 'skip', name: 'Skip for now' },
|
|
31
|
+
{ value: 'never', name: pc.dim("No, don't ask again") },
|
|
32
|
+
],
|
|
33
|
+
footer: '\nLearn about it at https://nx.dev/nx-cloud',
|
|
34
|
+
hint: `\n(it's free and can be disabled any time)`,
|
|
35
|
+
},
|
|
24
36
|
],
|
|
25
37
|
setupViewLogs: [
|
|
26
38
|
{
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enables V8's on-disk bytecode cache for the current process.
|
|
3
|
+
*
|
|
4
|
+
* Calls `module.enableCompileCache()` with no arguments and lets Node pick
|
|
5
|
+
* the location, which means Node's standard env vars work transparently:
|
|
6
|
+
* - `NODE_COMPILE_CACHE=<dir>` — override the cache directory.
|
|
7
|
+
* - `NODE_DISABLE_COMPILE_CACHE=1` — disable entirely.
|
|
8
|
+
*
|
|
9
|
+
* The default location lives under the OS temp dir keyed by V8 version, so
|
|
10
|
+
* the cache is shared across workspaces and self-invalidates on Node
|
|
11
|
+
* upgrades — no nx-specific cleanup needed.
|
|
12
|
+
*
|
|
13
|
+
* Called at the entry point of every long-lived nx process (main CLI,
|
|
14
|
+
* daemon, plugin workers) via `enable-compile-cache.ts`, which side-effects
|
|
15
|
+
* this on import.
|
|
16
|
+
*
|
|
17
|
+
* Set `NX_COMPILE_CACHE=false` to opt out without disabling the cache for
|
|
18
|
+
* non-nx Node processes the way `NODE_DISABLE_COMPILE_CACHE` would.
|
|
19
|
+
*
|
|
20
|
+
* No-op on Node versions without the `module.enableCompileCache` API.
|
|
21
|
+
* Errors are swallowed — the compile cache is a pure performance
|
|
22
|
+
* optimization and must never break the CLI.
|
|
23
|
+
*/
|
|
24
|
+
export declare function enableCompileCache(...override: [unknown?]): boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enableCompileCache = enableCompileCache;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const nodeModule = tslib_1.__importStar(require("node:module"));
|
|
6
|
+
/**
|
|
7
|
+
* Enables V8's on-disk bytecode cache for the current process.
|
|
8
|
+
*
|
|
9
|
+
* Calls `module.enableCompileCache()` with no arguments and lets Node pick
|
|
10
|
+
* the location, which means Node's standard env vars work transparently:
|
|
11
|
+
* - `NODE_COMPILE_CACHE=<dir>` — override the cache directory.
|
|
12
|
+
* - `NODE_DISABLE_COMPILE_CACHE=1` — disable entirely.
|
|
13
|
+
*
|
|
14
|
+
* The default location lives under the OS temp dir keyed by V8 version, so
|
|
15
|
+
* the cache is shared across workspaces and self-invalidates on Node
|
|
16
|
+
* upgrades — no nx-specific cleanup needed.
|
|
17
|
+
*
|
|
18
|
+
* Called at the entry point of every long-lived nx process (main CLI,
|
|
19
|
+
* daemon, plugin workers) via `enable-compile-cache.ts`, which side-effects
|
|
20
|
+
* this on import.
|
|
21
|
+
*
|
|
22
|
+
* Set `NX_COMPILE_CACHE=false` to opt out without disabling the cache for
|
|
23
|
+
* non-nx Node processes the way `NODE_DISABLE_COMPILE_CACHE` would.
|
|
24
|
+
*
|
|
25
|
+
* No-op on Node versions without the `module.enableCompileCache` API.
|
|
26
|
+
* Errors are swallowed — the compile cache is a pure performance
|
|
27
|
+
* optimization and must never break the CLI.
|
|
28
|
+
*/
|
|
29
|
+
function enableCompileCache(
|
|
30
|
+
// Test seam: production callers omit this. `unknown` (rather than
|
|
31
|
+
// `EnableCompileCacheFn | undefined`) lets tests pass a non-function to
|
|
32
|
+
// simulate pre-22.8 Node where `module.enableCompileCache` is missing. We
|
|
33
|
+
// read `arguments.length` so callers can *explicitly* pass `undefined`.
|
|
34
|
+
...override) {
|
|
35
|
+
if (process.env.NX_COMPILE_CACHE === 'false')
|
|
36
|
+
return false;
|
|
37
|
+
const impl = override.length === 0
|
|
38
|
+
? nodeModule.enableCompileCache
|
|
39
|
+
: override[0];
|
|
40
|
+
if (typeof impl !== 'function')
|
|
41
|
+
return false;
|
|
42
|
+
try {
|
|
43
|
+
impl();
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// Side-effect-only module: import this as the *first* import in any nx
|
|
4
|
+
// process entry point so V8's compile cache is enabled before the rest of
|
|
5
|
+
// the import chain (which is what we actually want to cache) starts loading.
|
|
6
|
+
const compile_cache_1 = require("./compile-cache");
|
|
7
|
+
(0, compile_cache_1.enableCompileCache)();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if `@nx/js` is installed by attempting to resolve its `package.json`.
|
|
3
|
+
*
|
|
4
|
+
* Lives in its own module so unit tests can `jest.doMock` it without having to
|
|
5
|
+
* intercept Node's resolver at the global level. The previous in-`package-json`
|
|
6
|
+
* definition was unreachable to mocks because callers in the same module
|
|
7
|
+
* referenced it via the local lexical binding rather than `module.exports`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function hasNxJsPlugin(projectRoot: string, workspaceRoot: string): boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasNxJsPlugin = hasNxJsPlugin;
|
|
4
|
+
const installation_directory_1 = require("./installation-directory");
|
|
5
|
+
/**
|
|
6
|
+
* Checks if `@nx/js` is installed by attempting to resolve its `package.json`.
|
|
7
|
+
*
|
|
8
|
+
* Lives in its own module so unit tests can `jest.doMock` it without having to
|
|
9
|
+
* intercept Node's resolver at the global level. The previous in-`package-json`
|
|
10
|
+
* definition was unreachable to mocks because callers in the same module
|
|
11
|
+
* referenced it via the local lexical binding rather than `module.exports`.
|
|
12
|
+
*/
|
|
13
|
+
function hasNxJsPlugin(projectRoot, workspaceRoot) {
|
|
14
|
+
try {
|
|
15
|
+
// nx-ignore-next-line
|
|
16
|
+
require.resolve('@nx/js/package.json', {
|
|
17
|
+
paths: [projectRoot, ...(0, installation_directory_1.getNxRequirePaths)(workspaceRoot), __dirname],
|
|
18
|
+
});
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
export declare const NX_PREFIX: string;
|
|
2
2
|
export declare const NX_ERROR: string;
|
|
3
|
+
type LogDriver = Pick<Console, 'warn' | 'error' | 'info' | 'log' | 'debug'>;
|
|
4
|
+
export declare function createLogger(driver: LogDriver): {
|
|
5
|
+
warn: (...v: any[]) => void;
|
|
6
|
+
error: (s: any) => void;
|
|
7
|
+
info: (s: any) => void;
|
|
8
|
+
log: (...s: any[]) => void;
|
|
9
|
+
debug: (...s: any[]) => void;
|
|
10
|
+
fatal: (...s: any[]) => void;
|
|
11
|
+
verbose: (...s: any[]) => void;
|
|
12
|
+
};
|
|
3
13
|
export declare const logger: {
|
|
4
|
-
warn: (
|
|
14
|
+
warn: (...v: any[]) => void;
|
|
5
15
|
error: (s: any) => void;
|
|
6
16
|
info: (s: any) => void;
|
|
7
17
|
log: (...s: any[]) => void;
|
|
@@ -10,3 +20,4 @@ export declare const logger: {
|
|
|
10
20
|
verbose: (...s: any[]) => void;
|
|
11
21
|
};
|
|
12
22
|
export declare function stripIndent(str: string): string;
|
|
23
|
+
export {};
|