@simplysm/sd-cli 13.0.68 → 13.0.70
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/README.md +10 -957
- package/dist/builders/BaseBuilder.d.ts +23 -23
- package/dist/builders/BaseBuilder.d.ts.map +1 -1
- package/dist/builders/BaseBuilder.js +15 -15
- package/dist/builders/DtsBuilder.d.ts +4 -4
- package/dist/builders/DtsBuilder.js +1 -1
- package/dist/builders/LibraryBuilder.d.ts +3 -3
- package/dist/builders/types.d.ts +10 -10
- package/dist/capacitor/capacitor.d.ts +36 -36
- package/dist/capacitor/capacitor.js +63 -63
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/add-client.d.ts +8 -8
- package/dist/commands/add-client.js +15 -15
- package/dist/commands/add-client.js.map +1 -1
- package/dist/commands/add-server.d.ts +9 -9
- package/dist/commands/add-server.js +13 -13
- package/dist/commands/add-server.js.map +1 -1
- package/dist/commands/build.d.ts +9 -9
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +9 -9
- package/dist/commands/device.d.ts +9 -9
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +17 -17
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/init.d.ts +6 -6
- package/dist/commands/init.js +12 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +23 -23
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +25 -25
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts +13 -13
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +61 -61
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.d.ts +3 -3
- package/dist/commands/replace-deps.d.ts.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/replace-deps.js.map +1 -1
- package/dist/commands/typecheck.d.ts +20 -20
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +20 -20
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts +7 -7
- package/dist/electron/electron.d.ts +27 -27
- package/dist/electron/electron.js +32 -32
- package/dist/electron/electron.js.map +1 -1
- package/dist/infra/ResultCollector.d.ts +9 -9
- package/dist/infra/ResultCollector.js +5 -5
- package/dist/infra/SignalHandler.d.ts +7 -7
- package/dist/infra/SignalHandler.js +4 -4
- package/dist/infra/WorkerManager.d.ts +14 -14
- package/dist/infra/WorkerManager.js +11 -11
- package/dist/orchestrators/BuildOrchestrator.d.ts +19 -19
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +26 -26
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.d.ts +25 -25
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +30 -30
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.d.ts +13 -13
- package/dist/orchestrators/WatchOrchestrator.js +17 -17
- package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +2 -2
- package/dist/sd-cli-entry.js +38 -38
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.d.ts +2 -2
- package/dist/sd-cli.js +1 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +84 -84
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/build-env.d.ts +1 -1
- package/dist/utils/config-editor.d.ts +5 -5
- package/dist/utils/config-editor.js +2 -2
- package/dist/utils/config-editor.js.map +1 -1
- package/dist/utils/copy-public.d.ts +9 -9
- package/dist/utils/copy-src.d.ts +9 -9
- package/dist/utils/esbuild-config.d.ts +30 -30
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/output-utils.d.ts +6 -6
- package/dist/utils/package-utils.d.ts +6 -6
- package/dist/utils/package-utils.js +1 -1
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/rebuild-manager.js +3 -3
- package/dist/utils/rebuild-manager.js.map +1 -1
- package/dist/utils/replace-deps.d.ts +25 -25
- package/dist/utils/replace-deps.js +3 -3
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts +3 -3
- package/dist/utils/sd-config.js +3 -3
- package/dist/utils/sd-config.js.map +1 -1
- package/dist/utils/tailwind-config-deps.d.ts +3 -3
- package/dist/utils/template.d.ts +8 -8
- package/dist/utils/tsconfig.d.ts +16 -16
- package/dist/utils/tsconfig.js +2 -2
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts +8 -8
- package/dist/utils/vite-config.d.ts +8 -8
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +3 -3
- package/dist/utils/worker-events.d.ts +12 -12
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-utils.d.ts +3 -3
- package/dist/utils/worker-utils.js +2 -2
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts +14 -14
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +1 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/dts.worker.d.ts +13 -13
- package/dist/workers/dts.worker.d.ts.map +1 -1
- package/dist/workers/dts.worker.js +3 -3
- package/dist/workers/dts.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts +12 -12
- package/dist/workers/library.worker.js +1 -1
- package/dist/workers/library.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/server-runtime.worker.d.ts +6 -6
- package/dist/workers/server-runtime.worker.js +6 -6
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts +20 -20
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +6 -6
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +8 -7
- package/src/builders/BaseBuilder.ts +33 -33
- package/src/builders/DtsBuilder.ts +5 -5
- package/src/builders/LibraryBuilder.ts +9 -9
- package/src/builders/types.ts +10 -10
- package/src/capacitor/capacitor.ts +119 -119
- package/src/commands/add-client.ts +31 -31
- package/src/commands/add-server.ts +34 -34
- package/src/commands/build.ts +9 -9
- package/src/commands/check.ts +5 -5
- package/src/commands/dev.ts +9 -9
- package/src/commands/device.ts +30 -30
- package/src/commands/init.ts +25 -25
- package/src/commands/lint.ts +64 -64
- package/src/commands/publish.ts +139 -139
- package/src/commands/replace-deps.ts +4 -4
- package/src/commands/typecheck.ts +74 -74
- package/src/commands/watch.ts +7 -7
- package/src/electron/electron.ts +51 -51
- package/src/infra/ResultCollector.ts +9 -9
- package/src/infra/SignalHandler.ts +7 -7
- package/src/infra/WorkerManager.ts +14 -14
- package/src/orchestrators/BuildOrchestrator.ts +76 -76
- package/src/orchestrators/DevOrchestrator.ts +88 -88
- package/src/orchestrators/WatchOrchestrator.ts +39 -39
- package/src/sd-cli-entry.ts +43 -43
- package/src/sd-cli.ts +15 -15
- package/src/sd-config.types.ts +85 -85
- package/src/utils/build-env.ts +1 -1
- package/src/utils/config-editor.ts +19 -19
- package/src/utils/copy-public.ts +17 -17
- package/src/utils/copy-src.ts +11 -11
- package/src/utils/esbuild-config.ts +33 -33
- package/src/utils/output-utils.ts +11 -11
- package/src/utils/package-utils.ts +12 -12
- package/src/utils/rebuild-manager.ts +3 -3
- package/src/utils/replace-deps.ts +361 -361
- package/src/utils/sd-config.ts +44 -44
- package/src/utils/tailwind-config-deps.ts +98 -98
- package/src/utils/template.ts +56 -56
- package/src/utils/tsconfig.ts +127 -127
- package/src/utils/typecheck-serialization.ts +86 -86
- package/src/utils/vite-config.ts +341 -341
- package/src/utils/worker-events.ts +16 -16
- package/src/utils/worker-utils.ts +45 -45
- package/src/workers/client.worker.ts +34 -34
- package/src/workers/dts.worker.ts +467 -467
- package/src/workers/library.worker.ts +314 -314
- package/src/workers/lint.worker.ts +16 -16
- package/src/workers/server-runtime.worker.ts +157 -157
- package/src/workers/server.worker.ts +572 -572
- package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
- package/templates/add-server/__SERVER__/package.json.hbs +2 -2
- package/templates/init/package.json.hbs +3 -3
- package/tests/config-editor.spec.ts +160 -0
- package/tests/copy-src.spec.ts +50 -0
- package/tests/get-compiler-options-for-package.spec.ts +139 -0
- package/tests/get-package-source-files.spec.ts +181 -0
- package/tests/get-types-from-package-json.spec.ts +107 -0
- package/tests/infra/ResultCollector.spec.ts +39 -0
- package/tests/infra/SignalHandler.spec.ts +38 -0
- package/tests/infra/WorkerManager.spec.ts +97 -0
- package/tests/load-ignore-patterns.spec.ts +188 -0
- package/tests/load-sd-config.spec.ts +137 -0
- package/tests/package-utils.spec.ts +188 -0
- package/tests/parse-root-tsconfig.spec.ts +89 -0
- package/tests/replace-deps.spec.ts +308 -0
- package/tests/run-lint.spec.ts +415 -0
- package/tests/run-typecheck.spec.ts +653 -0
- package/tests/run-watch.spec.ts +75 -0
- package/tests/sd-cli.spec.ts +330 -0
- package/tests/tailwind-config-deps.spec.ts +30 -0
- package/tests/template.spec.ts +70 -0
- package/tests/utils/rebuild-manager.spec.ts +43 -0
- package/tests/write-changed-output-files.spec.ts +97 -0
|
@@ -1,467 +1,467 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
import { createWorker, pathIsChildPath, pathNorm } from "@simplysm/core-node";
|
|
4
|
-
import { errorMessage } from "@simplysm/core-common";
|
|
5
|
-
import { consola } from "consola";
|
|
6
|
-
import {
|
|
7
|
-
getCompilerOptionsForPackage,
|
|
8
|
-
getPackageFiles,
|
|
9
|
-
getPackageSourceFiles,
|
|
10
|
-
parseRootTsconfig,
|
|
11
|
-
type TypecheckEnv,
|
|
12
|
-
} from "../utils/tsconfig";
|
|
13
|
-
import { serializeDiagnostic, type SerializedDiagnostic } from "../utils/typecheck-serialization";
|
|
14
|
-
import { createOnceGuard } from "../utils/worker-utils";
|
|
15
|
-
|
|
16
|
-
//#region Types
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* DTS watch
|
|
20
|
-
*/
|
|
21
|
-
export interface DtsWatchInfo {
|
|
22
|
-
name: string;
|
|
23
|
-
cwd: string;
|
|
24
|
-
pkgDir: string;
|
|
25
|
-
env: TypecheckEnv;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* DTS
|
|
30
|
-
*/
|
|
31
|
-
export interface DtsBuildInfo {
|
|
32
|
-
name: string;
|
|
33
|
-
cwd: string;
|
|
34
|
-
/**
|
|
35
|
-
pkgDir?: string;
|
|
36
|
-
/**
|
|
37
|
-
env?: TypecheckEnv;
|
|
38
|
-
/** true
|
|
39
|
-
emit?: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* DTS
|
|
44
|
-
*/
|
|
45
|
-
export interface DtsBuildResult {
|
|
46
|
-
success: boolean;
|
|
47
|
-
errors?: string[];
|
|
48
|
-
diagnostics: SerializedDiagnostic[];
|
|
49
|
-
errorCount: number;
|
|
50
|
-
warningCount: number;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
55
|
-
*/
|
|
56
|
-
export interface DtsBuildEvent {
|
|
57
|
-
success: boolean;
|
|
58
|
-
errors?: string[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
*
|
|
63
|
-
*/
|
|
64
|
-
export interface DtsErrorEvent {
|
|
65
|
-
message: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Worker
|
|
70
|
-
*/
|
|
71
|
-
export interface DtsWorkerEvents extends Record<string, unknown> {
|
|
72
|
-
buildStart: Record<string, never>;
|
|
73
|
-
build: DtsBuildEvent;
|
|
74
|
-
error: DtsErrorEvent;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
//#endregion
|
|
78
|
-
|
|
79
|
-
//#region
|
|
80
|
-
|
|
81
|
-
const logger = consola.withTag("sd:cli:dts:worker");
|
|
82
|
-
|
|
83
|
-
/** tsc watch program (
|
|
84
|
-
let tscWatchProgram:
|
|
85
|
-
| ts.WatchOfFilesAndCompilerOptions<ts.EmitAndSemanticDiagnosticsBuilderProgram>
|
|
86
|
-
| undefined;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
*
|
|
90
|
-
*/
|
|
91
|
-
function cleanup(): void {
|
|
92
|
-
if (tscWatchProgram != null) {
|
|
93
|
-
tscWatchProgram.close();
|
|
94
|
-
tscWatchProgram = undefined;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
process.on("SIGTERM", () => {
|
|
99
|
-
try {
|
|
100
|
-
cleanup();
|
|
101
|
-
} catch (err) {
|
|
102
|
-
logger.error("
|
|
103
|
-
}
|
|
104
|
-
process.exit(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
process.on("SIGINT", () => {
|
|
108
|
-
try {
|
|
109
|
-
cleanup();
|
|
110
|
-
} catch (err) {
|
|
111
|
-
logger.error("
|
|
112
|
-
}
|
|
113
|
-
process.exit(0);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
//#endregion
|
|
117
|
-
|
|
118
|
-
//#region DTS
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* .d.ts.map
|
|
122
|
-
*/
|
|
123
|
-
function adjustDtsMapSources(content: string, originalDir: string, newDir: string): string {
|
|
124
|
-
if (originalDir === newDir) return content;
|
|
125
|
-
try {
|
|
126
|
-
const map = JSON.parse(content) as { sources?: string[] };
|
|
127
|
-
if (Array.isArray(map.sources)) {
|
|
128
|
-
map.sources = map.sources.map((source) => {
|
|
129
|
-
const absoluteSource = path.resolve(originalDir, source);
|
|
130
|
-
return path.relative(newDir, absoluteSource);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
return JSON.stringify(map);
|
|
134
|
-
} catch {
|
|
135
|
-
return content;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* TypeScript
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* @returns (fileName, content) => [newPath, newContent] | null (null
|
|
148
|
-
*/
|
|
149
|
-
function createDtsPathRewriter(
|
|
150
|
-
pkgDir: string,
|
|
151
|
-
): (fileName: string, content: string) => [string, string] | null {
|
|
152
|
-
const pkgName = path.basename(pkgDir);
|
|
153
|
-
const distDir = pathNorm(path.join(pkgDir, "dist"));
|
|
154
|
-
const distPrefix = distDir + path.sep;
|
|
155
|
-
//
|
|
156
|
-
const ownNestedPrefix = pathNorm(path.join(distDir, pkgName, "src")) + path.sep;
|
|
157
|
-
|
|
158
|
-
return (fileName, content) => {
|
|
159
|
-
fileName = pathNorm(fileName);
|
|
160
|
-
|
|
161
|
-
if (!fileName.startsWith(distPrefix)) return null;
|
|
162
|
-
|
|
163
|
-
if (fileName.startsWith(ownNestedPrefix)) {
|
|
164
|
-
//
|
|
165
|
-
const flatPath = path.join(distDir, fileName.slice(ownNestedPrefix.length));
|
|
166
|
-
if (fileName.endsWith(".d.ts.map")) {
|
|
167
|
-
content = adjustDtsMapSources(content, path.dirname(fileName), path.dirname(flatPath));
|
|
168
|
-
}
|
|
169
|
-
return [flatPath, content];
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
//
|
|
173
|
-
const relFromDist = fileName.slice(distPrefix.length);
|
|
174
|
-
const segments = relFromDist.split(path.sep);
|
|
175
|
-
if (segments.length >= 3 && segments[1] === "src") {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
//
|
|
180
|
-
return [fileName, content];
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
//#endregion
|
|
185
|
-
|
|
186
|
-
//#region build (
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* DTS
|
|
190
|
-
*/
|
|
191
|
-
async function build(info: DtsBuildInfo): Promise<DtsBuildResult> {
|
|
192
|
-
try {
|
|
193
|
-
const parsedConfig = parseRootTsconfig(info.cwd);
|
|
194
|
-
|
|
195
|
-
let rootFiles: string[];
|
|
196
|
-
let baseOptions: ts.CompilerOptions;
|
|
197
|
-
let diagnosticFilter: (d: ts.Diagnostic) => boolean;
|
|
198
|
-
let tsBuildInfoFile: string;
|
|
199
|
-
|
|
200
|
-
if (info.pkgDir != null && info.env != null) {
|
|
201
|
-
//
|
|
202
|
-
baseOptions = await getCompilerOptionsForPackage(parsedConfig.options, info.env, info.pkgDir);
|
|
203
|
-
|
|
204
|
-
const shouldEmit = info.emit !== false;
|
|
205
|
-
if (shouldEmit) {
|
|
206
|
-
//
|
|
207
|
-
rootFiles = getPackageSourceFiles(info.pkgDir, parsedConfig);
|
|
208
|
-
const pkgSrcDir = path.join(info.pkgDir, "src");
|
|
209
|
-
diagnosticFilter = (d) => d.file == null || pathIsChildPath(d.file.fileName, pkgSrcDir);
|
|
210
|
-
} else {
|
|
211
|
-
//
|
|
212
|
-
rootFiles = getPackageFiles(info.pkgDir, parsedConfig);
|
|
213
|
-
const pkgDir = info.pkgDir;
|
|
214
|
-
diagnosticFilter = (d) => d.file == null || pathIsChildPath(d.file.fileName, pkgDir);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
tsBuildInfoFile = path.join(
|
|
218
|
-
info.pkgDir,
|
|
219
|
-
".cache",
|
|
220
|
-
shouldEmit ? "dts.tsbuildinfo" : `typecheck-${info.env}.tsbuildinfo`,
|
|
221
|
-
);
|
|
222
|
-
} else {
|
|
223
|
-
//
|
|
224
|
-
const packagesDir = path.join(info.cwd, "packages");
|
|
225
|
-
const isNonPackageFile = (fileName: string): boolean => {
|
|
226
|
-
if (!pathIsChildPath(fileName, packagesDir)) return true;
|
|
227
|
-
//
|
|
228
|
-
const relative = path.relative(packagesDir, fileName);
|
|
229
|
-
return relative.split(path.sep).length === 2;
|
|
230
|
-
};
|
|
231
|
-
rootFiles = parsedConfig.fileNames.filter(isNonPackageFile);
|
|
232
|
-
baseOptions = parsedConfig.options;
|
|
233
|
-
diagnosticFilter = (d) => d.file == null || isNonPackageFile(d.file.fileName);
|
|
234
|
-
tsBuildInfoFile = path.join(info.cwd, ".cache", "typecheck-root.tsbuildinfo");
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// emit
|
|
238
|
-
const shouldEmit = info.emit !== false;
|
|
239
|
-
|
|
240
|
-
const options: ts.CompilerOptions = {
|
|
241
|
-
...baseOptions,
|
|
242
|
-
sourceMap: false,
|
|
243
|
-
incremental: true,
|
|
244
|
-
tsBuildInfoFile,
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
//
|
|
248
|
-
if (shouldEmit && info.pkgDir != null) {
|
|
249
|
-
// dts
|
|
250
|
-
options.noEmit = false;
|
|
251
|
-
options.emitDeclarationOnly = true;
|
|
252
|
-
options.declaration = true;
|
|
253
|
-
options.declarationMap = true;
|
|
254
|
-
options.outDir = path.join(info.pkgDir, "dist");
|
|
255
|
-
options.declarationDir = path.join(info.pkgDir, "dist");
|
|
256
|
-
} else {
|
|
257
|
-
//
|
|
258
|
-
options.noEmit = true;
|
|
259
|
-
options.emitDeclarationOnly = false;
|
|
260
|
-
options.declaration = false;
|
|
261
|
-
options.declarationMap = false;
|
|
262
|
-
//
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// incremental program
|
|
266
|
-
const host = ts.createIncrementalCompilerHost(options);
|
|
267
|
-
|
|
268
|
-
//
|
|
269
|
-
if (shouldEmit && info.pkgDir != null) {
|
|
270
|
-
const rewritePath = createDtsPathRewriter(info.pkgDir);
|
|
271
|
-
const originalWriteFile = host.writeFile;
|
|
272
|
-
host.writeFile = (fileName, content, writeByteOrderMark, onError, sourceFiles, data) => {
|
|
273
|
-
const result = rewritePath(fileName, content);
|
|
274
|
-
if (result != null) {
|
|
275
|
-
originalWriteFile(result[0], result[1], writeByteOrderMark, onError, sourceFiles, data);
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const program = ts.createIncrementalProgram({
|
|
281
|
-
rootNames: rootFiles,
|
|
282
|
-
options,
|
|
283
|
-
host,
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
//
|
|
287
|
-
const emitResult = program.emit();
|
|
288
|
-
|
|
289
|
-
// diagnostics
|
|
290
|
-
const allDiagnostics = [
|
|
291
|
-
...program.getConfigFileParsingDiagnostics(),
|
|
292
|
-
...program.getSyntacticDiagnostics(),
|
|
293
|
-
...program.getOptionsDiagnostics(),
|
|
294
|
-
...program.getGlobalDiagnostics(),
|
|
295
|
-
...program.getSemanticDiagnostics(),
|
|
296
|
-
...emitResult.diagnostics,
|
|
297
|
-
];
|
|
298
|
-
|
|
299
|
-
//
|
|
300
|
-
const filteredDiagnostics = allDiagnostics.filter(diagnosticFilter);
|
|
301
|
-
|
|
302
|
-
const serializedDiagnostics = filteredDiagnostics.map(serializeDiagnostic);
|
|
303
|
-
const errorCount = filteredDiagnostics.filter(
|
|
304
|
-
(d) => d.category === ts.DiagnosticCategory.Error,
|
|
305
|
-
).length;
|
|
306
|
-
const warningCount = filteredDiagnostics.filter(
|
|
307
|
-
(d) => d.category === ts.DiagnosticCategory.Warning,
|
|
308
|
-
).length;
|
|
309
|
-
|
|
310
|
-
//
|
|
311
|
-
const errors = filteredDiagnostics
|
|
312
|
-
.filter((d) => d.category === ts.DiagnosticCategory.Error)
|
|
313
|
-
.map((d) => {
|
|
314
|
-
const message = ts.flattenDiagnosticMessageText(d.messageText, "\n");
|
|
315
|
-
if (d.file != null && d.start != null) {
|
|
316
|
-
const { line, character } = d.file.getLineAndCharacterOfPosition(d.start);
|
|
317
|
-
return `${d.file.fileName}:${line + 1}:${character + 1}: TS${d.code}: ${message}`;
|
|
318
|
-
}
|
|
319
|
-
return `TS${d.code}: ${message}`;
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
return {
|
|
323
|
-
success: errorCount === 0,
|
|
324
|
-
errors: errors.length > 0 ? errors : undefined,
|
|
325
|
-
diagnostics: serializedDiagnostics,
|
|
326
|
-
errorCount,
|
|
327
|
-
warningCount,
|
|
328
|
-
};
|
|
329
|
-
} catch (err) {
|
|
330
|
-
return {
|
|
331
|
-
success: false,
|
|
332
|
-
errors: [errorMessage(err)],
|
|
333
|
-
diagnostics: [],
|
|
334
|
-
errorCount: 1,
|
|
335
|
-
warningCount: 0,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
//#endregion
|
|
341
|
-
|
|
342
|
-
//#region startWatch (watch
|
|
343
|
-
|
|
344
|
-
const guardStartWatch = createOnceGuard("startWatch");
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* DTS watch
|
|
348
|
-
* @remarks
|
|
349
|
-
* @throws
|
|
350
|
-
*/
|
|
351
|
-
async function startWatch(info: DtsWatchInfo): Promise<void> {
|
|
352
|
-
guardStartWatch();
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
const parsedConfig = parseRootTsconfig(info.cwd);
|
|
356
|
-
const rootFiles = getPackageSourceFiles(info.pkgDir, parsedConfig);
|
|
357
|
-
const baseOptions = await getCompilerOptionsForPackage(
|
|
358
|
-
parsedConfig.options,
|
|
359
|
-
info.env,
|
|
360
|
-
info.pkgDir,
|
|
361
|
-
);
|
|
362
|
-
|
|
363
|
-
//
|
|
364
|
-
const pkgSrcDir = path.join(info.pkgDir, "src");
|
|
365
|
-
|
|
366
|
-
const options: ts.CompilerOptions = {
|
|
367
|
-
...baseOptions,
|
|
368
|
-
emitDeclarationOnly: true,
|
|
369
|
-
declaration: true,
|
|
370
|
-
declarationMap: true,
|
|
371
|
-
outDir: path.join(info.pkgDir, "dist"),
|
|
372
|
-
declarationDir: path.join(info.pkgDir, "dist"),
|
|
373
|
-
sourceMap: false,
|
|
374
|
-
noEmit: false,
|
|
375
|
-
incremental: true,
|
|
376
|
-
tsBuildInfoFile: path.join(info.pkgDir, ".cache", "dts.tsbuildinfo"),
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
let isFirstBuild = true;
|
|
380
|
-
const collectedErrors: string[] = [];
|
|
381
|
-
|
|
382
|
-
const reportDiagnostic: ts.DiagnosticReporter = (diagnostic) => {
|
|
383
|
-
if (diagnostic.category === ts.DiagnosticCategory.Error) {
|
|
384
|
-
//
|
|
385
|
-
if (diagnostic.file != null && !pathIsChildPath(diagnostic.file.fileName, pkgSrcDir)) {
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
390
|
-
|
|
391
|
-
//
|
|
392
|
-
if (diagnostic.file != null && diagnostic.start != null) {
|
|
393
|
-
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
|
|
394
|
-
diagnostic.start,
|
|
395
|
-
);
|
|
396
|
-
collectedErrors.push(
|
|
397
|
-
`${diagnostic.file.fileName}:${line + 1}:${character + 1}: TS${diagnostic.code}: ${message}`,
|
|
398
|
-
);
|
|
399
|
-
} else {
|
|
400
|
-
collectedErrors.push(`TS${diagnostic.code}: ${message}`);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
//
|
|
406
|
-
// TypeScript watch
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
const rewritePath = createDtsPathRewriter(info.pkgDir);
|
|
410
|
-
const originalWriteFile = ts.sys.writeFile;
|
|
411
|
-
const customSys: ts.System = {
|
|
412
|
-
...ts.sys,
|
|
413
|
-
writeFile: (filePath, content, writeByteOrderMark) => {
|
|
414
|
-
const result = rewritePath(filePath, content);
|
|
415
|
-
if (result != null) {
|
|
416
|
-
originalWriteFile(result[0], result[1], writeByteOrderMark);
|
|
417
|
-
}
|
|
418
|
-
},
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
const host = ts.createWatchCompilerHost(
|
|
422
|
-
rootFiles,
|
|
423
|
-
options,
|
|
424
|
-
customSys,
|
|
425
|
-
ts.createEmitAndSemanticDiagnosticsBuilderProgram,
|
|
426
|
-
reportDiagnostic,
|
|
427
|
-
() => {}, // watchStatusReporter -
|
|
428
|
-
);
|
|
429
|
-
|
|
430
|
-
const originalAfterProgramCreate = host.afterProgramCreate;
|
|
431
|
-
host.afterProgramCreate = (program) => {
|
|
432
|
-
originalAfterProgramCreate?.(program);
|
|
433
|
-
|
|
434
|
-
if (!isFirstBuild) {
|
|
435
|
-
sender.send("buildStart", {});
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
program.emit();
|
|
439
|
-
|
|
440
|
-
sender.send("build", {
|
|
441
|
-
success: collectedErrors.length === 0,
|
|
442
|
-
errors: collectedErrors.length > 0 ? [...collectedErrors] : undefined,
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
collectedErrors.length = 0;
|
|
446
|
-
isFirstBuild = false;
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
tscWatchProgram = ts.createWatchProgram(host);
|
|
450
|
-
} catch (err) {
|
|
451
|
-
sender.send("error", {
|
|
452
|
-
message: errorMessage(err),
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const sender = createWorker<
|
|
458
|
-
{ startWatch: typeof startWatch; build: typeof build },
|
|
459
|
-
DtsWorkerEvents
|
|
460
|
-
>({
|
|
461
|
-
startWatch,
|
|
462
|
-
build,
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
export default sender;
|
|
466
|
-
|
|
467
|
-
//#endregion
|
|
1
|
+
import path from "path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { createWorker, pathIsChildPath, pathNorm } from "@simplysm/core-node";
|
|
4
|
+
import { errorMessage } from "@simplysm/core-common";
|
|
5
|
+
import { consola } from "consola";
|
|
6
|
+
import {
|
|
7
|
+
getCompilerOptionsForPackage,
|
|
8
|
+
getPackageFiles,
|
|
9
|
+
getPackageSourceFiles,
|
|
10
|
+
parseRootTsconfig,
|
|
11
|
+
type TypecheckEnv,
|
|
12
|
+
} from "../utils/tsconfig";
|
|
13
|
+
import { serializeDiagnostic, type SerializedDiagnostic } from "../utils/typecheck-serialization";
|
|
14
|
+
import { createOnceGuard } from "../utils/worker-utils";
|
|
15
|
+
|
|
16
|
+
//#region Types
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* DTS watch start info
|
|
20
|
+
*/
|
|
21
|
+
export interface DtsWatchInfo {
|
|
22
|
+
name: string;
|
|
23
|
+
cwd: string;
|
|
24
|
+
pkgDir: string;
|
|
25
|
+
env: TypecheckEnv;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* DTS one-time build info
|
|
30
|
+
*/
|
|
31
|
+
export interface DtsBuildInfo {
|
|
32
|
+
name: string;
|
|
33
|
+
cwd: string;
|
|
34
|
+
/** Package directory. If unspecified, non-package mode (typecheck all except packages/) */
|
|
35
|
+
pkgDir?: string;
|
|
36
|
+
/** Typecheck environment. Used together with pkgDir */
|
|
37
|
+
env?: TypecheckEnv;
|
|
38
|
+
/** true to generate .d.ts + typecheck, false to typecheck only (default: true) */
|
|
39
|
+
emit?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* DTS one-time build result
|
|
44
|
+
*/
|
|
45
|
+
export interface DtsBuildResult {
|
|
46
|
+
success: boolean;
|
|
47
|
+
errors?: string[];
|
|
48
|
+
diagnostics: SerializedDiagnostic[];
|
|
49
|
+
errorCount: number;
|
|
50
|
+
warningCount: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Build event
|
|
55
|
+
*/
|
|
56
|
+
export interface DtsBuildEvent {
|
|
57
|
+
success: boolean;
|
|
58
|
+
errors?: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Error event
|
|
63
|
+
*/
|
|
64
|
+
export interface DtsErrorEvent {
|
|
65
|
+
message: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Worker event types
|
|
70
|
+
*/
|
|
71
|
+
export interface DtsWorkerEvents extends Record<string, unknown> {
|
|
72
|
+
buildStart: Record<string, never>;
|
|
73
|
+
build: DtsBuildEvent;
|
|
74
|
+
error: DtsErrorEvent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
|
|
79
|
+
//#region Resource Management
|
|
80
|
+
|
|
81
|
+
const logger = consola.withTag("sd:cli:dts:worker");
|
|
82
|
+
|
|
83
|
+
/** tsc watch program (to be cleaned up) */
|
|
84
|
+
let tscWatchProgram:
|
|
85
|
+
| ts.WatchOfFilesAndCompilerOptions<ts.EmitAndSemanticDiagnosticsBuilderProgram>
|
|
86
|
+
| undefined;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Clean up resources
|
|
90
|
+
*/
|
|
91
|
+
function cleanup(): void {
|
|
92
|
+
if (tscWatchProgram != null) {
|
|
93
|
+
tscWatchProgram.close();
|
|
94
|
+
tscWatchProgram = undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
process.on("SIGTERM", () => {
|
|
99
|
+
try {
|
|
100
|
+
cleanup();
|
|
101
|
+
} catch (err) {
|
|
102
|
+
logger.error("Cleanup failed", err);
|
|
103
|
+
}
|
|
104
|
+
process.exit(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
process.on("SIGINT", () => {
|
|
108
|
+
try {
|
|
109
|
+
cleanup();
|
|
110
|
+
} catch (err) {
|
|
111
|
+
logger.error("Cleanup failed", err);
|
|
112
|
+
}
|
|
113
|
+
process.exit(0);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
|
|
118
|
+
//#region DTS Output Path Rewriting
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Adjust sources path in .d.ts.map file to new location
|
|
122
|
+
*/
|
|
123
|
+
function adjustDtsMapSources(content: string, originalDir: string, newDir: string): string {
|
|
124
|
+
if (originalDir === newDir) return content;
|
|
125
|
+
try {
|
|
126
|
+
const map = JSON.parse(content) as { sources?: string[] };
|
|
127
|
+
if (Array.isArray(map.sources)) {
|
|
128
|
+
map.sources = map.sources.map((source) => {
|
|
129
|
+
const absoluteSource = path.resolve(originalDir, source);
|
|
130
|
+
return path.relative(newDir, absoluteSource);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return JSON.stringify(map);
|
|
134
|
+
} catch {
|
|
135
|
+
return content;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Create path rewriter function for DTS writeFile
|
|
141
|
+
*
|
|
142
|
+
* TypeScript includes other package sources referenced via path alias (@simplysm/*)
|
|
143
|
+
* in rootDir calculation, so output is generated as nested structure dist/{pkgName}/src/...
|
|
144
|
+
* The returned function rewrites only this package's .d.ts to flat structure (dist/...)
|
|
145
|
+
* and ignores .d.ts from other packages.
|
|
146
|
+
*
|
|
147
|
+
* @returns (fileName, content) => [newPath, newContent] | null (null to skip writing)
|
|
148
|
+
*/
|
|
149
|
+
function createDtsPathRewriter(
|
|
150
|
+
pkgDir: string,
|
|
151
|
+
): (fileName: string, content: string) => [string, string] | null {
|
|
152
|
+
const pkgName = path.basename(pkgDir);
|
|
153
|
+
const distDir = pathNorm(path.join(pkgDir, "dist"));
|
|
154
|
+
const distPrefix = distDir + path.sep;
|
|
155
|
+
// Nested structure prefix for this package: dist/{pkgName}/src/
|
|
156
|
+
const ownNestedPrefix = pathNorm(path.join(distDir, pkgName, "src")) + path.sep;
|
|
157
|
+
|
|
158
|
+
return (fileName, content) => {
|
|
159
|
+
fileName = pathNorm(fileName);
|
|
160
|
+
|
|
161
|
+
if (!fileName.startsWith(distPrefix)) return null;
|
|
162
|
+
|
|
163
|
+
if (fileName.startsWith(ownNestedPrefix)) {
|
|
164
|
+
// Rewrite nested path to flat: dist/{pkgName}/src/... → dist/...
|
|
165
|
+
const flatPath = path.join(distDir, fileName.slice(ownNestedPrefix.length));
|
|
166
|
+
if (fileName.endsWith(".d.ts.map")) {
|
|
167
|
+
content = adjustDtsMapSources(content, path.dirname(fileName), path.dirname(flatPath));
|
|
168
|
+
}
|
|
169
|
+
return [flatPath, content];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Nested output from other packages (dist/{otherPkg}/src/...) → ignore
|
|
173
|
+
const relFromDist = fileName.slice(distPrefix.length);
|
|
174
|
+
const segments = relFromDist.split(path.sep);
|
|
175
|
+
if (segments.length >= 3 && segments[1] === "src") {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Already flat structure (package with no dependencies) → output as is
|
|
180
|
+
return [fileName, content];
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
//#endregion
|
|
185
|
+
|
|
186
|
+
//#region build (one-time build)
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* DTS one-time build (typecheck + dts generation)
|
|
190
|
+
*/
|
|
191
|
+
async function build(info: DtsBuildInfo): Promise<DtsBuildResult> {
|
|
192
|
+
try {
|
|
193
|
+
const parsedConfig = parseRootTsconfig(info.cwd);
|
|
194
|
+
|
|
195
|
+
let rootFiles: string[];
|
|
196
|
+
let baseOptions: ts.CompilerOptions;
|
|
197
|
+
let diagnosticFilter: (d: ts.Diagnostic) => boolean;
|
|
198
|
+
let tsBuildInfoFile: string;
|
|
199
|
+
|
|
200
|
+
if (info.pkgDir != null && info.env != null) {
|
|
201
|
+
// Package mode
|
|
202
|
+
baseOptions = await getCompilerOptionsForPackage(parsedConfig.options, info.env, info.pkgDir);
|
|
203
|
+
|
|
204
|
+
const shouldEmit = info.emit !== false;
|
|
205
|
+
if (shouldEmit) {
|
|
206
|
+
// Emit mode: only src (generate d.ts)
|
|
207
|
+
rootFiles = getPackageSourceFiles(info.pkgDir, parsedConfig);
|
|
208
|
+
const pkgSrcDir = path.join(info.pkgDir, "src");
|
|
209
|
+
diagnosticFilter = (d) => d.file == null || pathIsChildPath(d.file.fileName, pkgSrcDir);
|
|
210
|
+
} else {
|
|
211
|
+
// Typecheck mode: all package files (src + tests)
|
|
212
|
+
rootFiles = getPackageFiles(info.pkgDir, parsedConfig);
|
|
213
|
+
const pkgDir = info.pkgDir;
|
|
214
|
+
diagnosticFilter = (d) => d.file == null || pathIsChildPath(d.file.fileName, pkgDir);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
tsBuildInfoFile = path.join(
|
|
218
|
+
info.pkgDir,
|
|
219
|
+
".cache",
|
|
220
|
+
shouldEmit ? "dts.tsbuildinfo" : `typecheck-${info.env}.tsbuildinfo`,
|
|
221
|
+
);
|
|
222
|
+
} else {
|
|
223
|
+
// Non-package mode: root project files + package root config files typecheck
|
|
224
|
+
const packagesDir = path.join(info.cwd, "packages");
|
|
225
|
+
const isNonPackageFile = (fileName: string): boolean => {
|
|
226
|
+
if (!pathIsChildPath(fileName, packagesDir)) return true;
|
|
227
|
+
// Include files directly in package root (config files): packages/{pkg}/file.ts
|
|
228
|
+
const relative = path.relative(packagesDir, fileName);
|
|
229
|
+
return relative.split(path.sep).length === 2;
|
|
230
|
+
};
|
|
231
|
+
rootFiles = parsedConfig.fileNames.filter(isNonPackageFile);
|
|
232
|
+
baseOptions = parsedConfig.options;
|
|
233
|
+
diagnosticFilter = (d) => d.file == null || isNonPackageFile(d.file.fileName);
|
|
234
|
+
tsBuildInfoFile = path.join(info.cwd, ".cache", "typecheck-root.tsbuildinfo");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Determine emit (default: true)
|
|
238
|
+
const shouldEmit = info.emit !== false;
|
|
239
|
+
|
|
240
|
+
const options: ts.CompilerOptions = {
|
|
241
|
+
...baseOptions,
|
|
242
|
+
sourceMap: false,
|
|
243
|
+
incremental: true,
|
|
244
|
+
tsBuildInfoFile,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Set related options based on emit
|
|
248
|
+
if (shouldEmit && info.pkgDir != null) {
|
|
249
|
+
// Generate dts + typecheck (package mode only)
|
|
250
|
+
options.noEmit = false;
|
|
251
|
+
options.emitDeclarationOnly = true;
|
|
252
|
+
options.declaration = true;
|
|
253
|
+
options.declarationMap = true;
|
|
254
|
+
options.outDir = path.join(info.pkgDir, "dist");
|
|
255
|
+
options.declarationDir = path.join(info.pkgDir, "dist");
|
|
256
|
+
} else {
|
|
257
|
+
// Typecheck only (no dts generation)
|
|
258
|
+
options.noEmit = true;
|
|
259
|
+
options.emitDeclarationOnly = false;
|
|
260
|
+
options.declaration = false;
|
|
261
|
+
options.declarationMap = false;
|
|
262
|
+
// outDir/declarationDir not needed when not emitting
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Create incremental program
|
|
266
|
+
const host = ts.createIncrementalCompilerHost(options);
|
|
267
|
+
|
|
268
|
+
// Output only this package's .d.ts in flat path (prevent other packages' .d.ts generation + rewrite nested paths)
|
|
269
|
+
if (shouldEmit && info.pkgDir != null) {
|
|
270
|
+
const rewritePath = createDtsPathRewriter(info.pkgDir);
|
|
271
|
+
const originalWriteFile = host.writeFile;
|
|
272
|
+
host.writeFile = (fileName, content, writeByteOrderMark, onError, sourceFiles, data) => {
|
|
273
|
+
const result = rewritePath(fileName, content);
|
|
274
|
+
if (result != null) {
|
|
275
|
+
originalWriteFile(result[0], result[1], writeByteOrderMark, onError, sourceFiles, data);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const program = ts.createIncrementalProgram({
|
|
281
|
+
rootNames: rootFiles,
|
|
282
|
+
options,
|
|
283
|
+
host,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Emit (must call even with noEmit to collect diagnostics)
|
|
287
|
+
const emitResult = program.emit();
|
|
288
|
+
|
|
289
|
+
// Collect diagnostics
|
|
290
|
+
const allDiagnostics = [
|
|
291
|
+
...program.getConfigFileParsingDiagnostics(),
|
|
292
|
+
...program.getSyntacticDiagnostics(),
|
|
293
|
+
...program.getOptionsDiagnostics(),
|
|
294
|
+
...program.getGlobalDiagnostics(),
|
|
295
|
+
...program.getSemanticDiagnostics(),
|
|
296
|
+
...emitResult.diagnostics,
|
|
297
|
+
];
|
|
298
|
+
|
|
299
|
+
// Collect errors only from this package's src folder (ignore other packages' errors)
|
|
300
|
+
const filteredDiagnostics = allDiagnostics.filter(diagnosticFilter);
|
|
301
|
+
|
|
302
|
+
const serializedDiagnostics = filteredDiagnostics.map(serializeDiagnostic);
|
|
303
|
+
const errorCount = filteredDiagnostics.filter(
|
|
304
|
+
(d) => d.category === ts.DiagnosticCategory.Error,
|
|
305
|
+
).length;
|
|
306
|
+
const warningCount = filteredDiagnostics.filter(
|
|
307
|
+
(d) => d.category === ts.DiagnosticCategory.Warning,
|
|
308
|
+
).length;
|
|
309
|
+
|
|
310
|
+
// Error message string array (for backward compatibility)
|
|
311
|
+
const errors = filteredDiagnostics
|
|
312
|
+
.filter((d) => d.category === ts.DiagnosticCategory.Error)
|
|
313
|
+
.map((d) => {
|
|
314
|
+
const message = ts.flattenDiagnosticMessageText(d.messageText, "\n");
|
|
315
|
+
if (d.file != null && d.start != null) {
|
|
316
|
+
const { line, character } = d.file.getLineAndCharacterOfPosition(d.start);
|
|
317
|
+
return `${d.file.fileName}:${line + 1}:${character + 1}: TS${d.code}: ${message}`;
|
|
318
|
+
}
|
|
319
|
+
return `TS${d.code}: ${message}`;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
success: errorCount === 0,
|
|
324
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
325
|
+
diagnostics: serializedDiagnostics,
|
|
326
|
+
errorCount,
|
|
327
|
+
warningCount,
|
|
328
|
+
};
|
|
329
|
+
} catch (err) {
|
|
330
|
+
return {
|
|
331
|
+
success: false,
|
|
332
|
+
errors: [errorMessage(err)],
|
|
333
|
+
diagnostics: [],
|
|
334
|
+
errorCount: 1,
|
|
335
|
+
warningCount: 0,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
//#endregion
|
|
341
|
+
|
|
342
|
+
//#region startWatch (watch mode)
|
|
343
|
+
|
|
344
|
+
const guardStartWatch = createOnceGuard("startWatch");
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Start DTS watch
|
|
348
|
+
* @remarks This function should be called only once per Worker.
|
|
349
|
+
* @throws If watch has already been started
|
|
350
|
+
*/
|
|
351
|
+
async function startWatch(info: DtsWatchInfo): Promise<void> {
|
|
352
|
+
guardStartWatch();
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
const parsedConfig = parseRootTsconfig(info.cwd);
|
|
356
|
+
const rootFiles = getPackageSourceFiles(info.pkgDir, parsedConfig);
|
|
357
|
+
const baseOptions = await getCompilerOptionsForPackage(
|
|
358
|
+
parsedConfig.options,
|
|
359
|
+
info.env,
|
|
360
|
+
info.pkgDir,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
// This package path (for filtering)
|
|
364
|
+
const pkgSrcDir = path.join(info.pkgDir, "src");
|
|
365
|
+
|
|
366
|
+
const options: ts.CompilerOptions = {
|
|
367
|
+
...baseOptions,
|
|
368
|
+
emitDeclarationOnly: true,
|
|
369
|
+
declaration: true,
|
|
370
|
+
declarationMap: true,
|
|
371
|
+
outDir: path.join(info.pkgDir, "dist"),
|
|
372
|
+
declarationDir: path.join(info.pkgDir, "dist"),
|
|
373
|
+
sourceMap: false,
|
|
374
|
+
noEmit: false,
|
|
375
|
+
incremental: true,
|
|
376
|
+
tsBuildInfoFile: path.join(info.pkgDir, ".cache", "dts.tsbuildinfo"),
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
let isFirstBuild = true;
|
|
380
|
+
const collectedErrors: string[] = [];
|
|
381
|
+
|
|
382
|
+
const reportDiagnostic: ts.DiagnosticReporter = (diagnostic) => {
|
|
383
|
+
if (diagnostic.category === ts.DiagnosticCategory.Error) {
|
|
384
|
+
// Collect errors only from this package's src folder (ignore other packages' errors)
|
|
385
|
+
if (diagnostic.file != null && !pathIsChildPath(diagnostic.file.fileName, pkgSrcDir)) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
390
|
+
|
|
391
|
+
// Include file location info if available (absolute path:line:column format - supports IDE links)
|
|
392
|
+
if (diagnostic.file != null && diagnostic.start != null) {
|
|
393
|
+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
|
|
394
|
+
diagnostic.start,
|
|
395
|
+
);
|
|
396
|
+
collectedErrors.push(
|
|
397
|
+
`${diagnostic.file.fileName}:${line + 1}:${character + 1}: TS${diagnostic.code}: ${message}`,
|
|
398
|
+
);
|
|
399
|
+
} else {
|
|
400
|
+
collectedErrors.push(`TS${diagnostic.code}: ${message}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// Output only this package's .d.ts in flat path (prevent other packages' .d.ts generation + rewrite nested paths)
|
|
406
|
+
// TypeScript watch mode attempts to generate .d.ts for all imported modules.
|
|
407
|
+
// In a monorepo, we prevent overwriting .d.ts from other packages and
|
|
408
|
+
// rewrite nested paths (dist/{pkgName}/src/...) to flat paths (dist/...).
|
|
409
|
+
const rewritePath = createDtsPathRewriter(info.pkgDir);
|
|
410
|
+
const originalWriteFile = ts.sys.writeFile;
|
|
411
|
+
const customSys: ts.System = {
|
|
412
|
+
...ts.sys,
|
|
413
|
+
writeFile: (filePath, content, writeByteOrderMark) => {
|
|
414
|
+
const result = rewritePath(filePath, content);
|
|
415
|
+
if (result != null) {
|
|
416
|
+
originalWriteFile(result[0], result[1], writeByteOrderMark);
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const host = ts.createWatchCompilerHost(
|
|
422
|
+
rootFiles,
|
|
423
|
+
options,
|
|
424
|
+
customSys,
|
|
425
|
+
ts.createEmitAndSemanticDiagnosticsBuilderProgram,
|
|
426
|
+
reportDiagnostic,
|
|
427
|
+
() => {}, // watchStatusReporter - not used
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const originalAfterProgramCreate = host.afterProgramCreate;
|
|
431
|
+
host.afterProgramCreate = (program) => {
|
|
432
|
+
originalAfterProgramCreate?.(program);
|
|
433
|
+
|
|
434
|
+
if (!isFirstBuild) {
|
|
435
|
+
sender.send("buildStart", {});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
program.emit();
|
|
439
|
+
|
|
440
|
+
sender.send("build", {
|
|
441
|
+
success: collectedErrors.length === 0,
|
|
442
|
+
errors: collectedErrors.length > 0 ? [...collectedErrors] : undefined,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
collectedErrors.length = 0;
|
|
446
|
+
isFirstBuild = false;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
tscWatchProgram = ts.createWatchProgram(host);
|
|
450
|
+
} catch (err) {
|
|
451
|
+
sender.send("error", {
|
|
452
|
+
message: errorMessage(err),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const sender = createWorker<
|
|
458
|
+
{ startWatch: typeof startWatch; build: typeof build },
|
|
459
|
+
DtsWorkerEvents
|
|
460
|
+
>({
|
|
461
|
+
startWatch,
|
|
462
|
+
build,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
export default sender;
|
|
466
|
+
|
|
467
|
+
//#endregion
|