@nx/js 23.0.1 → 23.0.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/executors/node/node.impl.js +17 -9
- package/dist/src/executors/prune-lockfile/prune-lockfile.d.ts +2 -0
- package/dist/src/executors/prune-lockfile/prune-lockfile.js +53 -1
- package/dist/src/executors/tsc/tsc.impl.js +6 -2
- package/dist/src/utils/buildable-libs-utils.js +9 -1
- package/dist/src/utils/prettier.js +17 -13
- package/dist/src/utils/swc/compile-swc.js +4 -1
- package/dist/src/utils/typescript/run-type-check.js +4 -0
- package/package.json +4 -4
- package/dist/src/executors/node/lib/kill-tree.d.ts +0 -1
- package/dist/src/executors/node/lib/kill-tree.js +0 -113
|
@@ -11,7 +11,7 @@ const crypto_1 = require("crypto");
|
|
|
11
11
|
const path = tslib_1.__importStar(require("path"));
|
|
12
12
|
const path_1 = require("path");
|
|
13
13
|
const buildable_libs_utils_1 = require("../../utils/buildable-libs-utils");
|
|
14
|
-
const
|
|
14
|
+
const native_1 = require("nx/src/native");
|
|
15
15
|
const line_aware_writer_1 = require("./lib/line-aware-writer");
|
|
16
16
|
const coalescing_debounce_1 = require("./lib/coalescing-debounce");
|
|
17
17
|
const fileutils_1 = require("nx/src/utils/fileutils");
|
|
@@ -67,23 +67,26 @@ async function* nodeExecutor(options, context) {
|
|
|
67
67
|
const previousTask = currentTask;
|
|
68
68
|
const task = tasks.shift();
|
|
69
69
|
if (previousTask && !previousTask.killed) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
previousTask.childProcess.disconnect();
|
|
73
|
-
}
|
|
74
|
-
previousTask.childProcess?.removeAllListeners();
|
|
70
|
+
// stop() marks the task killed, detaches listeners, and waits for the
|
|
71
|
+
// process tree to exit.
|
|
75
72
|
await previousTask.stop('SIGTERM');
|
|
76
73
|
await new Promise((resolve) => setImmediate(resolve));
|
|
77
74
|
}
|
|
78
75
|
currentTask = task;
|
|
79
76
|
globalLineAwareWriter.setActiveProcess(task.id);
|
|
80
77
|
await task.start();
|
|
78
|
+
// A file change may have queued another task while we waited for the
|
|
79
|
+
// previous process to stop. Its debounce trigger piggybacked on this
|
|
80
|
+
// in-flight run, so drain the queue now or it would sit unprocessed
|
|
81
|
+
// until the next change.
|
|
82
|
+
if (tasks.length > 0) {
|
|
83
|
+
await processQueue();
|
|
84
|
+
}
|
|
81
85
|
};
|
|
82
86
|
const debouncedProcessQueue = (0, coalescing_debounce_1.createCoalescingDebounce)(processQueue, options.debounce ?? 1_000);
|
|
83
87
|
const addToQueue = async (childProcess, buildResult) => {
|
|
84
88
|
for (const task of tasks) {
|
|
85
89
|
if (!task.killed) {
|
|
86
|
-
task.killed = true;
|
|
87
90
|
await task.stop('SIGTERM');
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -156,7 +159,7 @@ async function* nodeExecutor(options, context) {
|
|
|
156
159
|
},
|
|
157
160
|
stop: async (signal = 'SIGTERM') => {
|
|
158
161
|
task.killed = true;
|
|
159
|
-
if (task.childProcess) {
|
|
162
|
+
if (task.childProcess?.pid) {
|
|
160
163
|
if (task.childProcess.stdout) {
|
|
161
164
|
task.childProcess.stdout.pause();
|
|
162
165
|
}
|
|
@@ -167,7 +170,12 @@ async function* nodeExecutor(options, context) {
|
|
|
167
170
|
task.childProcess.disconnect();
|
|
168
171
|
}
|
|
169
172
|
task.childProcess.removeAllListeners();
|
|
170
|
-
|
|
173
|
+
// Wait for the process tree to fully exit so the port is released
|
|
174
|
+
// before a watch-mode restart boots the next server (EADDRINUSE).
|
|
175
|
+
// Windows cannot deliver graceful signals through this API, so
|
|
176
|
+
// skip the grace period and force-kill immediately (matching the
|
|
177
|
+
// previous taskkill /F behavior).
|
|
178
|
+
await (0, native_1.killProcessTreeGraceful)(task.childProcess.pid, signal, process.platform === 'win32' ? 0 : undefined);
|
|
171
179
|
}
|
|
172
180
|
if (task.id === globalLineAwareWriter.currentProcessId) {
|
|
173
181
|
globalLineAwareWriter.setActiveProcess(null);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { type PackageJson } from 'nx/src/utils/package-json';
|
|
2
3
|
import { type PruneLockfileOptions } from './schema';
|
|
3
4
|
export default function pruneLockfileExecutor(schema: PruneLockfileOptions, context: ExecutorContext): Promise<{
|
|
4
5
|
success: boolean;
|
|
5
6
|
}>;
|
|
7
|
+
export declare function resolveCatalogReferences(packageJson: PackageJson): PackageJson;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = pruneLockfileExecutor;
|
|
4
|
+
exports.resolveCatalogReferences = resolveCatalogReferences;
|
|
4
5
|
const devkit_1 = require("@nx/devkit");
|
|
6
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
5
7
|
const fs_1 = require("fs");
|
|
6
8
|
const path_1 = require("path");
|
|
7
9
|
const utils_1 = require("nx/src/tasks-runner/utils");
|
|
@@ -13,7 +15,8 @@ const strip_glob_to_base_dir_1 = require("../../utils/strip-glob-to-base-dir");
|
|
|
13
15
|
async function pruneLockfileExecutor(schema, context) {
|
|
14
16
|
devkit_1.logger.log('Pruning lockfile...');
|
|
15
17
|
const outputDirectory = getOutputDir(schema, context);
|
|
16
|
-
const packageJson = getPackageJson(schema, context);
|
|
18
|
+
const packageJson = resolveCatalogReferences(getPackageJson(schema, context));
|
|
19
|
+
mergeAllowScripts(packageJson);
|
|
17
20
|
const packageManager = (0, devkit_1.detectPackageManager)(devkit_1.workspaceRoot);
|
|
18
21
|
if (packageManager === 'bun') {
|
|
19
22
|
devkit_1.logger.warn('Bun lockfile generation is not supported. Only package.json will be generated. Run "bun install" in the output directory if needed.');
|
|
@@ -48,6 +51,55 @@ function createPrunedLockfile(packageJson, graph) {
|
|
|
48
51
|
lockFile,
|
|
49
52
|
};
|
|
50
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* npm reads the `allowScripts` install-script allowlist only from the install
|
|
56
|
+
* root, but `npm approve-scripts` writes it to the workspace root, so it never
|
|
57
|
+
* lives in the project package.json the prune output is built from. Carry the
|
|
58
|
+
* root allowlist over, with project-level entries preserved and winning on
|
|
59
|
+
* conflict. Mirrors the `pnpm.allowBuilds` handling in createPackageJson.
|
|
60
|
+
*/
|
|
61
|
+
function mergeAllowScripts(packageJson) {
|
|
62
|
+
const rootPackageJson = (0, devkit_1.readJsonFile)((0, path_1.join)(devkit_1.workspaceRoot, 'package.json'));
|
|
63
|
+
if (!rootPackageJson.allowScripts) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
packageJson.allowScripts = {
|
|
67
|
+
...rootPackageJson.allowScripts,
|
|
68
|
+
...packageJson.allowScripts,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function resolveCatalogReferences(packageJson) {
|
|
72
|
+
const manager = (0, internal_1.getCatalogManager)(devkit_1.workspaceRoot);
|
|
73
|
+
if (!manager) {
|
|
74
|
+
return packageJson;
|
|
75
|
+
}
|
|
76
|
+
const sections = [
|
|
77
|
+
'dependencies',
|
|
78
|
+
'optionalDependencies',
|
|
79
|
+
'devDependencies',
|
|
80
|
+
'peerDependencies',
|
|
81
|
+
];
|
|
82
|
+
const resolved = { ...packageJson };
|
|
83
|
+
for (const section of sections) {
|
|
84
|
+
const deps = packageJson[section];
|
|
85
|
+
if (!deps) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const resolvedDeps = { ...deps };
|
|
89
|
+
for (const [packageName, version] of Object.entries(deps)) {
|
|
90
|
+
if (!manager.isCatalogReference(version)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const resolvedVersion = manager.resolveCatalogReference(devkit_1.workspaceRoot, packageName, version);
|
|
94
|
+
if (!resolvedVersion) {
|
|
95
|
+
throw new Error(`Could not resolve catalog reference for package ${packageName}@${version}.`);
|
|
96
|
+
}
|
|
97
|
+
resolvedDeps[packageName] = resolvedVersion;
|
|
98
|
+
}
|
|
99
|
+
resolved[section] = resolvedDeps;
|
|
100
|
+
}
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
51
103
|
function getPackageJson(schema, context) {
|
|
52
104
|
const target = (0, devkit_1.parseTargetString)(schema.buildTarget, context);
|
|
53
105
|
const project = context.projectGraph.nodes[target.project].data;
|
|
@@ -55,8 +55,12 @@ function createTypeScriptCompilationOptions(normalizedOptions, context) {
|
|
|
55
55
|
outputPath: (0, devkit_1.joinPathFragments)(normalizedOptions.outputPath),
|
|
56
56
|
projectName: context.projectName,
|
|
57
57
|
projectRoot: normalizedOptions.projectRoot,
|
|
58
|
-
rootDir
|
|
59
|
-
|
|
58
|
+
// Keep the Windows drive letter on rootDir/tsConfig (only forward-slash them).
|
|
59
|
+
// joinPathFragments strips it via normalizePath, leaving them drive-less while
|
|
60
|
+
// the generated path mappings stay absolute drive-full, so alias-resolved files
|
|
61
|
+
// fail TypeScript's rootDir containment check (TS6059) on Windows.
|
|
62
|
+
rootDir: normalizedOptions.rootDir.replace(/\\/g, '/'),
|
|
63
|
+
tsConfig: normalizedOptions.tsConfig.replace(/\\/g, '/'),
|
|
60
64
|
watch: normalizedOptions.watch,
|
|
61
65
|
deleteOutputPath: normalizedOptions.clean,
|
|
62
66
|
getCustomTransformers: (0, lib_1.getCustomTrasformersFactory)(normalizedOptions.transformers),
|
|
@@ -282,7 +282,15 @@ function updatePaths(dependencies, paths) {
|
|
|
282
282
|
const { root } = dep.node.data;
|
|
283
283
|
// Update nested mappings to point to the dependency's output paths
|
|
284
284
|
mappedPaths = mappedPaths.concat(paths[path].flatMap((p) => dep.outputs.flatMap((output) => {
|
|
285
|
-
|
|
285
|
+
// Re-map the root prefix to the output. Match root only as a
|
|
286
|
+
// leading segment (after an optional `./`) so a root that
|
|
287
|
+
// also appears later in the value (e.g. output `dist/libs/base`
|
|
288
|
+
// for root `base`) isn't doubled.
|
|
289
|
+
const dotPrefix = p.startsWith('./') ? './' : '';
|
|
290
|
+
const value = dotPrefix ? p.slice(2) : p;
|
|
291
|
+
const basePath = value === root || value.startsWith(`${root}/`)
|
|
292
|
+
? `${dotPrefix}${output}${value.slice(root.length)}`
|
|
293
|
+
: p;
|
|
286
294
|
return [
|
|
287
295
|
// extension-less path to support compiled output
|
|
288
296
|
basePath.replace(new RegExp(`${(0, path_1.extname)(basePath)}$`, 'gi'), ''),
|
|
@@ -5,14 +5,23 @@ exports.generatePrettierSetup = generatePrettierSetup;
|
|
|
5
5
|
exports.resolvePrettierConfigPath = resolvePrettierConfigPath;
|
|
6
6
|
const devkit_1 = require("@nx/devkit");
|
|
7
7
|
const versions_1 = require("./versions");
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// Prettier v3 (ESM) exposes its API as named exports; v2 (CJS) exposes it under
|
|
9
|
+
// `.default` when loaded via `import()`. Return whichever carries the API, or
|
|
10
|
+
// null if prettier isn't installed.
|
|
11
|
+
async function importPrettier() {
|
|
10
12
|
try {
|
|
11
|
-
|
|
13
|
+
const imported = await import('prettier');
|
|
14
|
+
return (imported.resolveConfig ? imported : imported.default);
|
|
12
15
|
}
|
|
13
16
|
catch {
|
|
14
17
|
return null;
|
|
15
18
|
}
|
|
19
|
+
}
|
|
20
|
+
async function resolveUserExistingPrettierConfig() {
|
|
21
|
+
const prettier = await importPrettier();
|
|
22
|
+
if (!prettier) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
16
25
|
try {
|
|
17
26
|
const filepath = await prettier.resolveConfigFile();
|
|
18
27
|
if (!filepath) {
|
|
@@ -76,18 +85,13 @@ function generatePrettierSetup(tree, options) {
|
|
|
76
85
|
: (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { prettier: versions_1.prettierVersion });
|
|
77
86
|
}
|
|
78
87
|
async function resolvePrettierConfigPath(tree) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
prettier = await import('prettier');
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
88
|
+
const prettier = await importPrettier();
|
|
89
|
+
if (!prettier) {
|
|
84
90
|
return null;
|
|
85
91
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return filePath;
|
|
90
|
-
}
|
|
92
|
+
const configFilePath = await prettier.resolveConfigFile();
|
|
93
|
+
if (configFilePath) {
|
|
94
|
+
return configFilePath;
|
|
91
95
|
}
|
|
92
96
|
if (!tree) {
|
|
93
97
|
return null;
|
|
@@ -44,7 +44,10 @@ function getTypeCheckOptions(normalizedOptions) {
|
|
|
44
44
|
};
|
|
45
45
|
if (watch) {
|
|
46
46
|
typeCheckOptions.incremental = true;
|
|
47
|
-
|
|
47
|
+
// Scope the incremental .tsbuildinfo per project (in its own subdir, not
|
|
48
|
+
// alongside Nx's cache files) so concurrent watch type-checks don't collide
|
|
49
|
+
// on a single file.
|
|
50
|
+
typeCheckOptions.cacheDir = (0, node_path_1.join)(devkit_1.cacheDir, 'swc', projectRoot);
|
|
48
51
|
}
|
|
49
52
|
if (normalizedOptions.isTsSolutionSetup && normalizedOptions.skipTypeCheck) {
|
|
50
53
|
typeCheckOptions.ignoreDiagnostics = true;
|
|
@@ -43,6 +43,10 @@ async function runTypeCheck(options) {
|
|
|
43
43
|
options: {
|
|
44
44
|
...compilerOptions,
|
|
45
45
|
incremental: true,
|
|
46
|
+
// Set after the spread so it overrides any user-set tsBuildInfoFile.
|
|
47
|
+
// This is a dedicated type-check program with Nx-injected options that
|
|
48
|
+
// differ from the real build, so its build info must not share a file
|
|
49
|
+
// with the build, or it corrupts the build's incremental cache.
|
|
46
50
|
tsBuildInfoFile: path.join(cacheDir, '.tsbuildinfo'),
|
|
47
51
|
},
|
|
48
52
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/js",
|
|
3
|
-
"version": "23.0.
|
|
3
|
+
"version": "23.0.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"files": [
|
|
@@ -144,11 +144,11 @@
|
|
|
144
144
|
"source-map-support": "0.5.19",
|
|
145
145
|
"tinyglobby": "^0.2.12",
|
|
146
146
|
"tslib": "^2.3.0",
|
|
147
|
-
"@nx/devkit": "23.0.
|
|
148
|
-
"@nx/workspace": "23.0.
|
|
147
|
+
"@nx/devkit": "23.0.2",
|
|
148
|
+
"@nx/workspace": "23.0.2"
|
|
149
149
|
},
|
|
150
150
|
"devDependencies": {
|
|
151
|
-
"nx": "23.0.
|
|
151
|
+
"nx": "23.0.2"
|
|
152
152
|
},
|
|
153
153
|
"peerDependencies": {
|
|
154
154
|
"@swc/cli": ">=0.6.0 <0.9.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function killTree(pid: number, signal: NodeJS.Signals): Promise<void>;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.killTree = killTree;
|
|
4
|
-
// Adapted from https://raw.githubusercontent.com/pkrumins/node-tree-kill/deee138/index.js
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
async function killTree(pid, signal) {
|
|
7
|
-
const tree = {};
|
|
8
|
-
const pidsToProcess = {};
|
|
9
|
-
tree[pid] = [];
|
|
10
|
-
pidsToProcess[pid] = 1;
|
|
11
|
-
return new Promise((resolve, reject) => {
|
|
12
|
-
const callback = (error) => {
|
|
13
|
-
if (error) {
|
|
14
|
-
reject(error);
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
resolve();
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
switch (process.platform) {
|
|
21
|
-
case 'win32':
|
|
22
|
-
(0, child_process_1.exec)('taskkill /pid ' + pid + ' /T /F', {
|
|
23
|
-
windowsHide: true,
|
|
24
|
-
}, (error) => {
|
|
25
|
-
// Ignore Fatal errors (128) because it might be due to the process already being killed.
|
|
26
|
-
// On Linux/Mac we can check ESRCH (no such process), but on Windows we can't.
|
|
27
|
-
callback(error?.code !== 128 ? error : null);
|
|
28
|
-
});
|
|
29
|
-
break;
|
|
30
|
-
case 'darwin':
|
|
31
|
-
buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
|
|
32
|
-
return (0, child_process_1.spawn)('pgrep', ['-P', parentPid], {
|
|
33
|
-
windowsHide: true,
|
|
34
|
-
});
|
|
35
|
-
}, function () {
|
|
36
|
-
killAll(tree, signal, callback);
|
|
37
|
-
});
|
|
38
|
-
break;
|
|
39
|
-
default: // Linux
|
|
40
|
-
buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
|
|
41
|
-
return (0, child_process_1.spawn)('ps', ['-o', 'pid', '--no-headers', '--ppid', parentPid], {
|
|
42
|
-
windowsHide: true,
|
|
43
|
-
});
|
|
44
|
-
}, function () {
|
|
45
|
-
killAll(tree, signal, callback);
|
|
46
|
-
});
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
function killAll(tree, signal, callback) {
|
|
52
|
-
const killed = {};
|
|
53
|
-
try {
|
|
54
|
-
Object.keys(tree).forEach(function (pid) {
|
|
55
|
-
tree[pid].forEach(function (pidpid) {
|
|
56
|
-
if (!killed[pidpid]) {
|
|
57
|
-
killPid(pidpid, signal);
|
|
58
|
-
killed[pidpid] = 1;
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
if (!killed[pid]) {
|
|
62
|
-
killPid(pid, signal);
|
|
63
|
-
killed[pid] = 1;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
if (callback) {
|
|
69
|
-
return callback(err);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
throw err;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (callback) {
|
|
76
|
-
return callback();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function killPid(pid, signal) {
|
|
80
|
-
try {
|
|
81
|
-
process.kill(parseInt(pid, 10), signal);
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
if (err.code !== 'ESRCH')
|
|
85
|
-
throw err;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function buildProcessTree(parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) {
|
|
89
|
-
const ps = spawnChildProcessesList(parentPid);
|
|
90
|
-
let allData = '';
|
|
91
|
-
ps.stdout.on('data', (data) => {
|
|
92
|
-
data = data.toString('ascii');
|
|
93
|
-
allData += data;
|
|
94
|
-
});
|
|
95
|
-
const onClose = function (code) {
|
|
96
|
-
delete pidsToProcess[parentPid];
|
|
97
|
-
if (code != 0) {
|
|
98
|
-
// no more parent processes
|
|
99
|
-
if (Object.keys(pidsToProcess).length == 0) {
|
|
100
|
-
cb();
|
|
101
|
-
}
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
allData.match(/\d+/g).forEach((_pid) => {
|
|
105
|
-
const pid = parseInt(_pid, 10);
|
|
106
|
-
tree[parentPid].push(pid);
|
|
107
|
-
tree[pid] = [];
|
|
108
|
-
pidsToProcess[pid] = 1;
|
|
109
|
-
buildProcessTree(pid, tree, pidsToProcess, spawnChildProcessesList, cb);
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
ps.on('close', onClose);
|
|
113
|
-
}
|