@nx/js 23.1.0-rc.0 → 23.1.0-rc.1
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/migrations/update-23-1-0/set-tsconfig-root-dir-for-ts6.d.ts +10 -6
- package/dist/src/migrations/update-23-1-0/set-tsconfig-root-dir-for-ts6.js +26 -27
- package/dist/src/migrations/update-23-1-0/set-tsconfig-root-dir-for-ts6.md +2 -2
- package/migrations.json +2 -2
- 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);
|
|
@@ -12,15 +12,19 @@ import { type Tree } from '@nx/devkit';
|
|
|
12
12
|
* compilation and emit layout are unchanged under 6.0. The value is computed by
|
|
13
13
|
* the compiler itself: a throwaway program built with `configFilePath` cleared
|
|
14
14
|
* takes the file-derived branch of `getCommonSourceDirectory`, so it matches
|
|
15
|
-
* `tsc` exactly, including project-reference redirects.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* `tsc` exactly, including project-reference redirects. The pin is written even
|
|
16
|
+
* when the inferred directory already equals the tsconfig directory: inference
|
|
17
|
+
* is per-program, and tools like ts-jest's `isolatedModules` compile single
|
|
18
|
+
* files against the same config, collapsing the common directory below the
|
|
19
|
+
* config dir and failing with TS5011. Only composite configs are exempt — their
|
|
20
|
+
* `rootDir` defaults to the tsconfig directory in both versions regardless of
|
|
21
|
+
* the file set.
|
|
18
22
|
*
|
|
19
23
|
* Each config is written on its own; nothing is written to a shared `extends`
|
|
20
24
|
* base, so a config never inherits a `rootDir` computed for a sibling. The one
|
|
21
25
|
* case that needs care is a config that both needs a `rootDir` and is itself an
|
|
22
|
-
* `extends` base:
|
|
23
|
-
*
|
|
24
|
-
*
|
|
26
|
+
* `extends` base: a composite child would inherit the new value and shift its
|
|
27
|
+
* emit layout, so each such child is pinned to its own directory to block it.
|
|
28
|
+
* Runs only on TypeScript 6 workspaces (gated by `requires`).
|
|
25
29
|
*/
|
|
26
30
|
export default function (tree: Tree): Promise<void>;
|
|
@@ -22,16 +22,20 @@ let tsModule;
|
|
|
22
22
|
* compilation and emit layout are unchanged under 6.0. The value is computed by
|
|
23
23
|
* the compiler itself: a throwaway program built with `configFilePath` cleared
|
|
24
24
|
* takes the file-derived branch of `getCommonSourceDirectory`, so it matches
|
|
25
|
-
* `tsc` exactly, including project-reference redirects.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* `tsc` exactly, including project-reference redirects. The pin is written even
|
|
26
|
+
* when the inferred directory already equals the tsconfig directory: inference
|
|
27
|
+
* is per-program, and tools like ts-jest's `isolatedModules` compile single
|
|
28
|
+
* files against the same config, collapsing the common directory below the
|
|
29
|
+
* config dir and failing with TS5011. Only composite configs are exempt — their
|
|
30
|
+
* `rootDir` defaults to the tsconfig directory in both versions regardless of
|
|
31
|
+
* the file set.
|
|
28
32
|
*
|
|
29
33
|
* Each config is written on its own; nothing is written to a shared `extends`
|
|
30
34
|
* base, so a config never inherits a `rootDir` computed for a sibling. The one
|
|
31
35
|
* case that needs care is a config that both needs a `rootDir` and is itself an
|
|
32
|
-
* `extends` base:
|
|
33
|
-
*
|
|
34
|
-
*
|
|
36
|
+
* `extends` base: a composite child would inherit the new value and shift its
|
|
37
|
+
* emit layout, so each such child is pinned to its own directory to block it.
|
|
38
|
+
* Runs only on TypeScript 6 workspaces (gated by `requires`).
|
|
35
39
|
*/
|
|
36
40
|
async function default_1(tree) {
|
|
37
41
|
tsModule ??= (0, ensure_typescript_1.ensureTypescript)();
|
|
@@ -60,8 +64,8 @@ async function default_1(tree) {
|
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
|
-
// Phase 3: shield
|
|
64
|
-
// base. Re-parse each from the tree (so Phase 2's writes are visible); when
|
|
67
|
+
// Phase 3: shield composite configs that now inherit a pinned `rootDir` from
|
|
68
|
+
// a base. Re-parse each from the tree (so Phase 2's writes are visible); when
|
|
65
69
|
// TypeScript reports an inherited `rootDir` where there was none, pin the
|
|
66
70
|
// config to its own directory so its emit layout does not shift. Repeat until
|
|
67
71
|
// stable, since shielding one config can turn it into a base for another. The
|
|
@@ -78,7 +82,7 @@ async function default_1(tree) {
|
|
|
78
82
|
while (added) {
|
|
79
83
|
added = false;
|
|
80
84
|
for (const c of candidates) {
|
|
81
|
-
if (c.analysis.kind !== '
|
|
85
|
+
if (c.analysis.kind !== 'composite' || shielded.has(c.relPath)) {
|
|
82
86
|
continue;
|
|
83
87
|
}
|
|
84
88
|
if (inheritsRootDir(ts, treeParseHost, readFile, c.relPath)) {
|
|
@@ -107,25 +111,27 @@ function analyze(ts, absPath, parseConfigHost) {
|
|
|
107
111
|
return { kind: 'inert' };
|
|
108
112
|
}
|
|
109
113
|
// Composite already defaults `rootDir` to the tsconfig's own directory in both
|
|
110
|
-
// 5.x and 6.0, so the 6.0 change leaves it alone.
|
|
111
|
-
//
|
|
112
|
-
//
|
|
114
|
+
// 5.x and 6.0, so the 6.0 change leaves it alone. Never pin a file-derived
|
|
115
|
+
// value; only shield it from a `rootDir` this migration pins on an `extends`
|
|
116
|
+
// base.
|
|
113
117
|
if (options.composite) {
|
|
114
|
-
return { kind: '
|
|
118
|
+
return { kind: 'composite' };
|
|
115
119
|
}
|
|
116
120
|
const commonDir = computeCommonSourceDirectory(ts, fileNames, options, projectReferences);
|
|
117
121
|
if (!commonDir) {
|
|
118
122
|
return { kind: 'inert' };
|
|
119
123
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
// Written even when `commonDir` is the tsconfig's own directory (the 6.0
|
|
125
|
+
// default): a tool compiling a subset of the config's files — ts-jest with
|
|
126
|
+
// `isolatedModules` builds a program per test file — re-infers the common
|
|
127
|
+
// directory from that subset alone, and without an explicit `rootDir` TS6
|
|
128
|
+
// fails with TS5011.
|
|
123
129
|
return { kind: 'write', dirAbs: toPosix(commonDir).replace(/\/+$/, '') };
|
|
124
130
|
}
|
|
125
|
-
// True when TypeScript resolves an inherited `rootDir` for the config.
|
|
126
|
-
// config sets none of its own
|
|
127
|
-
//
|
|
128
|
-
// layout.
|
|
131
|
+
// True when TypeScript resolves an inherited `rootDir` for the config. A
|
|
132
|
+
// composite config sets none of its own (or it would be `has-rootDir`), so any
|
|
133
|
+
// `rootDir` reported here comes from a base this migration just pinned, and
|
|
134
|
+
// inheriting it would shift the config's emit layout.
|
|
129
135
|
function inheritsRootDir(ts, parseHost, readFile, relPath) {
|
|
130
136
|
const config = ts.readConfigFile(relPath, readFile).config;
|
|
131
137
|
if (!config) {
|
|
@@ -203,13 +209,6 @@ function toRelativeRootDir(tsconfigDir, commonDir) {
|
|
|
203
209
|
const rel = node_path_1.posix.relative(toPosix(tsconfigDir), toPosix(commonDir));
|
|
204
210
|
return rel === '' ? '.' : rel;
|
|
205
211
|
}
|
|
206
|
-
function equalPaths(ts, a, b) {
|
|
207
|
-
const normalize = (p) => {
|
|
208
|
-
const stripped = toPosix(p).replace(/\/+$/, '');
|
|
209
|
-
return ts.sys.useCaseSensitiveFileNames ? stripped : stripped.toLowerCase();
|
|
210
|
-
};
|
|
211
|
-
return normalize(a) === normalize(b);
|
|
212
|
-
}
|
|
213
212
|
function toPosix(p) {
|
|
214
213
|
return p.split('\\').join('/');
|
|
215
214
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Before TypeScript 6, a tsconfig that did not set `rootDir` had it inferred as the common directory of the program's non-declaration input files. TypeScript 6 changed that default to the tsconfig's own directory. A program whose files resolve outside that directory (most commonly a spec or e2e tsconfig that imports another project's source through a `paths` alias) now hard-fails with `TS5011` or `TS6059` because a file falls outside the assumed root.
|
|
4
4
|
|
|
5
|
-
For every project `tsconfig*.json` that does not already set `rootDir` (directly or through `extends`), this migration pins `rootDir` to exactly the directory TypeScript 5 would have inferred, so both compilation and emit layout stay the same under TypeScript 6. The value is computed by the TypeScript compiler itself, so it matches `tsc` exactly, including project-reference redirects. Configs that cannot hit the error are skipped: ones without an output option (`outDir`, `outFile`, `sourceRoot`, `mapRoot`, or `declaration` + `declarationDir`), ones with no input files, composite projects (whose `rootDir`
|
|
5
|
+
For every project `tsconfig*.json` that does not already set `rootDir` (directly or through `extends`), this migration pins `rootDir` to exactly the directory TypeScript 5 would have inferred, so both compilation and emit layout stay the same under TypeScript 6. The value is computed by the TypeScript compiler itself, so it matches `tsc` exactly, including project-reference redirects. The pin is written even when the inferred directory already equals the tsconfig directory: inference is per-program, and a tool that compiles a subset of the config's files — ts-jest with `isolatedModules` builds a program per test file — re-infers a deeper common directory from that subset and fails with `TS5011`. Configs that cannot hit the error are skipped: ones without an output option (`outDir`, `outFile`, `sourceRoot`, `mapRoot`, or `declaration` + `declarationDir`), ones with no input files, and composite projects (whose `rootDir` defaults to the tsconfig directory in both versions, for any file subset). The migration only runs when the workspace is on TypeScript 6.
|
|
6
6
|
|
|
7
|
-
Each config is updated on its own; the migration never writes `rootDir` to a shared `extends` base, so a config never inherits a value computed for a sibling. The one case that needs care is a config that both needs a `rootDir` and is itself an `extends` base:
|
|
7
|
+
Each config is updated on its own; the migration never writes `rootDir` to a shared `extends` base, so a config never inherits a value computed for a sibling. The one case that needs care is a config that both needs a `rootDir` and is itself an `extends` base: a composite child would inherit the new value and emit to the wrong place, so each such child is pinned to its own directory to keep the default it had.
|
|
8
8
|
|
|
9
9
|
#### Sample Code Changes
|
|
10
10
|
|
package/migrations.json
CHANGED
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"documentation": "./dist/src/migrations/update-23-1-0/add-ignore-deprecations-for-ts6.md"
|
|
40
40
|
},
|
|
41
41
|
"23-1-0-set-tsconfig-root-dir-for-ts6": {
|
|
42
|
-
"version": "23.1.0-
|
|
43
|
-
"description": "Sets an explicit `rootDir` on project `tsconfig*.json` files that lack one, pinned to the source directory TypeScript 5 inferred implicitly, so programs
|
|
42
|
+
"version": "23.1.0-rc.1",
|
|
43
|
+
"description": "Sets an explicit `rootDir` on project `tsconfig*.json` files that lack one, pinned to the source directory TypeScript 5 inferred implicitly, so programs keep compiling and emitting the same layout under TypeScript 6, which otherwise hard-fails with TS5011 or TS6059 (for example a spec tsconfig importing another project's source through a `paths` alias). The pin is written even when the inferred directory already equals the tsconfig directory, because tools like ts-jest with `isolatedModules` compile a program per file and re-infer a deeper directory from that subset. The value is computed by the compiler, so emit layout is unchanged. Each config is written on its own; the migration never writes to a shared `extends` base, so a config never inherits a value computed for a sibling. Composite projects are not given an inferred `rootDir`, since it already defaults to the tsconfig directory in both versions; a composite that extends a base receiving a pinned `rootDir` is itself pinned to its own directory to preserve that default.",
|
|
44
44
|
"requires": {
|
|
45
45
|
"typescript": ">=6.0.0"
|
|
46
46
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/js",
|
|
3
|
-
"version": "23.1.0-rc.
|
|
3
|
+
"version": "23.1.0-rc.1",
|
|
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/
|
|
148
|
-
"@nx/
|
|
147
|
+
"@nx/devkit": "23.1.0-rc.1",
|
|
148
|
+
"@nx/workspace": "23.1.0-rc.1"
|
|
149
149
|
},
|
|
150
150
|
"devDependencies": {
|
|
151
|
-
"nx": "23.1.0-rc.
|
|
151
|
+
"nx": "23.1.0-rc.1"
|
|
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
|
-
}
|