@simplysm/sd-cli 14.0.16 → 14.0.17
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,10 +1,10 @@
|
|
|
1
1
|
import { createWorker } from "@simplysm/core-node";
|
|
2
|
-
import { err as errNs } from "@simplysm/core-common";
|
|
2
|
+
import { env, err as errNs } from "@simplysm/core-common";
|
|
3
3
|
import { consola } from "consola";
|
|
4
4
|
import proxy from "@fastify/http-proxy";
|
|
5
5
|
import net from "net";
|
|
6
6
|
import { pathToFileURL } from "url";
|
|
7
|
-
import { registerCleanupHandlers,
|
|
7
|
+
import { registerCleanupHandlers, setupWorkerConsola } from "../utils/worker-utils";
|
|
8
8
|
|
|
9
9
|
//#region Types
|
|
10
10
|
|
|
@@ -42,7 +42,7 @@ export interface ServerRuntimeWorkerEvents extends Record<string, unknown> {
|
|
|
42
42
|
|
|
43
43
|
//#endregion
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
setupWorkerConsola();
|
|
46
46
|
|
|
47
47
|
const logger = consola.withTag("sd:cli:server-runtime:worker");
|
|
48
48
|
|
|
@@ -122,7 +122,7 @@ async function start(info: ServerRuntimeStartInfo): Promise<void> {
|
|
|
122
122
|
// main.js import 전에 환경변수를 process.env에 주입
|
|
123
123
|
if (info.env != null) {
|
|
124
124
|
for (const [key, value] of Object.entries(info.env)) {
|
|
125
|
-
|
|
125
|
+
env(key, value);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -3,6 +3,7 @@ import path from "path";
|
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import os from "os";
|
|
5
5
|
import { createClientTransformStylesheet } from "../../src/angular/client-transform-stylesheet.js";
|
|
6
|
+
import { compileScssFileAsync, compileScssStringAsync } from "../../src/utils/scss-compiler.js";
|
|
6
7
|
|
|
7
8
|
const TMP_DIR = path.join(os.tmpdir(), "sd-cli-stylesheet-test");
|
|
8
9
|
|
|
@@ -151,3 +152,45 @@ describe("createClientTransformStylesheet", () => {
|
|
|
151
152
|
expect(deps.size).toBeGreaterThan(0);
|
|
152
153
|
});
|
|
153
154
|
});
|
|
155
|
+
|
|
156
|
+
// ─── scss-compiler async (low-level) ───
|
|
157
|
+
|
|
158
|
+
describe("scss-compiler async", () => {
|
|
159
|
+
// Scenario: 외부 .scss 파일 변환
|
|
160
|
+
it("compiles external .scss file asynchronously", async () => {
|
|
161
|
+
ensureTmpDir();
|
|
162
|
+
const scssPath = path.join(TMP_DIR, "test.scss");
|
|
163
|
+
fs.writeFileSync(scssPath, "$color: red;\n.host { color: $color; }");
|
|
164
|
+
|
|
165
|
+
const result = await compileScssFileAsync(scssPath, []);
|
|
166
|
+
|
|
167
|
+
expect(result.css).toContain("color: red");
|
|
168
|
+
expect(result.css).not.toContain("$color");
|
|
169
|
+
expect(result.dependencies).toBeInstanceOf(Array);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Scenario: 인라인 SCSS 문자열 변환
|
|
173
|
+
it("compiles inline SCSS string asynchronously", async () => {
|
|
174
|
+
ensureTmpDir();
|
|
175
|
+
const containingFile = path.join(TMP_DIR, "component.ts");
|
|
176
|
+
|
|
177
|
+
const result = await compileScssStringAsync(
|
|
178
|
+
"$size: 16px;\n.text { font-size: $size; }",
|
|
179
|
+
containingFile,
|
|
180
|
+
[],
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
expect(result.css).toContain("font-size: 16px");
|
|
184
|
+
expect(result.css).not.toContain("$size");
|
|
185
|
+
expect(result.dependencies).toBeInstanceOf(Array);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Scenario: SCSS 컴파일 에러 시 에러
|
|
189
|
+
it("throws on invalid SCSS syntax", async () => {
|
|
190
|
+
ensureTmpDir();
|
|
191
|
+
const scssPath = path.join(TMP_DIR, "invalid.scss");
|
|
192
|
+
fs.writeFileSync(scssPath, ".host { @include nonexistent-mixin(); }");
|
|
193
|
+
|
|
194
|
+
await expect(compileScssFileAsync(scssPath, [])).rejects.toThrow();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { findAffectedByScss } from "../../src/angular/vite-angular-plugin.js";
|
|
3
|
+
|
|
4
|
+
describe("findAffectedByScss", () => {
|
|
5
|
+
it("returns owner file when SCSS is in its dependency set", () => {
|
|
6
|
+
const deps = new Map<string, Set<string>>();
|
|
7
|
+
deps.set("/app/src/comp.ts", new Set(["/app/scss/_vars.scss"]));
|
|
8
|
+
|
|
9
|
+
const result = findAffectedByScss("/app/scss/_vars.scss", deps);
|
|
10
|
+
expect(result).toEqual(["/app/src/comp.ts"]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("returns multiple owners when multiple components depend on the same SCSS", () => {
|
|
14
|
+
const deps = new Map<string, Set<string>>();
|
|
15
|
+
deps.set("/app/src/comp-a.ts", new Set(["/app/scss/_vars.scss"]));
|
|
16
|
+
deps.set("/app/src/comp-b.ts", new Set(["/app/scss/_vars.scss", "/app/scss/_extra.scss"]));
|
|
17
|
+
deps.set("/app/src/comp-c.ts", new Set(["/app/scss/_other.scss"]));
|
|
18
|
+
|
|
19
|
+
const result = findAffectedByScss("/app/scss/_vars.scss", deps);
|
|
20
|
+
expect(result).toEqual(expect.arrayContaining(["/app/src/comp-a.ts", "/app/src/comp-b.ts"]));
|
|
21
|
+
expect(result).not.toContain("/app/src/comp-c.ts");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("returns empty array when no component depends on the SCSS", () => {
|
|
25
|
+
const deps = new Map<string, Set<string>>();
|
|
26
|
+
deps.set("/app/src/comp.ts", new Set(["/app/scss/_vars.scss"]));
|
|
27
|
+
|
|
28
|
+
const result = findAffectedByScss("/app/scss/_unknown.scss", deps);
|
|
29
|
+
expect(result).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("returns empty array when scssDependencies is empty", () => {
|
|
33
|
+
const deps = new Map<string, Set<string>>();
|
|
34
|
+
const result = findAffectedByScss("/app/scss/_vars.scss", deps);
|
|
35
|
+
expect(result).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$base: blue;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Component } from "@angular/core";
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: "app-styled",
|
|
5
|
+
standalone: true,
|
|
6
|
+
template: `<p>styled</p>`,
|
|
7
|
+
styles: [`
|
|
8
|
+
@use 'variables' as vars;
|
|
9
|
+
:host {
|
|
10
|
+
color: vars.$primary;
|
|
11
|
+
}
|
|
12
|
+
`],
|
|
13
|
+
})
|
|
14
|
+
export class StyledComponent {}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
|
|
6
|
+
const mockTransformFile = vi.fn(() => Promise.resolve("transformed-code"));
|
|
7
|
+
|
|
8
|
+
vi.mock("@angular/build/private", () => ({
|
|
9
|
+
JavaScriptTransformer: class MockJSTransformer {
|
|
10
|
+
transformFile = mockTransformFile;
|
|
11
|
+
transformData = vi.fn(() => Promise.resolve(new TextEncoder().encode("transformed")));
|
|
12
|
+
close = vi.fn(() => Promise.resolve());
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const { sdAngularPlugin } = await import("../../src/angular/vite-angular-plugin.js");
|
|
17
|
+
|
|
18
|
+
const TMP_DIR = path.join(os.tmpdir(), "sd-cli-linker-cache-test");
|
|
19
|
+
const CACHE_DIR = path.join(TMP_DIR, "cache");
|
|
20
|
+
|
|
21
|
+
type LoadHandler = (id: string) => Promise<string | null>;
|
|
22
|
+
|
|
23
|
+
function getLoadHandler(plugin: ReturnType<typeof sdAngularPlugin>): LoadHandler {
|
|
24
|
+
const config = (plugin as any).config();
|
|
25
|
+
const rolldownPlugin = config.optimizeDeps.rolldownOptions.plugins[0] as {
|
|
26
|
+
load: (id: string) => Promise<string | null>;
|
|
27
|
+
};
|
|
28
|
+
return (id: string) => rolldownPlugin.load(id);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe("Linker disk cache (optimizeDeps)", () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
fs.mkdirSync(TMP_DIR, { recursive: true });
|
|
34
|
+
mockTransformFile.mockClear();
|
|
35
|
+
mockTransformFile.mockResolvedValue("transformed-code");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
fs.rmSync(TMP_DIR, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Acceptance: cache miss → transform + store, cache hit → load from disk
|
|
43
|
+
it("caches transformFile result on miss and returns cached on hit", async () => {
|
|
44
|
+
const jsFile = path.join(TMP_DIR, "module.js");
|
|
45
|
+
fs.writeFileSync(jsFile, "export const x = 1;");
|
|
46
|
+
|
|
47
|
+
const plugin = sdAngularPlugin({
|
|
48
|
+
tsconfig: path.join(TMP_DIR, "tsconfig.json"),
|
|
49
|
+
dev: true,
|
|
50
|
+
linkerCacheDir: CACHE_DIR,
|
|
51
|
+
});
|
|
52
|
+
const load = getLoadHandler(plugin);
|
|
53
|
+
|
|
54
|
+
// First call: cache miss
|
|
55
|
+
const result1 = await load(jsFile);
|
|
56
|
+
expect(mockTransformFile).toHaveBeenCalledOnce();
|
|
57
|
+
expect(result1).toBe("transformed-code");
|
|
58
|
+
|
|
59
|
+
// Cache file created
|
|
60
|
+
const cacheFiles = fs.readdirSync(CACHE_DIR);
|
|
61
|
+
expect(cacheFiles.length).toBe(1);
|
|
62
|
+
expect(cacheFiles[0]).toMatch(/^[a-f0-9]+\.js$/);
|
|
63
|
+
|
|
64
|
+
// Second call: cache hit
|
|
65
|
+
mockTransformFile.mockClear();
|
|
66
|
+
const result2 = await load(jsFile);
|
|
67
|
+
expect(mockTransformFile).not.toHaveBeenCalled();
|
|
68
|
+
expect(result2).toBe("transformed-code");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Acceptance: file content change → cache miss
|
|
72
|
+
it("invalidates cache when file content changes", async () => {
|
|
73
|
+
const jsFile = path.join(TMP_DIR, "changing.js");
|
|
74
|
+
fs.writeFileSync(jsFile, "export const v = 1;");
|
|
75
|
+
|
|
76
|
+
const plugin = sdAngularPlugin({
|
|
77
|
+
tsconfig: path.join(TMP_DIR, "tsconfig.json"),
|
|
78
|
+
dev: true,
|
|
79
|
+
linkerCacheDir: CACHE_DIR,
|
|
80
|
+
});
|
|
81
|
+
const load = getLoadHandler(plugin);
|
|
82
|
+
|
|
83
|
+
await load(jsFile);
|
|
84
|
+
expect(mockTransformFile).toHaveBeenCalledOnce();
|
|
85
|
+
|
|
86
|
+
// Change content
|
|
87
|
+
fs.writeFileSync(jsFile, "export const v = 2;");
|
|
88
|
+
mockTransformFile.mockClear();
|
|
89
|
+
mockTransformFile.mockResolvedValue("transformed-v2");
|
|
90
|
+
|
|
91
|
+
const result = await load(jsFile);
|
|
92
|
+
expect(mockTransformFile).toHaveBeenCalledOnce();
|
|
93
|
+
expect(result).toBe("transformed-v2");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Unit: corrupted cache file → graceful fallback
|
|
97
|
+
it("falls back to transform when cache file is corrupted", async () => {
|
|
98
|
+
const jsFile = path.join(TMP_DIR, "fallback.js");
|
|
99
|
+
fs.writeFileSync(jsFile, "export const y = 1;");
|
|
100
|
+
|
|
101
|
+
const plugin = sdAngularPlugin({
|
|
102
|
+
tsconfig: path.join(TMP_DIR, "tsconfig.json"),
|
|
103
|
+
dev: true,
|
|
104
|
+
linkerCacheDir: CACHE_DIR,
|
|
105
|
+
});
|
|
106
|
+
const load = getLoadHandler(plugin);
|
|
107
|
+
|
|
108
|
+
// First call to populate cache
|
|
109
|
+
await load(jsFile);
|
|
110
|
+
|
|
111
|
+
// Remove the cache file to simulate corruption/missing cache
|
|
112
|
+
const cacheFiles = fs.readdirSync(CACHE_DIR);
|
|
113
|
+
fs.rmSync(path.join(CACHE_DIR, cacheFiles[0]));
|
|
114
|
+
|
|
115
|
+
mockTransformFile.mockClear();
|
|
116
|
+
const result = await load(jsFile);
|
|
117
|
+
expect(mockTransformFile).toHaveBeenCalledOnce();
|
|
118
|
+
expect(result).toBe("transformed-code");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Unit: Uint8Array result from transformFile is handled
|
|
122
|
+
it("handles Uint8Array result from transformFile", async () => {
|
|
123
|
+
const jsFile = path.join(TMP_DIR, "uint8.js");
|
|
124
|
+
fs.writeFileSync(jsFile, "export const z = 1;");
|
|
125
|
+
|
|
126
|
+
mockTransformFile.mockResolvedValue(new TextEncoder().encode("uint8-transformed") as unknown as string);
|
|
127
|
+
|
|
128
|
+
const plugin = sdAngularPlugin({
|
|
129
|
+
tsconfig: path.join(TMP_DIR, "tsconfig.json"),
|
|
130
|
+
dev: true,
|
|
131
|
+
linkerCacheDir: CACHE_DIR,
|
|
132
|
+
});
|
|
133
|
+
const load = getLoadHandler(plugin);
|
|
134
|
+
|
|
135
|
+
const result = await load(jsFile);
|
|
136
|
+
expect(result).toBe("uint8-transformed");
|
|
137
|
+
|
|
138
|
+
// Second call: cache hit should also return string
|
|
139
|
+
mockTransformFile.mockClear();
|
|
140
|
+
const result2 = await load(jsFile);
|
|
141
|
+
expect(mockTransformFile).not.toHaveBeenCalled();
|
|
142
|
+
expect(result2).toBe("uint8-transformed");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Unit: non-.js file returns null (filter)
|
|
146
|
+
it("returns null for non-js files", async () => {
|
|
147
|
+
const plugin = sdAngularPlugin({
|
|
148
|
+
tsconfig: path.join(TMP_DIR, "tsconfig.json"),
|
|
149
|
+
dev: true,
|
|
150
|
+
linkerCacheDir: CACHE_DIR,
|
|
151
|
+
});
|
|
152
|
+
const load = getLoadHandler(plugin);
|
|
153
|
+
|
|
154
|
+
const result = await load(path.join(TMP_DIR, "data.json"));
|
|
155
|
+
expect(result).toBeNull();
|
|
156
|
+
expect(mockTransformFile).not.toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { createClientTransformStylesheet } from "../../src/angular/client-transform-stylesheet.js";
|
|
6
|
+
|
|
7
|
+
const TMP_DIR = path.join(os.tmpdir(), "sd-cli-scss-cache-test");
|
|
8
|
+
const CACHE_DIR = path.join(TMP_DIR, "scss-cache");
|
|
9
|
+
|
|
10
|
+
function ensureTmpDir(): void {
|
|
11
|
+
fs.mkdirSync(TMP_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("SCSS disk cache", () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
ensureTmpDir();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
fs.rmSync(TMP_DIR, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Acceptance: cache miss → compile + store, cache hit → load from disk
|
|
24
|
+
it("caches SCSS compile result on miss and returns cached on hit", async () => {
|
|
25
|
+
const scssPath = path.join(TMP_DIR, "component.scss");
|
|
26
|
+
fs.writeFileSync(scssPath, "$color: blue;\n.host { color: $color; }");
|
|
27
|
+
|
|
28
|
+
const deps = new Map<string, Set<string>>();
|
|
29
|
+
const transform = createClientTransformStylesheet({
|
|
30
|
+
loadPaths: [],
|
|
31
|
+
scssErrors: [],
|
|
32
|
+
scssDependencies: deps,
|
|
33
|
+
cacheDir: CACHE_DIR,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// First call: compile (cache miss)
|
|
37
|
+
const result1 = await transform("", path.join(TMP_DIR, "component.ts"), scssPath);
|
|
38
|
+
expect(result1).toContain("color: blue");
|
|
39
|
+
|
|
40
|
+
// Cache file should exist
|
|
41
|
+
const cacheFiles = fs.readdirSync(CACHE_DIR);
|
|
42
|
+
expect(cacheFiles.length).toBe(1);
|
|
43
|
+
|
|
44
|
+
// Second call: cache hit (same file)
|
|
45
|
+
deps.clear();
|
|
46
|
+
const result2 = await transform("", path.join(TMP_DIR, "component.ts"), scssPath);
|
|
47
|
+
expect(result2).toBe(result1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Acceptance: dependency change → cache miss
|
|
51
|
+
it("invalidates cache when dependency file changes", async () => {
|
|
52
|
+
const partialPath = path.join(TMP_DIR, "_vars.scss");
|
|
53
|
+
fs.writeFileSync(partialPath, "$color: green;");
|
|
54
|
+
const scssPath = path.join(TMP_DIR, "with-dep.scss");
|
|
55
|
+
fs.writeFileSync(scssPath, '@use "vars";\n.host { color: vars.$color; }');
|
|
56
|
+
|
|
57
|
+
const deps = new Map<string, Set<string>>();
|
|
58
|
+
const transform = createClientTransformStylesheet({
|
|
59
|
+
loadPaths: [TMP_DIR],
|
|
60
|
+
scssErrors: [],
|
|
61
|
+
scssDependencies: deps,
|
|
62
|
+
cacheDir: CACHE_DIR,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// First call
|
|
66
|
+
const result1 = await transform("", path.join(TMP_DIR, "comp.ts"), scssPath);
|
|
67
|
+
expect(result1).toContain("color: green");
|
|
68
|
+
|
|
69
|
+
// Change dependency content
|
|
70
|
+
fs.writeFileSync(partialPath, "$color: red;");
|
|
71
|
+
|
|
72
|
+
const result2 = await transform("", path.join(TMP_DIR, "comp.ts"), scssPath);
|
|
73
|
+
expect(result2).toContain("color: red");
|
|
74
|
+
expect(result2).not.toContain("color: green");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Acceptance: scssDependencies populated on cache hit
|
|
78
|
+
it("populates scssDependencies from cache on hit", async () => {
|
|
79
|
+
const partialPath = path.join(TMP_DIR, "_shared.scss");
|
|
80
|
+
fs.writeFileSync(partialPath, "$size: 14px;");
|
|
81
|
+
const scssPath = path.join(TMP_DIR, "deptest.scss");
|
|
82
|
+
fs.writeFileSync(scssPath, '@use "shared";\n.text { font-size: shared.$size; }');
|
|
83
|
+
|
|
84
|
+
const deps = new Map<string, Set<string>>();
|
|
85
|
+
const transform = createClientTransformStylesheet({
|
|
86
|
+
loadPaths: [TMP_DIR],
|
|
87
|
+
scssErrors: [],
|
|
88
|
+
scssDependencies: deps,
|
|
89
|
+
cacheDir: CACHE_DIR,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// First call: populate deps from compilation
|
|
93
|
+
await transform("", path.join(TMP_DIR, "comp.ts"), scssPath);
|
|
94
|
+
expect(deps.size).toBeGreaterThan(0);
|
|
95
|
+
|
|
96
|
+
// Clear deps and call again (cache hit)
|
|
97
|
+
deps.clear();
|
|
98
|
+
await transform("", path.join(TMP_DIR, "comp.ts"), scssPath);
|
|
99
|
+
|
|
100
|
+
// deps should be re-populated from cache
|
|
101
|
+
expect(deps.size).toBeGreaterThan(0);
|
|
102
|
+
const depSet = deps.get(scssPath);
|
|
103
|
+
expect(depSet).toBeDefined();
|
|
104
|
+
expect(depSet!.size).toBeGreaterThan(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Unit: inline SCSS is NOT cached
|
|
108
|
+
it("does not cache inline SCSS", async () => {
|
|
109
|
+
const deps = new Map<string, Set<string>>();
|
|
110
|
+
const transform = createClientTransformStylesheet({
|
|
111
|
+
loadPaths: [],
|
|
112
|
+
scssErrors: [],
|
|
113
|
+
scssDependencies: deps,
|
|
114
|
+
cacheDir: CACHE_DIR,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await transform(
|
|
118
|
+
"$size: 14px;\n.text { font-size: $size; }",
|
|
119
|
+
path.join(TMP_DIR, "component.ts"),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// No cache files should be created for inline SCSS
|
|
123
|
+
expect(fs.existsSync(CACHE_DIR)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Unit: file content change → cache miss
|
|
127
|
+
it("invalidates cache when SCSS file content changes", async () => {
|
|
128
|
+
const scssPath = path.join(TMP_DIR, "changing.scss");
|
|
129
|
+
fs.writeFileSync(scssPath, ".host { color: blue; }");
|
|
130
|
+
|
|
131
|
+
const transform = createClientTransformStylesheet({
|
|
132
|
+
loadPaths: [],
|
|
133
|
+
scssErrors: [],
|
|
134
|
+
scssDependencies: new Map(),
|
|
135
|
+
cacheDir: CACHE_DIR,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const result1 = await transform("", path.join(TMP_DIR, "comp.ts"), scssPath);
|
|
139
|
+
expect(result1).toContain("color: blue");
|
|
140
|
+
|
|
141
|
+
fs.writeFileSync(scssPath, ".host { color: red; }");
|
|
142
|
+
|
|
143
|
+
const result2 = await transform("", path.join(TMP_DIR, "comp.ts"), scssPath);
|
|
144
|
+
expect(result2).toContain("color: red");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Unit: no cacheDir → no caching (backward compatible)
|
|
148
|
+
it("works without cacheDir (no caching)", async () => {
|
|
149
|
+
const scssPath = path.join(TMP_DIR, "nodir.scss");
|
|
150
|
+
fs.writeFileSync(scssPath, ".host { color: navy; }");
|
|
151
|
+
|
|
152
|
+
const transform = createClientTransformStylesheet({
|
|
153
|
+
loadPaths: [],
|
|
154
|
+
scssErrors: [],
|
|
155
|
+
scssDependencies: new Map(),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const result = await transform("", path.join(TMP_DIR, "comp.ts"), scssPath);
|
|
159
|
+
expect(result).toContain("color: navy");
|
|
160
|
+
expect(fs.existsSync(CACHE_DIR)).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -28,7 +28,7 @@ describe("sdAngularPlugin CSS HMR compatibility", () => {
|
|
|
28
28
|
.join(FIXTURE_DIR, "node_modules/@scope/lib/dist/style.css")
|
|
29
29
|
.replace(/\\/g, "/");
|
|
30
30
|
|
|
31
|
-
const hmrResult = await (plugin as any).
|
|
31
|
+
const hmrResult = await (plugin as any).hotUpdate?.({
|
|
32
32
|
file: cssFilePath,
|
|
33
33
|
modules: [{ file: cssFilePath, id: cssFilePath }],
|
|
34
34
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -68,7 +68,7 @@ describe("sdAngularPlugin CSS HMR compatibility", () => {
|
|
|
68
68
|
.join(FIXTURE_DIR, "src/app.component.ts")
|
|
69
69
|
.replace(/\\/g, "/");
|
|
70
70
|
|
|
71
|
-
await (plugin as any).
|
|
71
|
+
await (plugin as any).hotUpdate?.({
|
|
72
72
|
file: tsFilePath,
|
|
73
73
|
modules: [{ file: tsFilePath, id: tsFilePath }],
|
|
74
74
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -88,7 +88,7 @@ describe("sdAngularPlugin CSS HMR compatibility", () => {
|
|
|
88
88
|
.join(FIXTURE_DIR, "node_modules/@scope/lib/dist/style.css")
|
|
89
89
|
.replace(/\\/g, "/");
|
|
90
90
|
|
|
91
|
-
await (plugin as any).
|
|
91
|
+
await (plugin as any).hotUpdate?.({
|
|
92
92
|
file: cssFilePath,
|
|
93
93
|
modules: [{ file: cssFilePath, id: cssFilePath }],
|
|
94
94
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -121,7 +121,7 @@ describe("sdAngularPlugin CSS HMR compatibility", () => {
|
|
|
121
121
|
];
|
|
122
122
|
|
|
123
123
|
for (const cssPath of cssVariants) {
|
|
124
|
-
const result = await (plugin as any).
|
|
124
|
+
const result = await (plugin as any).hotUpdate?.({
|
|
125
125
|
file: cssPath,
|
|
126
126
|
modules: [{ file: cssPath, id: cssPath }],
|
|
127
127
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -159,7 +159,7 @@ describe("sdAngularPlugin CSS HMR compatibility", () => {
|
|
|
159
159
|
.join(FIXTURE_DIR, "src/styles.scss")
|
|
160
160
|
.replace(/\\/g, "/");
|
|
161
161
|
|
|
162
|
-
const result = await (plugin as any).
|
|
162
|
+
const result = await (plugin as any).hotUpdate?.({
|
|
163
163
|
file: scssFilePath,
|
|
164
164
|
modules: [{ file: scssFilePath, id: scssFilePath }],
|
|
165
165
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -209,8 +209,8 @@ describe("sdAngularPlugin HMR fallback", () => {
|
|
|
209
209
|
.join(FIXTURE_DIR, "src/app.component.ts")
|
|
210
210
|
.replace(/\\/g, "/");
|
|
211
211
|
|
|
212
|
-
//
|
|
213
|
-
const hmrResult = await (plugin as any).
|
|
212
|
+
// hotUpdate 호출
|
|
213
|
+
const hmrResult = await (plugin as any).hotUpdate?.({
|
|
214
214
|
file: appComponentPath,
|
|
215
215
|
modules: [{ file: appComponentPath, id: appComponentPath }],
|
|
216
216
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -218,7 +218,7 @@ describe("sdAngularPlugin HMR fallback", () => {
|
|
|
218
218
|
read: () => Promise.resolve(""),
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
-
//
|
|
221
|
+
// hotUpdate는 항상 affected modules 배열을 반환해야 한다
|
|
222
222
|
// (templateUpdates가 undefined이든 아니든)
|
|
223
223
|
expect(Array.isArray(hmrResult)).toBe(true);
|
|
224
224
|
|
|
@@ -249,8 +249,8 @@ describe("sdAngularPlugin HMR fallback", () => {
|
|
|
249
249
|
await (plugin as any).buildEnd?.call({});
|
|
250
250
|
});
|
|
251
251
|
|
|
252
|
-
// Unit: prod 모드(dev: false)에서는
|
|
253
|
-
it("returns undefined from
|
|
252
|
+
// Unit: prod 모드(dev: false)에서는 hotUpdate가 void 반환 (HMR 비활성)
|
|
253
|
+
it("returns undefined from hotUpdate in prod mode", async () => {
|
|
254
254
|
const plugin = sdAngularPlugin({
|
|
255
255
|
tsconfig: TSCONFIG_PATH,
|
|
256
256
|
dev: false,
|
|
@@ -262,7 +262,7 @@ describe("sdAngularPlugin HMR fallback", () => {
|
|
|
262
262
|
.join(FIXTURE_DIR, "src/app.component.ts")
|
|
263
263
|
.replace(/\\/g, "/");
|
|
264
264
|
|
|
265
|
-
const hmrResult = await (plugin as any).
|
|
265
|
+
const hmrResult = await (plugin as any).hotUpdate?.({
|
|
266
266
|
file: appComponentPath,
|
|
267
267
|
modules: [{ file: appComponentPath }],
|
|
268
268
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -276,7 +276,7 @@ describe("sdAngularPlugin HMR fallback", () => {
|
|
|
276
276
|
await (plugin as any).buildEnd?.call({});
|
|
277
277
|
});
|
|
278
278
|
|
|
279
|
-
// Acceptance: 수정 파일 33개 이상일 때도
|
|
279
|
+
// Acceptance: 수정 파일 33개 이상일 때도 hotUpdate가 정상 동작
|
|
280
280
|
// (Angular 컴파일러 내부에서 HMR 분석을 생략하고 templateUpdates=undefined 반환)
|
|
281
281
|
it("handles update with many modified files gracefully (HMR skipped by compiler)", async () => {
|
|
282
282
|
const plugin = sdAngularPlugin({
|
|
@@ -290,11 +290,11 @@ describe("sdAngularPlugin HMR fallback", () => {
|
|
|
290
290
|
.join(FIXTURE_DIR, "src/app.component.ts")
|
|
291
291
|
.replace(/\\/g, "/");
|
|
292
292
|
|
|
293
|
-
//
|
|
293
|
+
// hotUpdate는 단일 파일에 대해 호출됨 (Vite 설계)
|
|
294
294
|
// 33개 이상 수정 파일 제한은 Angular 컴파일러 내부에서 처리
|
|
295
295
|
// AngularFacade.update()에서 modifiedFiles가 SourceFileCache.modifiedFiles로 전달되므로
|
|
296
296
|
// 실제 33개 이상 파일 변경은 SourceFileCache를 통해 추적됨
|
|
297
|
-
const hmrResult = await (plugin as any).
|
|
297
|
+
const hmrResult = await (plugin as any).hotUpdate?.({
|
|
298
298
|
file: appComponentPath,
|
|
299
299
|
modules: [{ file: appComponentPath }],
|
|
300
300
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -302,7 +302,7 @@ describe("sdAngularPlugin HMR fallback", () => {
|
|
|
302
302
|
read: () => Promise.resolve(""),
|
|
303
303
|
});
|
|
304
304
|
|
|
305
|
-
//
|
|
305
|
+
// hotUpdate가 에러 없이 동작해야 한다
|
|
306
306
|
expect(Array.isArray(hmrResult)).toBe(true);
|
|
307
307
|
|
|
308
308
|
await (plugin as any).buildEnd?.call({});
|
|
@@ -131,8 +131,8 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
|
|
|
131
131
|
await (plugin as any).buildEnd?.call({});
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
// Acceptance:
|
|
135
|
-
it("collects templateUpdates from
|
|
134
|
+
// Acceptance: hotUpdate에서 templateUpdates를 수집하고 middleware에서 서빙
|
|
135
|
+
it("collects templateUpdates from hotUpdate and serves via middleware", async () => {
|
|
136
136
|
const plugin = sdAngularPlugin({ tsconfig: TSCONFIG_PATH, dev: true });
|
|
137
137
|
|
|
138
138
|
await (plugin as any).buildStart?.call({});
|
|
@@ -151,12 +151,12 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
|
|
|
151
151
|
};
|
|
152
152
|
(plugin as any).configureServer?.(mockServer);
|
|
153
153
|
|
|
154
|
-
//
|
|
154
|
+
// hotUpdate 호출 (인라인 템플릿 변경)
|
|
155
155
|
const appComponentPath = path
|
|
156
156
|
.join(FIXTURE_DIR, "src/app.component.ts")
|
|
157
157
|
.replace(/\\/g, "/");
|
|
158
158
|
|
|
159
|
-
await (plugin as any).
|
|
159
|
+
await (plugin as any).hotUpdate?.({
|
|
160
160
|
file: appComponentPath,
|
|
161
161
|
modules: [{ file: appComponentPath, id: appComponentPath }],
|
|
162
162
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -174,7 +174,7 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
|
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
// Acceptance: rebuild 시작 시 이전 templateUpdates 정리
|
|
177
|
-
it("clears templateUpdates at the start of
|
|
177
|
+
it("clears templateUpdates at the start of hotUpdate", async () => {
|
|
178
178
|
const plugin = sdAngularPlugin({ tsconfig: TSCONFIG_PATH, dev: true });
|
|
179
179
|
await (plugin as any).buildStart?.call({});
|
|
180
180
|
|
|
@@ -195,8 +195,8 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
|
|
|
195
195
|
.join(FIXTURE_DIR, "src/app.component.ts")
|
|
196
196
|
.replace(/\\/g, "/");
|
|
197
197
|
|
|
198
|
-
// 첫 번째
|
|
199
|
-
await (plugin as any).
|
|
198
|
+
// 첫 번째 hotUpdate
|
|
199
|
+
await (plugin as any).hotUpdate?.({
|
|
200
200
|
file: appComponentPath,
|
|
201
201
|
modules: [{ file: appComponentPath }],
|
|
202
202
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -204,8 +204,8 @@ describe("sdAngularPlugin HMR + component-middleware", () => {
|
|
|
204
204
|
read: () => Promise.resolve(""),
|
|
205
205
|
});
|
|
206
206
|
|
|
207
|
-
// 두 번째
|
|
208
|
-
await (plugin as any).
|
|
207
|
+
// 두 번째 hotUpdate — 이전 templateUpdates가 정리되어야 한다
|
|
208
|
+
await (plugin as any).hotUpdate?.({
|
|
209
209
|
file: appComponentPath,
|
|
210
210
|
modules: [{ file: appComponentPath }],
|
|
211
211
|
server: { watcher: { emit: vi.fn() } },
|
|
@@ -145,7 +145,7 @@ describe("vite-angular-plugin lint integration (Slice 5)", () => {
|
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
describe("Scenario: lint runs in
|
|
148
|
+
describe("Scenario: lint runs in hotUpdate", () => {
|
|
149
149
|
it("runs lint after incremental compilation and passes result to onBuild", async () => {
|
|
150
150
|
const onBuildResults: any[] = [];
|
|
151
151
|
|
|
@@ -167,9 +167,9 @@ describe("vite-angular-plugin lint integration (Slice 5)", () => {
|
|
|
167
167
|
formattedOutput: "lint errors found",
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
// Call
|
|
171
|
-
const
|
|
172
|
-
await
|
|
170
|
+
// Call hotUpdate
|
|
171
|
+
const hotUpdate = (plugin as any).hotUpdate;
|
|
172
|
+
await hotUpdate.call({}, {
|
|
173
173
|
file: "/workspace/packages/client/src/app.ts",
|
|
174
174
|
modules: [],
|
|
175
175
|
server: {},
|