@simplysm/sd-cli 14.0.49 → 14.0.52
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/angular/angular-compiler.d.ts +3 -0
- package/dist/angular/angular-compiler.d.ts.map +1 -1
- package/dist/angular/angular-compiler.js +3 -0
- package/dist/angular/angular-compiler.js.map +1 -1
- package/dist/dev-server/hmr-service.d.ts.map +1 -1
- package/dist/dev-server/hmr-service.js +10 -5
- package/dist/dev-server/hmr-service.js.map +1 -1
- package/dist/engines/EsbuildClientEngine.d.ts.map +1 -1
- package/dist/engines/EsbuildClientEngine.js +1 -2
- package/dist/engines/EsbuildClientEngine.js.map +1 -1
- package/dist/esbuild/esbuild-angular-compiler-plugin.js +5 -5
- package/dist/esbuild/esbuild-angular-compiler-plugin.js.map +1 -1
- package/dist/esbuild/esbuild-client-config.d.ts.map +1 -1
- package/dist/esbuild/esbuild-client-config.js +10 -5
- package/dist/esbuild/esbuild-client-config.js.map +1 -1
- package/dist/lint/lint-core.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/orchestrators/ServerRuntimeManager.d.ts.map +1 -1
- package/dist/orchestrators/ServerRuntimeManager.js +2 -4
- package/dist/orchestrators/ServerRuntimeManager.js.map +1 -1
- package/dist/ts-compiler/SdTsCompiler.d.ts +1 -7
- package/dist/ts-compiler/SdTsCompiler.d.ts.map +1 -1
- package/dist/ts-compiler/SdTsCompiler.js +109 -116
- package/dist/ts-compiler/SdTsCompiler.js.map +1 -1
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +6 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/package.json +5 -5
- package/src/angular/angular-compiler.ts +3 -0
- package/src/dev-server/hmr-service.ts +10 -5
- package/src/engines/EsbuildClientEngine.ts +1 -2
- package/src/esbuild/esbuild-angular-compiler-plugin.ts +6 -6
- package/src/esbuild/esbuild-client-config.ts +17 -12
- package/src/lint/lint-core.ts +1 -1
- package/src/orchestrators/DevOrchestrator.ts +3 -3
- package/src/orchestrators/ServerRuntimeManager.ts +2 -5
- package/src/ts-compiler/SdTsCompiler.ts +136 -154
- package/src/utils/package-utils.ts +1 -2
- package/src/workers/client.worker.ts +6 -1
- package/tests/esbuild/esbuild-angular-compiler-plugin.spec.ts +1 -1
- package/tests/esbuild/esbuild-postcss-plugin.acc.spec.ts +1 -1
- package/tests/esbuild/esbuild-tsc-plugin.acc.spec.ts +2 -2
- package/tests/esbuild/esbuild-tsc-plugin.spec.ts +3 -3
- package/tests/esbuild/esbuild-worker-plugin.spec.ts +1 -1
- package/tests/orchestrators/build-orchestrator.spec.ts +9 -9
- package/tests/orchestrators/dev-orchestrator.spec.ts +4 -4
- package/tests/orchestrators/watch-orchestrator.spec.ts +2 -2
- package/tests/ts-compiler/SdTsCompiler-crash-handling.verify.md +24 -0
- package/tests/utils/copy-src.spec.ts +5 -5
- package/tests/utils/esbuild-client-config.acc.spec.ts +1 -1
- package/tests/utils/esbuild-client-config.spec.ts +4 -4
- package/tests/utils/esbuild-config.spec.ts +3 -3
- package/tests/utils/esbuild-postcss-plugin.spec.ts +1 -1
- package/tests/utils/hmr-client-script.acc.spec.ts +1 -1
- package/tests/utils/ngtsc-build-core.spec.ts +1 -1
- package/tests/utils/package-utils.spec.ts +6 -6
- package/tests/workers/server-build-worker.spec.ts +1 -1
- package/README.md +0 -782
|
@@ -44,9 +44,12 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
|
|
|
44
44
|
let prevOutputs: Map<string, string> | undefined;
|
|
45
45
|
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
46
46
|
let pendingMetafile: esbuild.Metafile | undefined;
|
|
47
|
+
let pendingTemplateKeys: string[] | undefined;
|
|
47
48
|
|
|
48
49
|
function onBuildEnd(metafile: esbuild.Metafile): void {
|
|
49
50
|
pendingMetafile = metafile;
|
|
51
|
+
pendingTemplateKeys =
|
|
52
|
+
templateUpdates.size > 0 ? [...templateUpdates.keys()] : undefined;
|
|
50
53
|
if (debounceTimer != null) clearTimeout(debounceTimer);
|
|
51
54
|
debounceTimer = setTimeout(() => {
|
|
52
55
|
debounceTimer = undefined;
|
|
@@ -62,7 +65,7 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
|
|
|
62
65
|
let fingerprint = String(output.bytes);
|
|
63
66
|
if (outDir != null) {
|
|
64
67
|
try {
|
|
65
|
-
const filePath = path.resolve(
|
|
68
|
+
const filePath = path.resolve(normalizedPath);
|
|
66
69
|
const content = fs.readFileSync(filePath);
|
|
67
70
|
fingerprint = crypto.createHash("md5").update(content).digest("hex");
|
|
68
71
|
} catch {
|
|
@@ -78,16 +81,18 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
|
|
|
78
81
|
function dispatchHmrMessage(): void {
|
|
79
82
|
if (pendingMetafile == null) return;
|
|
80
83
|
const metafile = pendingMetafile;
|
|
84
|
+
const snapshotKeys = pendingTemplateKeys;
|
|
81
85
|
pendingMetafile = undefined;
|
|
86
|
+
pendingTemplateKeys = undefined;
|
|
82
87
|
|
|
83
88
|
const timestamp = Date.now();
|
|
84
89
|
const currentOutputs = collectOutputs(metafile);
|
|
85
90
|
|
|
86
|
-
// 1. templateUpdates
|
|
87
|
-
if (
|
|
91
|
+
// 1. onBuildEnd 시점에 캡처된 templateUpdates가 있으면 component-update
|
|
92
|
+
if (snapshotKeys != null && snapshotKeys.length > 0) {
|
|
88
93
|
broadcast({
|
|
89
94
|
type: "component-update",
|
|
90
|
-
ids:
|
|
95
|
+
ids: snapshotKeys,
|
|
91
96
|
timestamp,
|
|
92
97
|
});
|
|
93
98
|
prevOutputs = currentOutputs;
|
|
@@ -170,7 +175,7 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
|
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
const componentId = parsedUrl.searchParams.get("c") ?? "";
|
|
173
|
-
const body = templateUpdates.get(componentId) ?? "";
|
|
178
|
+
const body = templateUpdates.get(encodeURIComponent(componentId)) ?? "";
|
|
174
179
|
|
|
175
180
|
res.writeHead(200, {
|
|
176
181
|
"Content-Type": "text/javascript",
|
|
@@ -96,8 +96,7 @@ export class EsbuildClientEngine implements BuildEngine {
|
|
|
96
96
|
|
|
97
97
|
// serverReady를 감지하여 포트 캡처 (EsbuildClientEngine 전용)
|
|
98
98
|
this._worker!.on("serverReady", (data) => {
|
|
99
|
-
|
|
100
|
-
this.port = event.port;
|
|
99
|
+
this.port = data.port;
|
|
101
100
|
});
|
|
102
101
|
|
|
103
102
|
// 공통 이벤트 처리 (buildStart/build/error)
|
|
@@ -96,7 +96,7 @@ export function createCompilerOptionsTransformer(
|
|
|
96
96
|
_enableHmr: !!options.templateUpdates,
|
|
97
97
|
supportTestBed: !!options.includeTestMetadata,
|
|
98
98
|
supportJitMode: !!options.includeTestMetadata,
|
|
99
|
-
}
|
|
99
|
+
};
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -333,10 +333,10 @@ export function createAngularCompilerPlugin(
|
|
|
333
333
|
if (isIncremental) {
|
|
334
334
|
const useHmr =
|
|
335
335
|
pluginOptions.templateUpdates != null &&
|
|
336
|
-
sourceFileCache.
|
|
336
|
+
sourceFileCache.cycleModifiedFiles.size <= HMR_MODIFIED_FILE_LIMIT;
|
|
337
337
|
|
|
338
338
|
if (useHmr && lastResult != null) {
|
|
339
|
-
for (const modifiedFile of sourceFileCache.
|
|
339
|
+
for (const modifiedFile of sourceFileCache.cycleModifiedFiles) {
|
|
340
340
|
const sf = lastResult.program.getSourceFile(modifiedFile);
|
|
341
341
|
if (sf != null) {
|
|
342
342
|
staleSourceFiles ??= new Map();
|
|
@@ -345,9 +345,9 @@ export function createAngularCompilerPlugin(
|
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
-
// referencedFileTracker 확장
|
|
348
|
+
// referencedFileTracker 확장 (per-cycle 변경 파일만 전달하여 불필요한 재순회 방지)
|
|
349
349
|
expandedModifiedFiles = referencedFileTracker.update(
|
|
350
|
-
sourceFileCache.
|
|
350
|
+
sourceFileCache.cycleModifiedFiles,
|
|
351
351
|
);
|
|
352
352
|
|
|
353
353
|
// 변경된 파일의 Worker 결과만 선택적 제거 (변경되지 않은 파일의
|
|
@@ -452,7 +452,7 @@ export function createAngularCompilerPlugin(
|
|
|
452
452
|
pluginOptions.templateUpdates != null
|
|
453
453
|
) {
|
|
454
454
|
const hmrCandidates = collectHmrCandidates(
|
|
455
|
-
sourceFileCache.
|
|
455
|
+
sourceFileCache.cycleModifiedFiles,
|
|
456
456
|
compileResult.ngtscProgram,
|
|
457
457
|
staleSourceFiles,
|
|
458
458
|
);
|
|
@@ -154,19 +154,24 @@ export async function createClientEsbuildContext(
|
|
|
154
154
|
define["import.meta.env"] = JSON.stringify(options.env);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
// import.meta.hot 폴리필
|
|
157
|
+
// import.meta.hot 폴리필 (Angular HMR 런타임용)
|
|
158
158
|
// Angular의 compileHmrInitializer가 import.meta.hot.on('angular:component-update', ...)을 사용.
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
159
|
+
// ES Module에서 import.meta는 모듈별로 고유하므로, banner가 아닌 globalThis에 hot 객체를 저장하고
|
|
160
|
+
// esbuild define으로 import.meta.hot을 globalThis.__hmr_hot으로 치환한다.
|
|
161
|
+
const useHmrPolyfill =
|
|
162
|
+
options.templateUpdates != null && options.legacyModule !== true;
|
|
163
|
+
if (useHmrPolyfill) {
|
|
164
|
+
define["import.meta.hot"] = "globalThis.__hmr_hot";
|
|
165
|
+
}
|
|
166
|
+
const hmrBanner = useHmrPolyfill
|
|
167
|
+
? [
|
|
168
|
+
"if(!globalThis.__hmr_hot){(function(){",
|
|
169
|
+
"var _l={};",
|
|
170
|
+
"globalThis.__hmr_hot={on:function(e,c){if(!_l[e])_l[e]=[];_l[e].push(c);},off:function(e,c){var a=_l[e];if(a){var i=a.indexOf(c);if(i!==-1)a.splice(i,1);}}};",
|
|
171
|
+
"globalThis.__hmr_dispatch=function(e,d){var a=_l[e];if(a)for(var i=0;i<a.length;i++)a[i](d);};",
|
|
172
|
+
"})()}",
|
|
173
|
+
].join("")
|
|
174
|
+
: undefined;
|
|
170
175
|
|
|
171
176
|
// esbuild context 생성
|
|
172
177
|
const context = await esbuild.context({
|
package/src/lint/lint-core.ts
CHANGED
|
@@ -52,7 +52,7 @@ function isGlobalIgnoresConfig(item: unknown): item is { ignores: string[] } {
|
|
|
52
52
|
if (item == null || typeof item !== "object") return false;
|
|
53
53
|
if (!("ignores" in item)) return false;
|
|
54
54
|
if ("files" in item) return false; // files가 있으면 globalIgnores가 아님
|
|
55
|
-
const ignores =
|
|
55
|
+
const ignores = item.ignores;
|
|
56
56
|
if (!Array.isArray(ignores)) return false;
|
|
57
57
|
return ignores.every((i) => typeof i === "string");
|
|
58
58
|
}
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
import { filterPackagesByTargets, classifyDevPackages } from "../utils/package-classify";
|
|
9
9
|
import { printDiagnostics, printServers } from "../utils/output-utils";
|
|
10
10
|
import { createBuildEngine } from "../engines/engine-factory";
|
|
11
|
-
import type { BuildEngine
|
|
11
|
+
import type { BuildEngine } from "../engines/types";
|
|
12
12
|
import { Capacitor } from "../capacitor/capacitor";
|
|
13
13
|
import { BaseOrchestrator } from "./BaseOrchestrator";
|
|
14
14
|
import { ServerRuntimeManager } from "./ServerRuntimeManager";
|
|
@@ -85,7 +85,7 @@ export class DevOrchestrator extends BaseOrchestrator implements OrchestratorLif
|
|
|
85
85
|
const engineConfig = { ...config, env: { ...this._baseEnv, ...config.env } };
|
|
86
86
|
|
|
87
87
|
const engine = createBuildEngine(
|
|
88
|
-
{ name, dir, config: engineConfig }
|
|
88
|
+
{ name, dir, config: engineConfig },
|
|
89
89
|
{
|
|
90
90
|
cwd: this._cwd,
|
|
91
91
|
replaceDeps: this._replaceDeps,
|
|
@@ -100,7 +100,7 @@ export class DevOrchestrator extends BaseOrchestrator implements OrchestratorLif
|
|
|
100
100
|
for (const { name, dir, config } of this._clientPackages) {
|
|
101
101
|
const engineConfig = { ...config, env: { ...this._baseEnv, ...config.env } };
|
|
102
102
|
const engine = createBuildEngine(
|
|
103
|
-
{ name, dir, config: engineConfig }
|
|
103
|
+
{ name, dir, config: engineConfig },
|
|
104
104
|
{
|
|
105
105
|
cwd: this._cwd,
|
|
106
106
|
replaceDeps: this._replaceDeps,
|
|
@@ -2,7 +2,6 @@ import type { ConsolaInstance } from "consola";
|
|
|
2
2
|
import { Worker, type WorkerProxy } from "@simplysm/core-node";
|
|
3
3
|
import { err as errNs } from "@simplysm/core-common";
|
|
4
4
|
import type * as ServerRuntimeWorkerModule from "../workers/server-runtime.worker";
|
|
5
|
-
import type { ServerReadyEventData, ErrorEventData } from "../runtime/worker-events";
|
|
6
5
|
import type { ResultCollector } from "../runtime/ResultCollector";
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -42,25 +41,23 @@ export class ServerRuntimeManager {
|
|
|
42
41
|
|
|
43
42
|
// 런타임 이벤트 핸들러
|
|
44
43
|
runtimeWorker.on("serverReady", (readyData) => {
|
|
45
|
-
const readyEvent = readyData as ServerReadyEventData;
|
|
46
44
|
params.resultCollector.add({
|
|
47
45
|
name: params.serverName,
|
|
48
46
|
target: "server",
|
|
49
47
|
type: "server",
|
|
50
48
|
status: "running",
|
|
51
|
-
port:
|
|
49
|
+
port: readyData.port,
|
|
52
50
|
});
|
|
53
51
|
params.onServerReady();
|
|
54
52
|
});
|
|
55
53
|
|
|
56
54
|
runtimeWorker.on("error", (errorData) => {
|
|
57
|
-
const errorEvent = errorData as ErrorEventData;
|
|
58
55
|
params.resultCollector.add({
|
|
59
56
|
name: params.serverName,
|
|
60
57
|
target: "server",
|
|
61
58
|
type: "server",
|
|
62
59
|
status: "error",
|
|
63
|
-
message:
|
|
60
|
+
message: errorData.message,
|
|
64
61
|
});
|
|
65
62
|
});
|
|
66
63
|
|
|
@@ -2,6 +2,7 @@ import path from "path";
|
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
3
|
import ts from "typescript";
|
|
4
4
|
import { consola, type ConsolaInstance } from "consola";
|
|
5
|
+
import { SdError } from "@simplysm/core-common";
|
|
5
6
|
import { pathx } from "@simplysm/core-node";
|
|
6
7
|
import type { ISdTsCompilerOptions, ISdTsCompilerEmitOptions } from "./sd-ts-compiler-options";
|
|
7
8
|
import type { ISdTsCompilerResult } from "./sd-ts-compiler-result";
|
|
@@ -104,6 +105,21 @@ export class SdTsCompiler {
|
|
|
104
105
|
return ctx.stage;
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
private _createCrashDiagnostic(e: unknown): SerializedDiagnostic {
|
|
109
|
+
const contextLabel = this._formatCrashContext();
|
|
110
|
+
const sdError =
|
|
111
|
+
e instanceof Error
|
|
112
|
+
? new SdError(e, `TsCompiler 내부 크래시 @${contextLabel}`)
|
|
113
|
+
: new SdError(`TsCompiler 내부 크래시 @${contextLabel}`, String(e));
|
|
114
|
+
const message = sdError.stack ?? sdError.message;
|
|
115
|
+
this._logger.debug(message);
|
|
116
|
+
return {
|
|
117
|
+
category: ts.DiagnosticCategory.Error,
|
|
118
|
+
code: 0,
|
|
119
|
+
messageText: message,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
107
123
|
private _getScssLoadPaths(): string[] {
|
|
108
124
|
const { pkgDir, cwd } = this._options;
|
|
109
125
|
return [path.join(pkgDir, "scss"), path.join(cwd, "node_modules")];
|
|
@@ -251,29 +267,40 @@ export class SdTsCompiler {
|
|
|
251
267
|
program = builderProgram.getProgram();
|
|
252
268
|
}
|
|
253
269
|
|
|
254
|
-
// 9. 위험 구간 (
|
|
270
|
+
// 9. 위험 구간 (단계별 catch — 부분 복구)
|
|
271
|
+
const crashDiagnostics: SerializedDiagnostic[] = [];
|
|
272
|
+
|
|
255
273
|
try {
|
|
274
|
+
// 9-0. analyzeAsync (Angular only)
|
|
256
275
|
if (isForAngular) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
276
|
+
try {
|
|
277
|
+
this._setCrashContext("analyzeAsync");
|
|
278
|
+
this._logger.debug(`[${pkgName}] AOT analyzeAsync 시작`);
|
|
279
|
+
await angularProgram!.compiler.analyzeAsync();
|
|
280
|
+
this._logger.debug(`[${pkgName}] AOT analyzeAsync 완료`);
|
|
281
|
+
this._ngtscProgram = angularProgram;
|
|
282
|
+
} catch (e) {
|
|
283
|
+
crashDiagnostics.push(this._createCrashDiagnostic(e));
|
|
284
|
+
}
|
|
262
285
|
}
|
|
263
286
|
|
|
264
287
|
// 9-1. affected files 추적
|
|
265
|
-
this._setCrashContext("findAffectedFiles");
|
|
266
288
|
let affectedFiles: ReadonlySet<string> | undefined;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
this.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
289
|
+
try {
|
|
290
|
+
this._setCrashContext("findAffectedFiles");
|
|
291
|
+
if (isForAngular && this._ngtscProgram != null) {
|
|
292
|
+
const result = this._findAffectedFilesForAngular(
|
|
293
|
+
builderProgram,
|
|
294
|
+
this._ngtscProgram.compiler,
|
|
295
|
+
this._sourceFileCache,
|
|
296
|
+
);
|
|
297
|
+
this._affectedSourceFiles = result.affectedSourceFiles;
|
|
298
|
+
affectedFiles = result.affectedPaths;
|
|
299
|
+
} else if (!isForAngular) {
|
|
300
|
+
affectedFiles = this._findAffectedFilesForTsc(builderProgram);
|
|
301
|
+
}
|
|
302
|
+
} catch (e) {
|
|
303
|
+
crashDiagnostics.push(this._createCrashDiagnostic(e));
|
|
277
304
|
}
|
|
278
305
|
|
|
279
306
|
if (affectedFiles != null) {
|
|
@@ -289,51 +316,83 @@ export class SdTsCompiler {
|
|
|
289
316
|
}
|
|
290
317
|
|
|
291
318
|
// 9-2. emit 처리
|
|
292
|
-
this._setCrashContext("emit");
|
|
293
319
|
let emitResults: EmitResult[] | undefined;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
320
|
+
try {
|
|
321
|
+
this._setCrashContext("emit");
|
|
322
|
+
if (isForAngular && this._ngtscProgram != null) {
|
|
323
|
+
emitResults = this._emitAngular(
|
|
324
|
+
this._ngtscProgram,
|
|
325
|
+
builderProgram,
|
|
326
|
+
this._affectedSourceFiles,
|
|
327
|
+
emitOptions,
|
|
328
|
+
);
|
|
329
|
+
} else if (!isForAngular) {
|
|
330
|
+
this._emitTsc(builderProgram);
|
|
331
|
+
}
|
|
332
|
+
} catch (e) {
|
|
333
|
+
crashDiagnostics.push(this._createCrashDiagnostic(e));
|
|
303
334
|
}
|
|
304
335
|
|
|
305
336
|
// 9-3. 진단 수집
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
337
|
+
let diagResult:
|
|
338
|
+
| {
|
|
339
|
+
diagnostics: SerializedDiagnostic[];
|
|
340
|
+
errorCount: number;
|
|
341
|
+
warningCount: number;
|
|
342
|
+
errors?: string[];
|
|
343
|
+
}
|
|
344
|
+
| undefined;
|
|
345
|
+
try {
|
|
346
|
+
this._setCrashContext("collectDiagnostics");
|
|
347
|
+
const rawDiagnostics =
|
|
348
|
+
isForAngular && this._ngtscProgram != null
|
|
349
|
+
? this._collectDiagnosticsForAngular(
|
|
350
|
+
this._ngtscProgram,
|
|
351
|
+
builderProgram,
|
|
352
|
+
this._affectedSourceFiles,
|
|
353
|
+
)
|
|
354
|
+
: this._collectDiagnosticsForTsc(builderProgram);
|
|
355
|
+
diagResult = this._finalizeDiagnostics(rawDiagnostics);
|
|
356
|
+
} catch (e) {
|
|
357
|
+
crashDiagnostics.push(this._createCrashDiagnostic(e));
|
|
358
|
+
}
|
|
315
359
|
|
|
316
360
|
// 9-4. 글로벌 SCSS + lint 병렬 실행
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
361
|
+
let lintResult: LintWithProgramResult | undefined;
|
|
362
|
+
try {
|
|
363
|
+
this._setCrashContext("lintAndGlobalScss");
|
|
364
|
+
const [, lr] = await Promise.all([
|
|
365
|
+
// globalScss
|
|
366
|
+
this._options.globalScss === true
|
|
367
|
+
? Promise.resolve().then(() => {
|
|
368
|
+
const loadPaths = this._getScssLoadPaths();
|
|
369
|
+
const globalScssErrors = compileGlobalScss(pkgDir, loadPaths);
|
|
370
|
+
this._scssErrors.push(...globalScssErrors);
|
|
371
|
+
})
|
|
372
|
+
: Promise.resolve(),
|
|
373
|
+
// lint
|
|
374
|
+
this._options.lint === true
|
|
375
|
+
? this._runLint(program, affectedFiles)
|
|
376
|
+
: Promise.resolve(undefined),
|
|
377
|
+
]);
|
|
378
|
+
lintResult = lr;
|
|
379
|
+
} catch (e) {
|
|
380
|
+
crashDiagnostics.push(this._createCrashDiagnostic(e));
|
|
381
|
+
}
|
|
332
382
|
|
|
333
383
|
// 9-5. 상태 저장
|
|
334
384
|
this._builderProgram = builderProgram;
|
|
335
385
|
this._crashContext = undefined;
|
|
336
386
|
|
|
387
|
+
// 결과 조립 (crashDiagnostics 합산)
|
|
388
|
+
const finalDiagnostics = [...(diagResult?.diagnostics ?? []), ...crashDiagnostics];
|
|
389
|
+
const finalErrorCount = (diagResult?.errorCount ?? 0) + crashDiagnostics.length;
|
|
390
|
+
const finalWarningCount = diagResult?.warningCount ?? 0;
|
|
391
|
+
const crashErrors = crashDiagnostics.map((d) =>
|
|
392
|
+
typeof d.messageText === "string" ? d.messageText : "TsCompiler 내부 크래시",
|
|
393
|
+
);
|
|
394
|
+
const finalErrors = [...(diagResult?.errors ?? []), ...crashErrors];
|
|
395
|
+
|
|
337
396
|
this._logger.debug(`[${pkgName}] compileAsync 완료`);
|
|
338
397
|
|
|
339
398
|
return {
|
|
@@ -341,10 +400,10 @@ export class SdTsCompiler {
|
|
|
341
400
|
builderProgram,
|
|
342
401
|
isForAngular,
|
|
343
402
|
affectedFiles,
|
|
344
|
-
diagnostics:
|
|
345
|
-
errorCount:
|
|
346
|
-
warningCount:
|
|
347
|
-
errors:
|
|
403
|
+
diagnostics: finalDiagnostics,
|
|
404
|
+
errorCount: finalErrorCount,
|
|
405
|
+
warningCount: finalWarningCount,
|
|
406
|
+
errors: finalErrors.length > 0 ? finalErrors : undefined,
|
|
348
407
|
ngtscProgram: this._ngtscProgram,
|
|
349
408
|
emitResults,
|
|
350
409
|
lint: lintResult,
|
|
@@ -352,35 +411,8 @@ export class SdTsCompiler {
|
|
|
352
411
|
scssDependencies: new Map(this._scssDependencies),
|
|
353
412
|
};
|
|
354
413
|
} catch (e) {
|
|
355
|
-
//
|
|
356
|
-
const
|
|
357
|
-
const rawMsg = e instanceof Error ? (e.stack ?? e.message) : String(e);
|
|
358
|
-
this._logger.debug(`[${pkgName}] crash @${contextLabel}: ${rawMsg}`);
|
|
359
|
-
|
|
360
|
-
// per-file 프로브: affected 파일 각각을 개별 try-catch로 재체크하여 재현 파일 특정
|
|
361
|
-
const probeReport = isForAngular
|
|
362
|
-
? this._probeCrashPerFileAngular(
|
|
363
|
-
this._ngtscProgram,
|
|
364
|
-
builderProgram,
|
|
365
|
-
this._affectedSourceFiles,
|
|
366
|
-
)
|
|
367
|
-
: this._probeCrashPerFileTsc(builderProgram, this._affectedSourceFiles);
|
|
368
|
-
|
|
369
|
-
const parts: string[] = [
|
|
370
|
-
`TsCompiler 내부 크래시 @${contextLabel}`,
|
|
371
|
-
"",
|
|
372
|
-
rawMsg,
|
|
373
|
-
];
|
|
374
|
-
if (probeReport.length > 0) {
|
|
375
|
-
parts.push("", "크래시 재현 파일 (per-file 프로브):", ...probeReport);
|
|
376
|
-
}
|
|
377
|
-
const message = parts.join("\n");
|
|
378
|
-
|
|
379
|
-
const crashDiag: SerializedDiagnostic = {
|
|
380
|
-
category: ts.DiagnosticCategory.Error,
|
|
381
|
-
code: 0,
|
|
382
|
-
messageText: message,
|
|
383
|
-
};
|
|
414
|
+
// 최종 안전망 — 단계별 catch를 우회한 예상치 못한 에러
|
|
415
|
+
const crashDiag = this._createCrashDiagnostic(e);
|
|
384
416
|
return {
|
|
385
417
|
program,
|
|
386
418
|
builderProgram,
|
|
@@ -389,7 +421,11 @@ export class SdTsCompiler {
|
|
|
389
421
|
diagnostics: [crashDiag],
|
|
390
422
|
errorCount: 1,
|
|
391
423
|
warningCount: 0,
|
|
392
|
-
errors: [
|
|
424
|
+
errors: [
|
|
425
|
+
typeof crashDiag.messageText === "string"
|
|
426
|
+
? crashDiag.messageText
|
|
427
|
+
: "TsCompiler 내부 크래시",
|
|
428
|
+
],
|
|
393
429
|
ngtscProgram: this._ngtscProgram,
|
|
394
430
|
emitResults: undefined,
|
|
395
431
|
lint: undefined,
|
|
@@ -399,72 +435,6 @@ export class SdTsCompiler {
|
|
|
399
435
|
}
|
|
400
436
|
}
|
|
401
437
|
|
|
402
|
-
/**
|
|
403
|
-
* 크래시 발생 후, affected sourceFile을 개별 try-catch로 재검사하여 원인 파일을 특정한다.
|
|
404
|
-
* Angular: `getDiagnosticsForFile(sf, SingleFile)` + `builderProgram.getSemanticDiagnostics(sf)` 개별 호출.
|
|
405
|
-
* 각 파일별로 크래시 재현 여부를 기록. 프로브 자체 크래시는 흡수한다.
|
|
406
|
-
*/
|
|
407
|
-
private _probeCrashPerFileAngular(
|
|
408
|
-
ngtscProgram: NgtscProgram | undefined,
|
|
409
|
-
builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
|
|
410
|
-
affectedSourceFiles: ReadonlySet<ts.SourceFile>,
|
|
411
|
-
): string[] {
|
|
412
|
-
const report: string[] = [];
|
|
413
|
-
const angularCompiler = ngtscProgram?.compiler;
|
|
414
|
-
const { cwd } = this._options;
|
|
415
|
-
|
|
416
|
-
for (const sf of affectedSourceFiles) {
|
|
417
|
-
if (angularCompiler?.ignoreForDiagnostics.has(sf) === true) continue;
|
|
418
|
-
|
|
419
|
-
const rel = path.relative(cwd, sf.fileName);
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
builderProgram.getSemanticDiagnostics(sf);
|
|
423
|
-
} catch (e) {
|
|
424
|
-
report.push(
|
|
425
|
-
` - ${rel} [getSemanticDiagnostics]: ${e instanceof Error ? e.message : String(e)}`,
|
|
426
|
-
);
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
if (angularCompiler != null && !sf.isDeclarationFile) {
|
|
431
|
-
try {
|
|
432
|
-
angularCompiler.getDiagnosticsForFile(sf, OptimizeFor.SingleFile);
|
|
433
|
-
} catch (e) {
|
|
434
|
-
report.push(
|
|
435
|
-
` - ${rel} [getDiagnosticsForFile]: ${e instanceof Error ? e.message : String(e)}`,
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return report;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
private _probeCrashPerFileTsc(
|
|
444
|
-
builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
|
|
445
|
-
affectedSourceFiles: ReadonlySet<ts.SourceFile>,
|
|
446
|
-
): string[] {
|
|
447
|
-
const report: string[] = [];
|
|
448
|
-
const { cwd } = this._options;
|
|
449
|
-
|
|
450
|
-
const targets =
|
|
451
|
-
affectedSourceFiles.size > 0
|
|
452
|
-
? affectedSourceFiles
|
|
453
|
-
: new Set(builderProgram.getSourceFiles());
|
|
454
|
-
|
|
455
|
-
for (const sf of targets) {
|
|
456
|
-
try {
|
|
457
|
-
builderProgram.getSemanticDiagnostics(sf);
|
|
458
|
-
} catch (e) {
|
|
459
|
-
const rel = path.relative(cwd, sf.fileName);
|
|
460
|
-
report.push(
|
|
461
|
-
` - ${rel} [getSemanticDiagnostics]: ${e instanceof Error ? e.message : String(e)}`,
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
return report;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
438
|
private async _runLint(
|
|
469
439
|
program: ts.Program,
|
|
470
440
|
affectedFiles?: ReadonlySet<string>,
|
|
@@ -615,11 +585,16 @@ export class SdTsCompiler {
|
|
|
615
585
|
): ts.Diagnostic[] {
|
|
616
586
|
const diagnostics: ts.Diagnostic[] = [
|
|
617
587
|
...builderProgram.getConfigFileParsingDiagnostics(),
|
|
618
|
-
...builderProgram.getSyntacticDiagnostics(),
|
|
619
588
|
...builderProgram.getOptionsDiagnostics(),
|
|
620
589
|
...builderProgram.getGlobalDiagnostics(),
|
|
621
|
-
...builderProgram.getSemanticDiagnostics(),
|
|
622
590
|
];
|
|
591
|
+
|
|
592
|
+
for (const sourceFile of builderProgram.getSourceFiles()) {
|
|
593
|
+
this._setCrashContext("collectDiagnostics", sourceFile.fileName);
|
|
594
|
+
diagnostics.push(...builderProgram.getSyntacticDiagnostics(sourceFile));
|
|
595
|
+
diagnostics.push(...builderProgram.getSemanticDiagnostics(sourceFile));
|
|
596
|
+
}
|
|
597
|
+
|
|
623
598
|
if (!this._options.output.dts) {
|
|
624
599
|
diagnostics.push(...builderProgram.getProgram().getDeclarationDiagnostics());
|
|
625
600
|
}
|
|
@@ -802,7 +777,13 @@ export class SdTsCompiler {
|
|
|
802
777
|
): ReadonlySet<string> | undefined {
|
|
803
778
|
const affectedFiles = new Set<string>();
|
|
804
779
|
while (true) {
|
|
805
|
-
const result = builderProgram.getSemanticDiagnosticsOfNextAffectedFile(
|
|
780
|
+
const result = builderProgram.getSemanticDiagnosticsOfNextAffectedFile(
|
|
781
|
+
undefined,
|
|
782
|
+
(sourceFile) => {
|
|
783
|
+
this._setCrashContext("findAffectedFiles", sourceFile.fileName);
|
|
784
|
+
return false;
|
|
785
|
+
},
|
|
786
|
+
);
|
|
806
787
|
if (result == null) break;
|
|
807
788
|
if ("fileName" in result.affected) {
|
|
808
789
|
affectedFiles.add(pathx.posix(result.affected.fileName));
|
|
@@ -829,6 +810,7 @@ export class SdTsCompiler {
|
|
|
829
810
|
const result = builderProgram.getSemanticDiagnosticsOfNextAffectedFile(
|
|
830
811
|
undefined,
|
|
831
812
|
(sourceFile) => {
|
|
813
|
+
this._setCrashContext("findAffectedFiles", sourceFile.fileName);
|
|
832
814
|
if (
|
|
833
815
|
angularCompiler.ignoreForDiagnostics.has(sourceFile) &&
|
|
834
816
|
sourceFile.fileName.endsWith(".ngtypecheck.ts")
|
|
@@ -4,7 +4,6 @@ import { SdError } from "@simplysm/core-common";
|
|
|
4
4
|
import { pathx } from "@simplysm/core-node";
|
|
5
5
|
import { createLazyLogger } from "../runtime/lazy-logger";
|
|
6
6
|
import type {
|
|
7
|
-
SdBuildPackageConfig,
|
|
8
7
|
SdPackageConfig,
|
|
9
8
|
} from "../sd-config.types";
|
|
10
9
|
|
|
@@ -90,7 +89,7 @@ export function mergeTestsPackagesIntoConfig(
|
|
|
90
89
|
);
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
merged[name] = { target: "node" }
|
|
92
|
+
merged[name] = { target: "node" };
|
|
94
93
|
pathMap.set(name, relPath);
|
|
95
94
|
}
|
|
96
95
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import { createWorker, FsWatcher } from "@simplysm/core-node";
|
|
3
|
+
import { createWorker, FsWatcher, pathx } from "@simplysm/core-node";
|
|
4
4
|
import { err as errNs } from "@simplysm/core-common";
|
|
5
5
|
import { setupWorkerLifecycle } from "./shared-worker-lifecycle";
|
|
6
6
|
import {
|
|
@@ -208,6 +208,11 @@ function createSourceFileCachePlugin(): esbuild.Plugin {
|
|
|
208
208
|
...typeScriptFileCache.keys(),
|
|
209
209
|
];
|
|
210
210
|
const changedFiles = mtimeTracker.detectChanges(watchTargets);
|
|
211
|
+
const normalizedChangedFiles = new Set<string>();
|
|
212
|
+
for (const file of changedFiles) {
|
|
213
|
+
normalizedChangedFiles.add(pathx.posix(file));
|
|
214
|
+
}
|
|
215
|
+
esbuildResult.sourceFileCache.cycleModifiedFiles = normalizedChangedFiles;
|
|
211
216
|
if (changedFiles.size > 0) {
|
|
212
217
|
esbuildResult.sourceFileCache.invalidate(changedFiles);
|
|
213
218
|
}
|