@simplysm/sd-cli 14.0.16 → 14.0.18
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 +2 -1
- package/dist/angular/client-transform-stylesheet.d.ts +2 -0
- package/dist/angular/client-transform-stylesheet.d.ts.map +1 -1
- package/dist/angular/client-transform-stylesheet.js +88 -2
- package/dist/angular/client-transform-stylesheet.js.map +1 -1
- package/dist/angular/vite-angular-plugin.d.ts +7 -0
- package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
- package/dist/angular/vite-angular-plugin.js +78 -16
- package/dist/angular/vite-angular-plugin.js.map +1 -1
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +9 -13
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +8 -9
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +33 -1
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/lint.d.ts +0 -1
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +2 -3
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.js +2 -2
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +0 -1
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/electron/electron.d.ts +3 -2
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +54 -31
- package/dist/electron/electron.js.map +1 -1
- package/dist/engines/BaseEngine.js +1 -1
- package/dist/engines/BaseEngine.js.map +1 -1
- package/dist/engines/NgtscEngine.d.ts.map +1 -1
- package/dist/engines/NgtscEngine.js +0 -1
- package/dist/engines/NgtscEngine.js.map +1 -1
- package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
- package/dist/engines/ServerEsbuildEngine.js +0 -1
- package/dist/engines/ServerEsbuildEngine.js.map +1 -1
- package/dist/engines/TscEngine.d.ts.map +1 -1
- package/dist/engines/TscEngine.js +0 -1
- package/dist/engines/TscEngine.js.map +1 -1
- package/dist/engines/ViteEngine.d.ts.map +1 -1
- package/dist/engines/ViteEngine.js +8 -1
- package/dist/engines/ViteEngine.js.map +1 -1
- package/dist/engines/index.d.ts +0 -10
- package/dist/engines/index.d.ts.map +1 -1
- package/dist/engines/index.js +0 -5
- package/dist/engines/index.js.map +1 -1
- package/dist/engines/types.d.ts +0 -1
- package/dist/engines/types.d.ts.map +1 -1
- package/dist/infra/SignalHandler.d.ts +1 -6
- package/dist/infra/SignalHandler.d.ts.map +1 -1
- package/dist/infra/SignalHandler.js +4 -13
- package/dist/infra/SignalHandler.js.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +7 -12
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.js +17 -10
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +0 -1
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +7 -8
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-config.types.d.ts +11 -1
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/angular-compiler.d.ts.map +1 -1
- package/dist/utils/angular-compiler.js +20 -13
- package/dist/utils/angular-compiler.js.map +1 -1
- package/dist/utils/esbuild-config.d.ts +1 -1
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +1 -4
- package/dist/utils/esbuild-config.js.map +1 -1
- package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
- package/dist/utils/ngtsc-build-core.js +3 -0
- package/dist/utils/ngtsc-build-core.js.map +1 -1
- package/dist/utils/tsc-build.d.ts +5 -0
- package/dist/utils/tsc-build.d.ts.map +1 -1
- package/dist/utils/tsc-build.js +2 -1
- package/dist/utils/tsc-build.js.map +1 -1
- package/dist/utils/vite-config.d.ts +1 -1
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +22 -53
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/vite-pwa-plugin.d.ts +9 -0
- package/dist/utils/vite-pwa-plugin.d.ts.map +1 -0
- package/dist/utils/vite-pwa-plugin.js +139 -0
- package/dist/utils/vite-pwa-plugin.js.map +1 -0
- package/dist/utils/worker-utils.d.ts +2 -5
- package/dist/utils/worker-utils.d.ts.map +1 -1
- package/dist/utils/worker-utils.js +5 -11
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +9 -3
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/library-build.worker.d.ts.map +1 -1
- package/dist/workers/library-build.worker.js +6 -2
- package/dist/workers/library-build.worker.js.map +1 -1
- package/dist/workers/ngtsc-build.worker.js +2 -2
- package/dist/workers/ngtsc-build.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +6 -2
- package/dist/workers/server-build.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.js +4 -4
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/docs/config.md +26 -0
- package/docs/pwa-configuration-types.md +1 -1
- package/package.json +8 -10
- package/src/angular/client-transform-stylesheet.ts +104 -2
- package/src/angular/vite-angular-plugin.ts +92 -31
- package/src/capacitor/capacitor.ts +10 -26
- package/src/commands/check.ts +8 -11
- package/src/commands/device.ts +38 -3
- package/src/commands/lint.ts +2 -3
- package/src/commands/publish.ts +2 -2
- package/src/commands/typecheck.ts +0 -1
- package/src/electron/electron.ts +62 -43
- package/src/engines/BaseEngine.ts +1 -1
- package/src/engines/NgtscEngine.ts +0 -1
- package/src/engines/ServerEsbuildEngine.ts +0 -1
- package/src/engines/TscEngine.ts +0 -1
- package/src/engines/ViteEngine.ts +7 -1
- package/src/engines/index.ts +0 -10
- package/src/engines/types.ts +0 -1
- package/src/infra/SignalHandler.ts +4 -14
- package/src/orchestrators/BuildOrchestrator.ts +7 -9
- package/src/orchestrators/DevWatchOrchestrator.ts +21 -9
- package/src/sd-cli-entry.ts +11 -16
- package/src/sd-config.types.ts +12 -1
- package/src/utils/angular-compiler.ts +21 -21
- package/src/utils/esbuild-config.ts +2 -5
- package/src/utils/ngtsc-build-core.ts +7 -0
- package/src/utils/tsc-build.ts +7 -0
- package/src/utils/vite-config.ts +23 -55
- package/src/utils/vite-pwa-plugin.ts +168 -0
- package/src/utils/worker-utils.ts +5 -11
- package/src/workers/client.worker.ts +11 -3
- package/src/workers/library-build.worker.ts +6 -2
- package/src/workers/ngtsc-build.worker.ts +2 -2
- package/src/workers/server-build.worker.ts +7 -2
- package/src/workers/server-runtime.worker.ts +4 -4
- package/tests/angular/client-transform-stylesheet.spec.ts +43 -0
- package/tests/angular/find-affected-by-scss.spec.ts +37 -0
- package/tests/angular/fixtures/basic-app/scss/_colors.scss +1 -0
- package/tests/angular/fixtures/basic-app/scss/_variables.scss +3 -0
- package/tests/angular/fixtures/basic-app/src/styled.component.ts +14 -0
- package/tests/angular/linker-disk-cache.spec.ts +158 -0
- package/tests/angular/scss-disk-cache.spec.ts +162 -0
- package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +15 -15
- package/tests/angular/vite-angular-plugin-hmr.spec.ts +9 -9
- package/tests/angular/vite-angular-plugin-lint.spec.ts +4 -4
- package/tests/angular/vite-angular-plugin-scss-hmr.spec.ts +87 -0
- package/tests/angular/vite-angular-plugin.spec.ts +15 -15
- package/tests/capacitor/capacitor-icon.spec.ts +2 -4
- package/tests/capacitor/capacitor-init.spec.ts +2 -4
- package/tests/capacitor/capacitor-workspace.spec.ts +2 -4
- package/tests/commands/device.spec.ts +100 -0
- package/tests/electron/electron.spec.ts +24 -17
- package/tests/engines/ngtsc-engine.spec.ts +0 -3
- package/tests/engines/server-esbuild-engine.spec.ts +0 -3
- package/tests/engines/tsc-engine.spec.ts +1 -2
- package/tests/engines/vite-engine.spec.ts +0 -2
- package/tests/infra/signal-handler.spec.ts +1 -12
- package/tests/orchestrators/build-orchestrator.spec.ts +0 -6
- package/tests/orchestrators/dev-watch-orchestrator.spec.ts +24 -66
- package/tests/utils/angular-compiler.spec.ts +1396 -32
- package/tests/utils/esbuild-config.spec.ts +4 -7
- package/tests/utils/{ngtsc-build-core-angular-compiler.spec.ts → ngtsc-build-core.spec.ts} +142 -11
- package/tests/utils/tsc-build.spec.ts +4 -1
- package/tests/utils/vite-config.spec.ts +130 -261
- package/tests/utils/vite-pwa-plugin.acc.spec.ts +143 -0
- package/tests/utils/vite-pwa-plugin.spec.ts +350 -0
- package/tests/utils/worker-utils.spec.ts +8 -7
- package/tests/workers/client-worker.spec.ts +50 -1
- package/tests/workers/dev-port-file.verify.md +6 -0
- package/tests/workers/library-build-lint.spec.ts +1 -1
- package/tests/workers/library-build-worker.spec.ts +1 -1
- package/tests/workers/ngtsc-build-lint.spec.ts +1 -1
- package/tests/workers/server-build-lint.spec.ts +1 -1
- package/tests/workers/server-build-worker.spec.ts +1 -1
- package/tests/workers/server-runtime-worker.spec.ts +8 -1
- package/dist/infra/WorkerManager.d.ts +0 -40
- package/dist/infra/WorkerManager.d.ts.map +0 -1
- package/dist/infra/WorkerManager.js +0 -59
- package/dist/infra/WorkerManager.js.map +0 -1
- package/dist/utils/SdCliReporter.d.ts +0 -18
- package/dist/utils/SdCliReporter.d.ts.map +0 -1
- package/dist/utils/SdCliReporter.js +0 -144
- package/dist/utils/SdCliReporter.js.map +0 -1
- package/src/infra/WorkerManager.ts +0 -65
- package/src/utils/SdCliReporter.ts +0 -177
- package/tests/angular/scss-compiler-async.spec.ts +0 -54
- package/tests/commands/dev.spec.ts +0 -53
- package/tests/commands/watch.spec.ts +0 -53
- package/tests/infra/worker-manager.spec.ts +0 -63
- package/tests/utils/angular-compiler-emit.spec.ts +0 -570
- package/tests/utils/angular-compiler-init.spec.ts +0 -705
- package/tests/utils/angular-compiler-update.spec.ts +0 -293
- package/tests/utils/build-env.spec.ts +0 -33
- package/tests/utils/ngtsc-build-core-transform-stylesheet.spec.ts +0 -124
- package/tests/utils/ngtsc-scss-refactor.spec.ts +0 -47
|
@@ -1,705 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import os from "os";
|
|
6
|
-
|
|
7
|
-
// --- Mock Setup ---
|
|
8
|
-
|
|
9
|
-
const mockAnalyzeAsync = vi.fn().mockResolvedValue(undefined);
|
|
10
|
-
const mockGetDiagnosticsForFile = vi.fn().mockReturnValue([]);
|
|
11
|
-
const mockGetOptionDiagnostics = vi.fn().mockReturnValue([]);
|
|
12
|
-
const mockGetResourceDependencies = vi.fn().mockReturnValue([]);
|
|
13
|
-
const mockIgnoreForDiagnostics = new Set<ts.SourceFile>();
|
|
14
|
-
const mockIgnoreForEmit = new Set<ts.SourceFile>();
|
|
15
|
-
|
|
16
|
-
const ngtscConstructorSpy = vi.fn();
|
|
17
|
-
|
|
18
|
-
// Create a real ts.Program from a temp directory to satisfy TypeScript's BuilderProgram API
|
|
19
|
-
function createRealTsProgram(
|
|
20
|
-
files: Record<string, string> = { "index.ts": "export const x = 1;" },
|
|
21
|
-
): { program: ts.Program; dir: string; rootNames: string[] } {
|
|
22
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "angular-compiler-test-"));
|
|
23
|
-
const rootNames: string[] = [];
|
|
24
|
-
for (const [name, content] of Object.entries(files)) {
|
|
25
|
-
const filePath = path.join(dir, name);
|
|
26
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
27
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
28
|
-
rootNames.push(filePath);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const options: ts.CompilerOptions = {
|
|
32
|
-
target: ts.ScriptTarget.ESNext,
|
|
33
|
-
module: ts.ModuleKind.ESNext,
|
|
34
|
-
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
35
|
-
noEmit: true,
|
|
36
|
-
strict: false,
|
|
37
|
-
skipLibCheck: true,
|
|
38
|
-
types: [],
|
|
39
|
-
};
|
|
40
|
-
const host = ts.createCompilerHost(options);
|
|
41
|
-
const program = ts.createProgram(rootNames, options, host);
|
|
42
|
-
return { program, dir, rootNames };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let realProgram: { program: ts.Program; dir: string; rootNames: string[] };
|
|
46
|
-
|
|
47
|
-
vi.mock("../../src/utils/angular-build", () => {
|
|
48
|
-
class NgtscProgram {
|
|
49
|
-
compiler = {
|
|
50
|
-
analyzeAsync: mockAnalyzeAsync,
|
|
51
|
-
getDiagnosticsForFile: mockGetDiagnosticsForFile,
|
|
52
|
-
getOptionDiagnostics: mockGetOptionDiagnostics,
|
|
53
|
-
getResourceDependencies: mockGetResourceDependencies,
|
|
54
|
-
ignoreForDiagnostics: mockIgnoreForDiagnostics,
|
|
55
|
-
ignoreForEmit: mockIgnoreForEmit,
|
|
56
|
-
};
|
|
57
|
-
constructor(...args: unknown[]) {
|
|
58
|
-
ngtscConstructorSpy(...args);
|
|
59
|
-
}
|
|
60
|
-
getTsProgram() {
|
|
61
|
-
return realProgram.program;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
NgtscProgram,
|
|
66
|
-
OptimizeFor: { WholeProgram: 0, SingleFile: 1 },
|
|
67
|
-
};
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const { AngularCompiler, AngularSourceFileCache } = await import(
|
|
71
|
-
"../../src/utils/angular-compiler"
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
// --- Tests ---
|
|
75
|
-
|
|
76
|
-
beforeEach(() => {
|
|
77
|
-
vi.clearAllMocks();
|
|
78
|
-
mockIgnoreForDiagnostics.clear();
|
|
79
|
-
mockIgnoreForEmit.clear();
|
|
80
|
-
mockGetResourceDependencies.mockReturnValue([]);
|
|
81
|
-
mockGetDiagnosticsForFile.mockReturnValue([]);
|
|
82
|
-
|
|
83
|
-
realProgram = createRealTsProgram();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// --- Unit Tests: Slice 2 — AngularCompiler ---
|
|
87
|
-
|
|
88
|
-
describe("AngularCompiler — Unit Tests", () => {
|
|
89
|
-
it("initialize() 전 getTsProgram()은 에러를 던진다", () => {
|
|
90
|
-
const compiler = new AngularCompiler({
|
|
91
|
-
rootNames: ["src/main.ts"],
|
|
92
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
expect(() => compiler.getTsProgram()).toThrow("initialize()");
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("initialize() 전 compiler getter는 에러를 던진다", () => {
|
|
99
|
-
const compiler = new AngularCompiler({
|
|
100
|
-
rootNames: ["src/main.ts"],
|
|
101
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
expect(() => compiler.compiler).toThrow("initialize()");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("initialize() 전 ngtscProgram은 undefined이다", () => {
|
|
108
|
-
const compiler = new AngularCompiler({
|
|
109
|
-
rootNames: ["src/main.ts"],
|
|
110
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
expect(compiler.ngtscProgram).toBeUndefined();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("host.readResource가 파일 내용을 반환한다", async () => {
|
|
117
|
-
const compiler = new AngularCompiler({
|
|
118
|
-
rootNames: realProgram.rootNames,
|
|
119
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
await compiler.initialize();
|
|
123
|
-
|
|
124
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
125
|
-
// readResource는 host.readFile을 래핑한다
|
|
126
|
-
expect(typeof hostArg["readResource"]).toBe("function");
|
|
127
|
-
const content = (hostArg["readResource"])(realProgram.rootNames[0]);
|
|
128
|
-
expect(content).toContain("export const x = 1");
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("host.transformResource — style이 아닌 type은 null을 반환한다", async () => {
|
|
132
|
-
const transformStylesheet = vi.fn().mockResolvedValue("css");
|
|
133
|
-
const compiler = new AngularCompiler({
|
|
134
|
-
rootNames: realProgram.rootNames,
|
|
135
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
136
|
-
transformStylesheet,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
await compiler.initialize();
|
|
140
|
-
|
|
141
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
142
|
-
const result = await (hostArg["transformResource"])("data", {
|
|
143
|
-
type: "template",
|
|
144
|
-
containingFile: "/app/comp.ts",
|
|
145
|
-
resourceFile: null,
|
|
146
|
-
});
|
|
147
|
-
expect(result).toBeNull();
|
|
148
|
-
expect(transformStylesheet).not.toHaveBeenCalled();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("host.transformResource — 빈 스타일은 빈 content를 반환한다", async () => {
|
|
152
|
-
const transformStylesheet = vi.fn().mockResolvedValue("css");
|
|
153
|
-
const compiler = new AngularCompiler({
|
|
154
|
-
rootNames: realProgram.rootNames,
|
|
155
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
156
|
-
transformStylesheet,
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
await compiler.initialize();
|
|
160
|
-
|
|
161
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
162
|
-
const result = await (hostArg["transformResource"])(" ", {
|
|
163
|
-
type: "style",
|
|
164
|
-
containingFile: "/app/comp.ts",
|
|
165
|
-
resourceFile: null,
|
|
166
|
-
});
|
|
167
|
-
expect(result).toEqual({ content: "" });
|
|
168
|
-
expect(transformStylesheet).not.toHaveBeenCalled();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("host.getModifiedResourceFiles는 sourceFileCache.modifiedFiles를 반환한다", async () => {
|
|
172
|
-
const cache = new AngularSourceFileCache();
|
|
173
|
-
cache.modifiedFiles.add("src/styles.scss");
|
|
174
|
-
|
|
175
|
-
const compiler = new AngularCompiler({
|
|
176
|
-
rootNames: realProgram.rootNames,
|
|
177
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
178
|
-
sourceFileCache: cache,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
await compiler.initialize();
|
|
182
|
-
|
|
183
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
184
|
-
expect(typeof hostArg["getModifiedResourceFiles"]).toBe("function");
|
|
185
|
-
const modifiedFiles = (hostArg["getModifiedResourceFiles"])();
|
|
186
|
-
expect(modifiedFiles.has("src/styles.scss")).toBe(true);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("resourceNameToFileName — 존재하지 않는 파일은 null 반환", async () => {
|
|
190
|
-
const compiler = new AngularCompiler({
|
|
191
|
-
rootNames: realProgram.rootNames,
|
|
192
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
await compiler.initialize();
|
|
196
|
-
|
|
197
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
198
|
-
const result = (hostArg["resourceNameToFileName"])(
|
|
199
|
-
"./nonexistent.html",
|
|
200
|
-
realProgram.rootNames[0],
|
|
201
|
-
);
|
|
202
|
-
expect(result).toBeNull();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("resourceNameToFileName — 존재하는 템플릿 파일은 resolvedPath 반환", async () => {
|
|
206
|
-
const htmlPath = path.join(realProgram.dir, "comp.html");
|
|
207
|
-
fs.writeFileSync(htmlPath, "<div></div>", "utf-8");
|
|
208
|
-
|
|
209
|
-
const compiler = new AngularCompiler({
|
|
210
|
-
rootNames: realProgram.rootNames,
|
|
211
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
await compiler.initialize();
|
|
215
|
-
|
|
216
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
217
|
-
const result = (hostArg["resourceNameToFileName"])(
|
|
218
|
-
"./comp.html",
|
|
219
|
-
realProgram.rootNames[0],
|
|
220
|
-
);
|
|
221
|
-
expect(result).toBe(htmlPath);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("resourceNameToFileName — externalStylesheets와 stylesheet일 때 SHA256 ID 반환", async () => {
|
|
225
|
-
const scssPath = path.join(realProgram.dir, "comp.scss");
|
|
226
|
-
fs.writeFileSync(scssPath, ".x{}", "utf-8");
|
|
227
|
-
|
|
228
|
-
const externalStylesheets = new Map<string, string>();
|
|
229
|
-
const compiler = new AngularCompiler({
|
|
230
|
-
rootNames: realProgram.rootNames,
|
|
231
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
232
|
-
externalStylesheets,
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
await compiler.initialize();
|
|
236
|
-
|
|
237
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
238
|
-
const result = (hostArg["resourceNameToFileName"])(
|
|
239
|
-
"./comp.scss",
|
|
240
|
-
realProgram.rootNames[0],
|
|
241
|
-
);
|
|
242
|
-
// SHA256 ID + .css가 반환되어야 한다
|
|
243
|
-
expect(result).toMatch(/^[a-f0-9]{64}\.css$/);
|
|
244
|
-
// externalStylesheets에 매핑이 저장되어야 한다
|
|
245
|
-
expect(externalStylesheets.has(scssPath)).toBe(true);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it("angularCompilerOptions가 compilerOptions에 병합된다", async () => {
|
|
249
|
-
const compiler = new AngularCompiler({
|
|
250
|
-
rootNames: realProgram.rootNames,
|
|
251
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
252
|
-
angularCompilerOptions: { strictTemplates: true },
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
await compiler.initialize();
|
|
256
|
-
|
|
257
|
-
const passedOptions = ngtscConstructorSpy.mock.calls[0][1] as Record<string, unknown>;
|
|
258
|
-
expect(passedOptions["strictTemplates"]).toBe(true);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("collectDiagnostics() — initialize() 전 호출 시 에러", () => {
|
|
262
|
-
const compiler = new AngularCompiler({
|
|
263
|
-
rootNames: ["src/main.ts"],
|
|
264
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
expect(() => [...compiler.collectDiagnostics()]).toThrow("initialize()");
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// --- Acceptance Tests: Slice 2 — AngularCompiler 초기화 ---
|
|
272
|
-
|
|
273
|
-
describe("AngularCompiler — 초기화", () => {
|
|
274
|
-
// Scenario: 최초 초기화
|
|
275
|
-
it("initialize()로 호스트, NgtscProgram, BuilderProgram이 생성되고 analyzeAsync가 호출된다", async () => {
|
|
276
|
-
const compiler = new AngularCompiler({
|
|
277
|
-
rootNames: ["src/main.ts"],
|
|
278
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
const result = await compiler.initialize();
|
|
282
|
-
|
|
283
|
-
// NgtscProgram이 생성되었다
|
|
284
|
-
expect(ngtscConstructorSpy).toHaveBeenCalledTimes(1);
|
|
285
|
-
// oldProgram은 undefined (최초)
|
|
286
|
-
expect(ngtscConstructorSpy.mock.calls[0][3]).toBeUndefined();
|
|
287
|
-
// analyzeAsync가 호출되었다
|
|
288
|
-
expect(mockAnalyzeAsync).toHaveBeenCalledTimes(1);
|
|
289
|
-
// affectedFiles가 반환된다
|
|
290
|
-
expect(result.affectedFiles).toBeInstanceOf(Set);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// Scenario: 재초기화 (incremental)
|
|
294
|
-
it("두번째 initialize()에서 이전 NgtscProgram이 oldProgram으로 전달된다", async () => {
|
|
295
|
-
const compiler = new AngularCompiler({
|
|
296
|
-
rootNames: ["src/main.ts"],
|
|
297
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
await compiler.initialize();
|
|
301
|
-
const firstProgram = compiler.ngtscProgram;
|
|
302
|
-
|
|
303
|
-
await compiler.initialize();
|
|
304
|
-
|
|
305
|
-
// 두번째 호출에서 oldProgram으로 첫번째 NgtscProgram이 전달됨
|
|
306
|
-
expect(ngtscConstructorSpy).toHaveBeenCalledTimes(2);
|
|
307
|
-
expect(ngtscConstructorSpy.mock.calls[1][3]).toBe(firstProgram);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// Scenario: transformStylesheet 콜백 주입 (library)
|
|
311
|
-
it("transformStylesheet 콜백이 host.transformResource를 통해 호출된다", async () => {
|
|
312
|
-
const transformStylesheet = vi.fn().mockResolvedValue("body { color: red; }");
|
|
313
|
-
|
|
314
|
-
const compiler = new AngularCompiler({
|
|
315
|
-
rootNames: ["src/main.ts"],
|
|
316
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
317
|
-
transformStylesheet,
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
await compiler.initialize();
|
|
321
|
-
|
|
322
|
-
// NgtscProgram 생성 시 전달된 host를 가져온다
|
|
323
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, unknown>;
|
|
324
|
-
expect(typeof hostArg["transformResource"]).toBe("function");
|
|
325
|
-
|
|
326
|
-
// transformResource를 호출하면 transformStylesheet가 호출된다
|
|
327
|
-
const result = await (hostArg["transformResource"] as Function)(
|
|
328
|
-
".button { color: blue; }",
|
|
329
|
-
{ type: "style", containingFile: "/app/comp.ts", resourceFile: "/app/comp.scss" },
|
|
330
|
-
);
|
|
331
|
-
expect(transformStylesheet).toHaveBeenCalledWith(
|
|
332
|
-
".button { color: blue; }",
|
|
333
|
-
"/app/comp.ts",
|
|
334
|
-
"/app/comp.scss",
|
|
335
|
-
);
|
|
336
|
-
expect(result).toEqual({ content: "body { color: red; }" });
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Scenario: compilerOptionsTransformer 적용
|
|
340
|
-
it("compilerOptionsTransformer가 NgtscProgram 생성에 사용된다", async () => {
|
|
341
|
-
const transformer = vi.fn((opts: ts.CompilerOptions) => ({
|
|
342
|
-
...opts,
|
|
343
|
-
noEmit: false,
|
|
344
|
-
declaration: false,
|
|
345
|
-
}));
|
|
346
|
-
|
|
347
|
-
const compiler = new AngularCompiler({
|
|
348
|
-
rootNames: ["src/main.ts"],
|
|
349
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext, noEmit: true },
|
|
350
|
-
compilerOptionsTransformer: transformer,
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
await compiler.initialize();
|
|
354
|
-
|
|
355
|
-
expect(transformer).toHaveBeenCalledTimes(1);
|
|
356
|
-
// NgtscProgram에 전달된 options에 변환이 적용되었는지 확인
|
|
357
|
-
const passedOptions = ngtscConstructorSpy.mock.calls[0][1] as ts.CompilerOptions;
|
|
358
|
-
expect(passedOptions.noEmit).toBe(false);
|
|
359
|
-
expect(passedOptions.declaration).toBe(false);
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
// Scenario: resourceNameToFileName으로 외부 스타일시트 경로 해석
|
|
363
|
-
it("host.resourceNameToFileName이 절대 경로를 반환한다", async () => {
|
|
364
|
-
const compiler = new AngularCompiler({
|
|
365
|
-
rootNames: ["src/main.ts"],
|
|
366
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
await compiler.initialize();
|
|
370
|
-
|
|
371
|
-
const hostArg = ngtscConstructorSpy.mock.calls[0][2] as Record<string, Function>;
|
|
372
|
-
expect(typeof hostArg["resourceNameToFileName"]).toBe("function");
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
// Scenario: declaration true 설정 (library)
|
|
376
|
-
it("compilerOptionsTransformer로 declaration: true 설정", async () => {
|
|
377
|
-
const compiler = new AngularCompiler({
|
|
378
|
-
rootNames: ["src/main.ts"],
|
|
379
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
380
|
-
compilerOptionsTransformer: (opts) => ({
|
|
381
|
-
...opts,
|
|
382
|
-
declaration: true,
|
|
383
|
-
declarationMap: true,
|
|
384
|
-
}),
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
await compiler.initialize();
|
|
388
|
-
|
|
389
|
-
const passedOptions = ngtscConstructorSpy.mock.calls[0][1] as ts.CompilerOptions;
|
|
390
|
-
expect(passedOptions.declaration).toBe(true);
|
|
391
|
-
expect(passedOptions.declarationMap).toBe(true);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
// Scenario: declaration false 설정 (client)
|
|
395
|
-
it("compilerOptionsTransformer로 declaration: false 설정", async () => {
|
|
396
|
-
const compiler = new AngularCompiler({
|
|
397
|
-
rootNames: ["src/main.ts"],
|
|
398
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
399
|
-
compilerOptionsTransformer: (opts) => ({
|
|
400
|
-
...opts,
|
|
401
|
-
declaration: false,
|
|
402
|
-
}),
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
await compiler.initialize();
|
|
406
|
-
|
|
407
|
-
const passedOptions = ngtscConstructorSpy.mock.calls[0][1] as ts.CompilerOptions;
|
|
408
|
-
expect(passedOptions.declaration).toBe(false);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
// Scenario: 초기화 후 ts.Program 획득
|
|
412
|
-
it("getTsProgram()으로 ts.Program을 반환한다", async () => {
|
|
413
|
-
const compiler = new AngularCompiler({
|
|
414
|
-
rootNames: ["src/main.ts"],
|
|
415
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
await compiler.initialize();
|
|
419
|
-
|
|
420
|
-
const program = compiler.getTsProgram();
|
|
421
|
-
expect(program).toBe(realProgram.program);
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
// Scenario: ESLint에 Program 주입 가능
|
|
425
|
-
it("getTsProgram()의 결과가 ESLint parserOptions.programs에 주입 가능한 형태이다", async () => {
|
|
426
|
-
const compiler = new AngularCompiler({
|
|
427
|
-
rootNames: ["src/main.ts"],
|
|
428
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
await compiler.initialize();
|
|
432
|
-
|
|
433
|
-
const program = compiler.getTsProgram();
|
|
434
|
-
// ESLint parserOptions.programs는 ts.Program 배열을 받는다
|
|
435
|
-
const parserOptions = { programs: [program], project: null };
|
|
436
|
-
expect(parserOptions.programs).toHaveLength(1);
|
|
437
|
-
expect(parserOptions.programs[0]).toBe(realProgram.program);
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
// Scenario: SourceFileCache 통합
|
|
441
|
-
it("sourceFileCache 제공 시 augmentHostWithCaching이 적용된다", async () => {
|
|
442
|
-
const cache = new AngularSourceFileCache();
|
|
443
|
-
|
|
444
|
-
const compiler = new AngularCompiler({
|
|
445
|
-
rootNames: ["src/main.ts"],
|
|
446
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
447
|
-
sourceFileCache: cache,
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
await compiler.initialize();
|
|
451
|
-
|
|
452
|
-
// host가 캐시를 사용하도록 래핑되었는지 간접 확인:
|
|
453
|
-
// NgtscProgram이 성공적으로 생성되었으면 호스트가 정상적으로 구성된 것
|
|
454
|
-
expect(ngtscConstructorSpy).toHaveBeenCalledTimes(1);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// Scenario: packageJsonCache — node_modules 변경 시 clear
|
|
458
|
-
it("modifiedFiles에 node_modules 파일이 있으면 packageJsonCache가 clear된다", async () => {
|
|
459
|
-
const cache = new AngularSourceFileCache();
|
|
460
|
-
|
|
461
|
-
const compiler = new AngularCompiler({
|
|
462
|
-
rootNames: ["src/main.ts"],
|
|
463
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
464
|
-
sourceFileCache: cache,
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
// 첫 초기화 — packageJsonCache 생성
|
|
468
|
-
await compiler.initialize();
|
|
469
|
-
|
|
470
|
-
// node_modules 파일 변경
|
|
471
|
-
cache.modifiedFiles.add("node_modules/some-pkg/index.js");
|
|
472
|
-
|
|
473
|
-
// 재초기화 — packageJsonCache.clear() 호출되어야 함
|
|
474
|
-
// 에러 없이 완료되면 성공
|
|
475
|
-
await compiler.initialize();
|
|
476
|
-
|
|
477
|
-
expect(ngtscConstructorSpy).toHaveBeenCalledTimes(2);
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
// Scenario: packageJsonCache — node_modules 미변경 시 재사용
|
|
481
|
-
it("modifiedFiles에 node_modules 파일이 없으면 packageJsonCache가 재사용된다", async () => {
|
|
482
|
-
const cache = new AngularSourceFileCache();
|
|
483
|
-
|
|
484
|
-
const compiler = new AngularCompiler({
|
|
485
|
-
rootNames: ["src/main.ts"],
|
|
486
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
487
|
-
sourceFileCache: cache,
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
// 첫 초기화
|
|
491
|
-
await compiler.initialize();
|
|
492
|
-
|
|
493
|
-
// 일반 파일만 변경
|
|
494
|
-
cache.modifiedFiles.add("src/app/component.ts");
|
|
495
|
-
|
|
496
|
-
// 재초기화
|
|
497
|
-
await compiler.initialize();
|
|
498
|
-
|
|
499
|
-
// 에러 없이 완료
|
|
500
|
-
expect(ngtscConstructorSpy).toHaveBeenCalledTimes(2);
|
|
501
|
-
});
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
// --- Acceptance Tests: Slice 2 — affected 파일 ---
|
|
505
|
-
|
|
506
|
-
describe("AngularCompiler — affected 파일", () => {
|
|
507
|
-
// Scenario: 소스 파일 변경 시 affected 파일 수집
|
|
508
|
-
it("initialize()가 BuilderProgram 기반으로 affected 파일을 반환한다", async () => {
|
|
509
|
-
const compiler = new AngularCompiler({
|
|
510
|
-
rootNames: realProgram.rootNames,
|
|
511
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
const result = await compiler.initialize();
|
|
515
|
-
// affected 파일이 Set으로 반환된다
|
|
516
|
-
expect(result.affectedFiles).toBeInstanceOf(Set);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
// Scenario: 리소스 변경 시 해당 .ts 파일이 affected에 추가
|
|
520
|
-
it("리소스 변경 시 해당 .ts 파일이 affected에 추가된다", async () => {
|
|
521
|
-
// realProgram has index.ts — mock its resource dependencies
|
|
522
|
-
const sourceFiles = realProgram.program.getSourceFiles();
|
|
523
|
-
const indexFile = sourceFiles.find((sf) => sf.fileName.includes("index.ts"));
|
|
524
|
-
const scssPath = path.join(realProgram.dir, "component.scss");
|
|
525
|
-
fs.writeFileSync(scssPath, ".x { }", "utf-8");
|
|
526
|
-
|
|
527
|
-
mockGetResourceDependencies.mockImplementation((sf: ts.SourceFile) => {
|
|
528
|
-
if (sf === indexFile) {
|
|
529
|
-
return [scssPath];
|
|
530
|
-
}
|
|
531
|
-
return [];
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
const cache = new AngularSourceFileCache();
|
|
535
|
-
cache.modifiedFiles.add(scssPath);
|
|
536
|
-
|
|
537
|
-
const compiler = new AngularCompiler({
|
|
538
|
-
rootNames: realProgram.rootNames,
|
|
539
|
-
compilerOptions: { target: ts.ScriptTarget.ESNext },
|
|
540
|
-
sourceFileCache: cache,
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
const result = await compiler.initialize();
|
|
544
|
-
// index.ts가 affected에 추가되어야 한다 (리소스 변경으로 인해)
|
|
545
|
-
const affectedFileNames = [...result.affectedFiles].map((sf) => sf.fileName);
|
|
546
|
-
expect(affectedFileNames.some((f) => f.includes("index.ts"))).toBe(true);
|
|
547
|
-
});
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
// --- Acceptance Tests: Slice 3 — collectDiagnostics ---
|
|
551
|
-
|
|
552
|
-
describe("AngularCompiler — collectDiagnostics", () => {
|
|
553
|
-
// Scenario: 4종 진단 수집
|
|
554
|
-
it("collectDiagnostics가 Option, Syntactic, Semantic, Angular 진단을 수집한다", async () => {
|
|
555
|
-
const compiler = new AngularCompiler({
|
|
556
|
-
rootNames: realProgram.rootNames,
|
|
557
|
-
compilerOptions: {
|
|
558
|
-
target: ts.ScriptTarget.ESNext,
|
|
559
|
-
module: ts.ModuleKind.ESNext,
|
|
560
|
-
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
561
|
-
skipLibCheck: true,
|
|
562
|
-
types: [],
|
|
563
|
-
},
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
await compiler.initialize();
|
|
567
|
-
|
|
568
|
-
// collectDiagnostics가 에러 없이 실행된다
|
|
569
|
-
const diagnostics = [...compiler.collectDiagnostics()];
|
|
570
|
-
expect(Array.isArray(diagnostics)).toBe(true);
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
// Scenario: affected 파일의 Angular 진단은 재계산 후 캐시
|
|
574
|
-
it("affected 파일의 Angular 진단은 getDiagnosticsForFile로 재계산된다", async () => {
|
|
575
|
-
const indexFile = realProgram.program.getSourceFiles()
|
|
576
|
-
.find((sf) => sf.fileName.includes("index.ts"));
|
|
577
|
-
const scssPath = path.join(realProgram.dir, "component.scss");
|
|
578
|
-
fs.writeFileSync(scssPath, ".x { }", "utf-8");
|
|
579
|
-
|
|
580
|
-
const mockDiag = {
|
|
581
|
-
category: ts.DiagnosticCategory.Error,
|
|
582
|
-
code: 1000,
|
|
583
|
-
messageText: "test error",
|
|
584
|
-
file: indexFile,
|
|
585
|
-
start: 0,
|
|
586
|
-
length: 5,
|
|
587
|
-
} as ts.Diagnostic;
|
|
588
|
-
mockGetDiagnosticsForFile.mockReturnValue([mockDiag]);
|
|
589
|
-
mockGetResourceDependencies.mockImplementation((sf: ts.SourceFile) => {
|
|
590
|
-
if (sf.fileName.includes("index.ts")) {
|
|
591
|
-
return [scssPath];
|
|
592
|
-
}
|
|
593
|
-
return [];
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
const cache = new AngularSourceFileCache();
|
|
597
|
-
cache.modifiedFiles.add(scssPath);
|
|
598
|
-
|
|
599
|
-
const compiler = new AngularCompiler({
|
|
600
|
-
rootNames: realProgram.rootNames,
|
|
601
|
-
compilerOptions: {
|
|
602
|
-
target: ts.ScriptTarget.ESNext,
|
|
603
|
-
module: ts.ModuleKind.ESNext,
|
|
604
|
-
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
605
|
-
skipLibCheck: true,
|
|
606
|
-
types: [],
|
|
607
|
-
},
|
|
608
|
-
sourceFileCache: cache,
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
await compiler.initialize();
|
|
612
|
-
|
|
613
|
-
const diagnostics = [...compiler.collectDiagnostics()];
|
|
614
|
-
// getDiagnosticsForFile이 호출되었다
|
|
615
|
-
expect(mockGetDiagnosticsForFile).toHaveBeenCalled();
|
|
616
|
-
// 진단이 수집되었다
|
|
617
|
-
expect(diagnostics.some((d) => d.code === 1000)).toBe(true);
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
// Scenario: non-affected 파일의 Angular 진단은 캐시 반환
|
|
621
|
-
it("non-affected 파일의 Angular 진단은 캐시에서 반환된다 (첫 빌드에서 모든 파일이 affected)", async () => {
|
|
622
|
-
const mockDiag = {
|
|
623
|
-
category: ts.DiagnosticCategory.Warning,
|
|
624
|
-
code: 2000,
|
|
625
|
-
messageText: "cached warning",
|
|
626
|
-
} as ts.Diagnostic;
|
|
627
|
-
mockGetDiagnosticsForFile.mockReturnValue([mockDiag]);
|
|
628
|
-
|
|
629
|
-
const compiler = new AngularCompiler({
|
|
630
|
-
rootNames: realProgram.rootNames,
|
|
631
|
-
compilerOptions: {
|
|
632
|
-
target: ts.ScriptTarget.ESNext,
|
|
633
|
-
module: ts.ModuleKind.ESNext,
|
|
634
|
-
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
635
|
-
skipLibCheck: true,
|
|
636
|
-
types: [],
|
|
637
|
-
},
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
await compiler.initialize();
|
|
641
|
-
|
|
642
|
-
// 첫 collectDiagnostics — 초기 빌드에서 모든 파일이 affected → getDiagnosticsForFile 호출됨
|
|
643
|
-
const diags1 = [...compiler.collectDiagnostics()];
|
|
644
|
-
const callCount1 = mockGetDiagnosticsForFile.mock.calls.length;
|
|
645
|
-
expect(callCount1).toBeGreaterThan(0);
|
|
646
|
-
expect(diags1.some((d) => d.code === 2000)).toBe(true);
|
|
647
|
-
|
|
648
|
-
// 두번째 initialize + collectDiagnostics — 변경 없으면 캐시 사용
|
|
649
|
-
mockGetDiagnosticsForFile.mockClear();
|
|
650
|
-
await compiler.initialize();
|
|
651
|
-
const diags2 = [...compiler.collectDiagnostics()];
|
|
652
|
-
// 두번째에서는 변경이 없으므로 affected가 비어 있어야 하고, 캐시에서 반환
|
|
653
|
-
// (하지만 BuilderProgram은 재생성되므로 affected가 다시 계산될 수 있다)
|
|
654
|
-
// 최소한 에러 없이 동작해야 한다
|
|
655
|
-
expect(Array.isArray(diags2)).toBe(true);
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
// Scenario: 리소스 변경 시 해당 .ts 파일의 캐시 무효화
|
|
659
|
-
it("리소스 변경 시 해당 .ts 파일의 diagnosticCache가 무효화된다", async () => {
|
|
660
|
-
const scssPath = path.join(realProgram.dir, "styles.scss");
|
|
661
|
-
fs.writeFileSync(scssPath, ".y { }", "utf-8");
|
|
662
|
-
|
|
663
|
-
const mockDiag = {
|
|
664
|
-
category: ts.DiagnosticCategory.Error,
|
|
665
|
-
code: 3000,
|
|
666
|
-
messageText: "resource error",
|
|
667
|
-
} as ts.Diagnostic;
|
|
668
|
-
mockGetDiagnosticsForFile.mockReturnValue([mockDiag]);
|
|
669
|
-
mockGetResourceDependencies.mockImplementation((sf: ts.SourceFile) => {
|
|
670
|
-
if (sf.fileName.includes("index.ts")) {
|
|
671
|
-
return [scssPath];
|
|
672
|
-
}
|
|
673
|
-
return [];
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
const cache = new AngularSourceFileCache();
|
|
677
|
-
|
|
678
|
-
const compiler = new AngularCompiler({
|
|
679
|
-
rootNames: realProgram.rootNames,
|
|
680
|
-
compilerOptions: {
|
|
681
|
-
target: ts.ScriptTarget.ESNext,
|
|
682
|
-
module: ts.ModuleKind.ESNext,
|
|
683
|
-
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
684
|
-
skipLibCheck: true,
|
|
685
|
-
types: [],
|
|
686
|
-
},
|
|
687
|
-
sourceFileCache: cache,
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
// 첫 빌드
|
|
691
|
-
await compiler.initialize();
|
|
692
|
-
void [...compiler.collectDiagnostics()];
|
|
693
|
-
expect(mockGetDiagnosticsForFile).toHaveBeenCalled();
|
|
694
|
-
|
|
695
|
-
// 리소스 변경 후 재빌드
|
|
696
|
-
mockGetDiagnosticsForFile.mockClear();
|
|
697
|
-
cache.modifiedFiles.add(scssPath);
|
|
698
|
-
|
|
699
|
-
await compiler.initialize();
|
|
700
|
-
void [...compiler.collectDiagnostics()];
|
|
701
|
-
|
|
702
|
-
// 리소스 변경으로 캐시 무효화 → getDiagnosticsForFile이 다시 호출되어야 한다
|
|
703
|
-
expect(mockGetDiagnosticsForFile).toHaveBeenCalled();
|
|
704
|
-
});
|
|
705
|
-
});
|