@simplysm/sd-cli 14.0.49 → 14.0.51
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 +22 -679
- 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.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/docs/angular-vite-plugin/sd-angular-plugin.md +54 -0
- package/docs/config/build-target.md +31 -0
- package/docs/config/npm-config.md +27 -0
- package/docs/config/sd-browser-support-config.md +19 -0
- package/docs/config/sd-build-package-config.md +21 -0
- package/docs/config/sd-capacitor-config.md +109 -0
- package/docs/config/sd-client-package-config.md +33 -0
- package/docs/config/sd-config.md +73 -0
- package/docs/config/sd-electron-config.md +27 -0
- package/docs/config/sd-package-config.md +18 -0
- package/docs/config/sd-post-publish-script-config.md +19 -0
- package/docs/config/sd-publish-config.md +72 -0
- package/docs/config/sd-pwa-config.md +41 -0
- package/docs/config/sd-scripts-package-config.md +19 -0
- package/docs/config/sd-server-package-config.md +32 -0
- package/docs/config/sd-watch-hook-config.md +19 -0
- package/docs/ts-compiler/sd-ts-compiler.md +152 -0
- package/package.json +6 -5
- package/src/engines/EsbuildClientEngine.ts +1 -2
- package/src/esbuild/esbuild-angular-compiler-plugin.ts +1 -1
- 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/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
|
@@ -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
|
|
|
@@ -41,7 +41,7 @@ function setupPlugin(plugin: esbuild.Plugin) {
|
|
|
41
41
|
outputFiles: [],
|
|
42
42
|
metafile: { inputs: {}, outputs: {} },
|
|
43
43
|
...result,
|
|
44
|
-
}
|
|
44
|
+
})) ?? null;
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -73,7 +73,7 @@ function createErrorCompileResult(): ISdTsCompilerResult {
|
|
|
73
73
|
return {
|
|
74
74
|
...createSuccessCompileResult(),
|
|
75
75
|
errors: ["TS2322: Type 'string' is not assignable to type 'number'"],
|
|
76
|
-
diagnostics: [{ category: 1, code: 2322, messageText: "Type mismatch" }]
|
|
76
|
+
diagnostics: [{ category: 1, code: 2322, messageText: "Type mismatch" }],
|
|
77
77
|
errorCount: 1,
|
|
78
78
|
};
|
|
79
79
|
}
|
|
@@ -41,7 +41,7 @@ function setupPlugin(plugin: esbuild.Plugin) {
|
|
|
41
41
|
outputFiles: [],
|
|
42
42
|
metafile: { inputs: {}, outputs: {} },
|
|
43
43
|
...result,
|
|
44
|
-
}
|
|
44
|
+
})) ?? null;
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -147,7 +147,7 @@ describe("createTscPlugin — Unit Tests", () => {
|
|
|
147
147
|
it("env, includeTests 옵션을 SdTsCompiler에 전달한다", async () => {
|
|
148
148
|
const result = createTscPlugin({
|
|
149
149
|
...baseOptions,
|
|
150
|
-
env: "node"
|
|
150
|
+
env: "node",
|
|
151
151
|
includeTests: true,
|
|
152
152
|
});
|
|
153
153
|
const lifecycle = setupPlugin(result.plugin);
|
|
@@ -172,7 +172,7 @@ describe("createTscPlugin — Unit Tests", () => {
|
|
|
172
172
|
];
|
|
173
173
|
mockCompileAsync.mockResolvedValue({
|
|
174
174
|
...createSuccessCompileResult(),
|
|
175
|
-
diagnostics: diagnostics
|
|
175
|
+
diagnostics: diagnostics,
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
const result = createTscPlugin(baseOptions);
|
|
@@ -129,7 +129,7 @@ function createMockWorkerProxy(overrides: Partial<MockWorkerProxy> = {}): MockWo
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
function setupDefaults(config: Partial<SdConfig> = {}): void {
|
|
132
|
-
vi.mocked(loadSdConfig).mockResolvedValue({ packages: {}, ...config }
|
|
132
|
+
vi.mocked(loadSdConfig).mockResolvedValue({ packages: {}, ...config });
|
|
133
133
|
vi.mocked(getVersion).mockResolvedValue("1.0.0");
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -372,7 +372,7 @@ describe("BuildOrchestrator.start", () => {
|
|
|
372
372
|
run: vi.fn().mockRejectedValue(new Error("build failed")),
|
|
373
373
|
startWatch: vi.fn(),
|
|
374
374
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
375
|
-
}
|
|
375
|
+
});
|
|
376
376
|
const mockProxy = createMockWorkerProxy();
|
|
377
377
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
378
378
|
|
|
@@ -413,7 +413,7 @@ describe("BuildOrchestrator.start", () => {
|
|
|
413
413
|
}),
|
|
414
414
|
startWatch: vi.fn(),
|
|
415
415
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
416
|
-
}
|
|
416
|
+
});
|
|
417
417
|
const mockProxy = createMockWorkerProxy();
|
|
418
418
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
419
419
|
|
|
@@ -466,7 +466,7 @@ describe("BuildOrchestrator.start", () => {
|
|
|
466
466
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
467
467
|
};
|
|
468
468
|
mockEngines.push(engine);
|
|
469
|
-
return engine
|
|
469
|
+
return engine;
|
|
470
470
|
});
|
|
471
471
|
const mockProxy = createMockWorkerProxy();
|
|
472
472
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
@@ -496,7 +496,7 @@ describe("BuildOrchestrator.start", () => {
|
|
|
496
496
|
}),
|
|
497
497
|
startWatch: vi.fn(),
|
|
498
498
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
499
|
-
}
|
|
499
|
+
});
|
|
500
500
|
const mockProxy = createMockWorkerProxy();
|
|
501
501
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
502
502
|
|
|
@@ -602,7 +602,7 @@ describe("BuildOrchestrator client build", () => {
|
|
|
602
602
|
}),
|
|
603
603
|
startWatch: vi.fn(),
|
|
604
604
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
605
|
-
}
|
|
605
|
+
});
|
|
606
606
|
const mockProxy = createMockWorkerProxy();
|
|
607
607
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
608
608
|
|
|
@@ -635,7 +635,7 @@ describe("BuildOrchestrator client build", () => {
|
|
|
635
635
|
}),
|
|
636
636
|
startWatch: vi.fn(),
|
|
637
637
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
638
|
-
}
|
|
638
|
+
});
|
|
639
639
|
const mockProxy = createMockWorkerProxy();
|
|
640
640
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
641
641
|
|
|
@@ -720,7 +720,7 @@ describe("BuildOrchestrator client build", () => {
|
|
|
720
720
|
run: vi.fn().mockRejectedValue(new Error("build crashed")),
|
|
721
721
|
startWatch: vi.fn(),
|
|
722
722
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
723
|
-
}
|
|
723
|
+
});
|
|
724
724
|
const mockProxy = createMockWorkerProxy();
|
|
725
725
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
726
726
|
|
|
@@ -954,7 +954,7 @@ describe("BuildOrchestrator native build integration (Slice 1)", () => {
|
|
|
954
954
|
}),
|
|
955
955
|
startWatch: vi.fn(),
|
|
956
956
|
stop: vi.fn().mockResolvedValue(undefined),
|
|
957
|
-
}
|
|
957
|
+
});
|
|
958
958
|
const mockProxy = createMockWorkerProxy();
|
|
959
959
|
vi.mocked(Worker.create).mockReturnValue(mockProxy as any);
|
|
960
960
|
|
|
@@ -187,7 +187,7 @@ function setupEngineWithResult(status: "success" | "error" = "success"): void {
|
|
|
187
187
|
_pkgName: pkg.name,
|
|
188
188
|
};
|
|
189
189
|
mockBuildEngines.push(engine);
|
|
190
|
-
return engine
|
|
190
|
+
return engine;
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -275,7 +275,7 @@ describe("DevOrchestrator", () => {
|
|
|
275
275
|
_pkgName: pkg.name,
|
|
276
276
|
};
|
|
277
277
|
mockBuildEngines.push(engine);
|
|
278
|
-
return engine
|
|
278
|
+
return engine;
|
|
279
279
|
});
|
|
280
280
|
});
|
|
281
281
|
|
|
@@ -471,13 +471,13 @@ describe("DevOrchestrator", () => {
|
|
|
471
471
|
// --- Unit: disposes replaceDeps on shutdown ---
|
|
472
472
|
it("disposes replaceDeps watcher on dev shutdown", async () => {
|
|
473
473
|
const mockDispose = vi.fn();
|
|
474
|
-
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose }
|
|
474
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose });
|
|
475
475
|
|
|
476
476
|
setupDefaults(createConfig({
|
|
477
477
|
packages: { "service-server": { target: "server" } },
|
|
478
478
|
replaceDeps: { "@simplysm/*": "packages/*/src" },
|
|
479
479
|
}));
|
|
480
|
-
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose }
|
|
480
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose });
|
|
481
481
|
setupEngineWithResult("success");
|
|
482
482
|
|
|
483
483
|
const orchestrator = new DevOrchestrator({ targets: [], options: [] });
|
|
@@ -156,7 +156,7 @@ describe("WatchOrchestrator", () => {
|
|
|
156
156
|
_pkgName: pkg.name,
|
|
157
157
|
};
|
|
158
158
|
mockBuildEngines.push(engine);
|
|
159
|
-
return engine
|
|
159
|
+
return engine;
|
|
160
160
|
});
|
|
161
161
|
});
|
|
162
162
|
|
|
@@ -494,7 +494,7 @@ describe("WatchOrchestrator", () => {
|
|
|
494
494
|
|
|
495
495
|
it("disposes replaceDepWatcher even when initialize fails after watchReplaceDeps", async () => {
|
|
496
496
|
const mockDispose = vi.fn();
|
|
497
|
-
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose }
|
|
497
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose });
|
|
498
498
|
|
|
499
499
|
// loadSdConfig succeeds but we'll make classifyWatchPackages fail
|
|
500
500
|
// by having watchReplaceDeps succeed first, then causing a later failure
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SdTsCompiler 크래시 핸들링 — LLM 검증
|
|
2
|
+
|
|
3
|
+
## 검증 항목
|
|
4
|
+
|
|
5
|
+
### 구조적 검증
|
|
6
|
+
|
|
7
|
+
- 단계별 try-catch: compileAsync 내 analyzeAsync, findAffectedFiles, emit, collectDiagnostics, lintAndGlobalScss 각각이 개별 try-catch로 감싸져 있는지 확인
|
|
8
|
+
- 바깥 try-catch 유지: 단계별 try-catch를 감싸는 최종 안전망 try-catch가 존재하는지 확인
|
|
9
|
+
- SdError 사용: catch 블록에서 SdError(원본에러, context)로 감싸는지 확인
|
|
10
|
+
- crashDiagnostics 누적: 각 단계 catch에서 SerializedDiagnostic을 배열에 누적하고, 최종 결과에 합산하는지 확인
|
|
11
|
+
- per-file 프로브 제거: _probeCrashPerFileAngular, _probeCrashPerFileTsc 메서드가 삭제되었는지 확인
|
|
12
|
+
|
|
13
|
+
### 파일 추적 검증
|
|
14
|
+
|
|
15
|
+
- _findAffectedFilesForTsc: ignoreSourceFile 콜백으로 _setCrashContext("findAffectedFiles", sourceFile.fileName)를 호출하는지 확인
|
|
16
|
+
- _findAffectedFilesForAngular: 기존 콜백에 _setCrashContext("findAffectedFiles", sourceFile.fileName) 추가, 기존 ngtypecheck 필터링 로직 유지 확인
|
|
17
|
+
- _collectDiagnosticsForTsc: 파일 단위 루프로 분리되어 _setCrashContext("collectDiagnostics", sourceFile.fileName)를 호출하는지 확인
|
|
18
|
+
- _collectDiagnosticsForAngular: 기존 파일 추적 유지 확인
|
|
19
|
+
|
|
20
|
+
### 단계 간 의존성 검증
|
|
21
|
+
|
|
22
|
+
- analyzeAsync 크래시 시: _ngtscProgram 미설정 → Angular emit/diagnostics에서 null 체크로 스킵하는지 확인
|
|
23
|
+
- findAffectedFiles 크래시 시: affectedFiles=undefined(전체), _affectedSourceFiles 빈 Set → emit/diagnostics가 전체 대상으로 동작하는지 확인
|
|
24
|
+
- _createCrashDiagnostic: SdError cause chain으로 원본 에러 메시지+스택이 보존되는지 확인
|
|
@@ -35,8 +35,8 @@ const distDir = toPosix(path.join(pkgDir, "dist"));
|
|
|
35
35
|
describe("copySrcFiles", () => {
|
|
36
36
|
beforeEach(() => {
|
|
37
37
|
vi.clearAllMocks();
|
|
38
|
-
vi.mocked(fsx.mkdir).mockResolvedValue(undefined
|
|
39
|
-
vi.mocked(fsx.copy).mockResolvedValue(undefined
|
|
38
|
+
vi.mocked(fsx.mkdir).mockResolvedValue(undefined);
|
|
39
|
+
vi.mocked(fsx.copy).mockResolvedValue(undefined);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
it("copies files matching glob patterns preserving relative paths", async () => {
|
|
@@ -84,9 +84,9 @@ describe("watchCopySrcFiles", () => {
|
|
|
84
84
|
beforeEach(() => {
|
|
85
85
|
vi.clearAllMocks();
|
|
86
86
|
vi.mocked(fsx.glob).mockResolvedValue([]);
|
|
87
|
-
vi.mocked(fsx.mkdir).mockResolvedValue(undefined
|
|
88
|
-
vi.mocked(fsx.copy).mockResolvedValue(undefined
|
|
89
|
-
vi.mocked(fsx.rm).mockResolvedValue(undefined
|
|
87
|
+
vi.mocked(fsx.mkdir).mockResolvedValue(undefined);
|
|
88
|
+
vi.mocked(fsx.copy).mockResolvedValue(undefined);
|
|
89
|
+
vi.mocked(fsx.rm).mockResolvedValue(undefined);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
it("performs initial copy then starts watch", async () => {
|