@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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
// --- Mock factories ---
|
|
6
|
+
|
|
7
|
+
const mockGeneratePwaIcons = vi.fn();
|
|
8
|
+
vi.mock("../../src/utils/generate-pwa-icons.js", () => ({
|
|
9
|
+
generatePwaIcons: mockGeneratePwaIcons,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
const mockGlob = vi.fn();
|
|
13
|
+
vi.mock("glob", () => ({
|
|
14
|
+
glob: mockGlob,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// --- Dynamic import ---
|
|
18
|
+
|
|
19
|
+
const { sdPwaPlugin } = await import("../../src/utils/vite-pwa-plugin");
|
|
20
|
+
|
|
21
|
+
// --- Helpers ---
|
|
22
|
+
|
|
23
|
+
function createPlugin(
|
|
24
|
+
overrides?: Partial<Parameters<typeof sdPwaPlugin>[0]>,
|
|
25
|
+
): Plugin {
|
|
26
|
+
return sdPwaPlugin({
|
|
27
|
+
pkgDir: "/packages/test-app",
|
|
28
|
+
pkgName: "test-app",
|
|
29
|
+
...overrides,
|
|
30
|
+
}) as Plugin;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function initPlugin(plugin: Plugin): void {
|
|
34
|
+
(plugin.configResolved as Function)({
|
|
35
|
+
base: "/test-app/",
|
|
36
|
+
build: { outDir: "/packages/test-app/dist" },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getManifestWriteCall(): [string, string] | undefined {
|
|
41
|
+
return vi.mocked(fs.writeFileSync).mock.calls.find((c) =>
|
|
42
|
+
String(c[0]).includes("manifest.webmanifest"),
|
|
43
|
+
) as [string, string] | undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parseWrittenManifest(): Record<string, unknown> {
|
|
47
|
+
const call = getManifestWriteCall();
|
|
48
|
+
if (call == null) throw new Error("manifest.webmanifest not written");
|
|
49
|
+
return JSON.parse(call[1]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// --- Tests ---
|
|
53
|
+
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
vi.clearAllMocks();
|
|
56
|
+
mockGeneratePwaIcons.mockResolvedValue([]);
|
|
57
|
+
mockGlob.mockResolvedValue([]);
|
|
58
|
+
vi.spyOn(fs, "readFileSync").mockReturnValue(
|
|
59
|
+
JSON.stringify({ version: "1.0.0" }),
|
|
60
|
+
);
|
|
61
|
+
vi.spyOn(fs, "writeFileSync").mockReturnValue(undefined);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
vi.restoreAllMocks();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("sdPwaPlugin — Acceptance: Slice 1 manifest 생성", () => {
|
|
69
|
+
// Scenario: 기본 manifest 생성
|
|
70
|
+
it("generates manifest.webmanifest with default fields when pwa is empty", async () => {
|
|
71
|
+
const plugin = createPlugin({ pwa: {} });
|
|
72
|
+
initPlugin(plugin);
|
|
73
|
+
await (plugin.closeBundle as Function)();
|
|
74
|
+
|
|
75
|
+
const manifest = parseWrittenManifest();
|
|
76
|
+
expect(manifest["name"]).toBe("test-app");
|
|
77
|
+
expect(manifest["short_name"]).toBe("test-app");
|
|
78
|
+
expect(manifest["display"]).toBe("standalone");
|
|
79
|
+
expect(manifest["theme_color"]).toBe("#ffffff");
|
|
80
|
+
expect(manifest["background_color"]).toBe("#ffffff");
|
|
81
|
+
expect(manifest["start_url"]).toBe(".");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Scenario: manifest 필드 커스텀
|
|
85
|
+
it("applies custom manifest fields from SdPwaConfig", async () => {
|
|
86
|
+
const plugin = createPlugin({
|
|
87
|
+
pwa: { manifest: { name: "My App", theme_color: "#000000" } },
|
|
88
|
+
});
|
|
89
|
+
initPlugin(plugin);
|
|
90
|
+
await (plugin.closeBundle as Function)();
|
|
91
|
+
|
|
92
|
+
const manifest = parseWrittenManifest();
|
|
93
|
+
expect(manifest["name"]).toBe("My App");
|
|
94
|
+
expect(manifest["theme_color"]).toBe("#000000");
|
|
95
|
+
expect(manifest["short_name"]).toBe("test-app");
|
|
96
|
+
expect(manifest["display"]).toBe("standalone");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Scenario: 기본 아이콘 자동 생성
|
|
100
|
+
it("includes generated icons in manifest when no custom icons", async () => {
|
|
101
|
+
mockGeneratePwaIcons.mockResolvedValue([
|
|
102
|
+
{ src: "icons/icon-192x192.png", sizes: "192x192", type: "image/png" },
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const plugin = createPlugin();
|
|
106
|
+
initPlugin(plugin);
|
|
107
|
+
await (plugin.closeBundle as Function)();
|
|
108
|
+
|
|
109
|
+
expect(mockGeneratePwaIcons).toHaveBeenCalledWith("/packages/test-app");
|
|
110
|
+
const manifest = parseWrittenManifest();
|
|
111
|
+
expect(manifest["icons"]).toEqual([
|
|
112
|
+
{ src: "icons/icon-192x192.png", sizes: "192x192", type: "image/png" },
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Scenario: 커스텀 아이콘 설정 시 자동 생성 건너뜀
|
|
117
|
+
it("uses custom icons and skips generatePwaIcons", async () => {
|
|
118
|
+
const icons = [
|
|
119
|
+
{ src: "/custom.png", sizes: "512x512", type: "image/png" },
|
|
120
|
+
];
|
|
121
|
+
const plugin = createPlugin({
|
|
122
|
+
pwa: { manifest: { icons } },
|
|
123
|
+
});
|
|
124
|
+
initPlugin(plugin);
|
|
125
|
+
await (plugin.closeBundle as Function)();
|
|
126
|
+
|
|
127
|
+
expect(mockGeneratePwaIcons).not.toHaveBeenCalled();
|
|
128
|
+
const manifest = parseWrittenManifest();
|
|
129
|
+
expect(manifest["icons"]).toEqual(icons);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Scenario: 원본 아이콘 없음
|
|
133
|
+
it("omits icons field when generatePwaIcons returns empty array", async () => {
|
|
134
|
+
mockGeneratePwaIcons.mockResolvedValue([]);
|
|
135
|
+
|
|
136
|
+
const plugin = createPlugin();
|
|
137
|
+
initPlugin(plugin);
|
|
138
|
+
await (plugin.closeBundle as Function)();
|
|
139
|
+
|
|
140
|
+
const manifest = parseWrittenManifest();
|
|
141
|
+
expect(manifest["icons"]).toBeUndefined();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
// --- Mock factories ---
|
|
6
|
+
|
|
7
|
+
const mockGeneratePwaIcons = vi.fn();
|
|
8
|
+
vi.mock("../../src/utils/generate-pwa-icons.js", () => ({
|
|
9
|
+
generatePwaIcons: mockGeneratePwaIcons,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
const mockGlob = vi.fn();
|
|
13
|
+
vi.mock("glob", () => ({
|
|
14
|
+
glob: mockGlob,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// --- Dynamic import ---
|
|
18
|
+
|
|
19
|
+
const { sdPwaPlugin } = await import("../../src/utils/vite-pwa-plugin");
|
|
20
|
+
|
|
21
|
+
// --- Helpers ---
|
|
22
|
+
|
|
23
|
+
function createPlugin(
|
|
24
|
+
overrides?: Partial<Parameters<typeof sdPwaPlugin>[0]>,
|
|
25
|
+
): Plugin {
|
|
26
|
+
return sdPwaPlugin({
|
|
27
|
+
pkgDir: "/packages/test-app",
|
|
28
|
+
pkgName: "test-app",
|
|
29
|
+
...overrides,
|
|
30
|
+
}) as Plugin;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function initPlugin(plugin: Plugin): void {
|
|
34
|
+
(plugin.configResolved as Function)({
|
|
35
|
+
base: "/test-app/",
|
|
36
|
+
build: { outDir: "/packages/test-app/dist" },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getWriteCall(filename: string): [string, string] | undefined {
|
|
41
|
+
return vi.mocked(fs.writeFileSync).mock.calls.find((c) =>
|
|
42
|
+
String(c[0]).endsWith(filename),
|
|
43
|
+
) as [string, string] | undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// --- Tests ---
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
vi.clearAllMocks();
|
|
50
|
+
mockGeneratePwaIcons.mockResolvedValue([]);
|
|
51
|
+
mockGlob.mockResolvedValue([]);
|
|
52
|
+
vi.spyOn(fs, "readFileSync").mockReturnValue(
|
|
53
|
+
JSON.stringify({ version: "1.0.0" }),
|
|
54
|
+
);
|
|
55
|
+
vi.spyOn(fs, "writeFileSync").mockReturnValue(undefined);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
vi.restoreAllMocks();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("sdPwaPlugin — manifest generation", () => {
|
|
63
|
+
// Unit: pwa undefined uses same defaults as empty object
|
|
64
|
+
it("uses defaults when pwa is undefined", async () => {
|
|
65
|
+
const plugin = createPlugin({ pwa: undefined });
|
|
66
|
+
initPlugin(plugin);
|
|
67
|
+
await (plugin.closeBundle as Function)();
|
|
68
|
+
|
|
69
|
+
const call = getWriteCall("manifest.webmanifest");
|
|
70
|
+
expect(call).toBeDefined();
|
|
71
|
+
const manifest = JSON.parse(call![1]) as Record<string, unknown>;
|
|
72
|
+
expect(manifest["name"]).toBe("test-app");
|
|
73
|
+
expect(manifest["scope"]).toBe(".");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Unit: manifest written to correct outDir path
|
|
77
|
+
it("writes manifest to resolvedOutDir", async () => {
|
|
78
|
+
const plugin = createPlugin();
|
|
79
|
+
initPlugin(plugin);
|
|
80
|
+
await (plugin.closeBundle as Function)();
|
|
81
|
+
|
|
82
|
+
const call = getWriteCall("manifest.webmanifest");
|
|
83
|
+
const writtenPath = String(call![0]).replace(/\\/g, "/");
|
|
84
|
+
expect(writtenPath).toContain("packages/test-app/dist");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Unit: all manifest fields can be overridden
|
|
88
|
+
it("overrides all customizable manifest fields", async () => {
|
|
89
|
+
const plugin = createPlugin({
|
|
90
|
+
pwa: {
|
|
91
|
+
manifest: {
|
|
92
|
+
name: "A",
|
|
93
|
+
short_name: "B",
|
|
94
|
+
display: "fullscreen",
|
|
95
|
+
theme_color: "#111",
|
|
96
|
+
background_color: "#222",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
initPlugin(plugin);
|
|
101
|
+
await (plugin.closeBundle as Function)();
|
|
102
|
+
|
|
103
|
+
const manifest = JSON.parse(getWriteCall("manifest.webmanifest")![1]) as Record<
|
|
104
|
+
string,
|
|
105
|
+
unknown
|
|
106
|
+
>;
|
|
107
|
+
expect(manifest["name"]).toBe("A");
|
|
108
|
+
expect(manifest["short_name"]).toBe("B");
|
|
109
|
+
expect(manifest["display"]).toBe("fullscreen");
|
|
110
|
+
expect(manifest["theme_color"]).toBe("#111");
|
|
111
|
+
expect(manifest["background_color"]).toBe("#222");
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("sdPwaPlugin — precache file collection", () => {
|
|
116
|
+
// Unit: default globPatterns
|
|
117
|
+
it("uses default globPatterns when workbox is not configured", async () => {
|
|
118
|
+
mockGlob.mockResolvedValue(["index.html", "assets/main.js"]);
|
|
119
|
+
|
|
120
|
+
const plugin = createPlugin();
|
|
121
|
+
initPlugin(plugin);
|
|
122
|
+
await (plugin.closeBundle as Function)();
|
|
123
|
+
|
|
124
|
+
expect(mockGlob).toHaveBeenCalledWith(
|
|
125
|
+
"**/*.{js,css,html,ico,png,svg,woff2}",
|
|
126
|
+
{ cwd: "/packages/test-app/dist" },
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Unit: custom globPatterns
|
|
131
|
+
it("uses custom globPatterns from workbox config", async () => {
|
|
132
|
+
mockGlob.mockResolvedValue(["index.html"]);
|
|
133
|
+
|
|
134
|
+
const plugin = createPlugin({
|
|
135
|
+
pwa: { workbox: { globPatterns: ["**/*.{js,html}"] } },
|
|
136
|
+
});
|
|
137
|
+
initPlugin(plugin);
|
|
138
|
+
await (plugin.closeBundle as Function)();
|
|
139
|
+
|
|
140
|
+
expect(mockGlob).toHaveBeenCalledWith("**/*.{js,html}", expect.anything());
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Unit: excludes sw.js and manifest.webmanifest from precache
|
|
144
|
+
it("excludes sw.js and manifest.webmanifest from precache list", async () => {
|
|
145
|
+
mockGlob.mockResolvedValue([
|
|
146
|
+
"index.html",
|
|
147
|
+
"main.js",
|
|
148
|
+
"sw.js",
|
|
149
|
+
"manifest.webmanifest",
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
const plugin = createPlugin();
|
|
153
|
+
initPlugin(plugin);
|
|
154
|
+
await (plugin.closeBundle as Function)();
|
|
155
|
+
|
|
156
|
+
const swCall = getWriteCall("sw.js");
|
|
157
|
+
const swContent = swCall![1];
|
|
158
|
+
expect(swContent).toContain('"index.html"');
|
|
159
|
+
expect(swContent).toContain('"main.js"');
|
|
160
|
+
expect(swContent).not.toMatch(/"sw\.js"/);
|
|
161
|
+
expect(swContent).not.toMatch(/"manifest\.webmanifest"/);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Unit: deduplicates file list
|
|
165
|
+
it("deduplicates files matched by multiple patterns", async () => {
|
|
166
|
+
mockGlob
|
|
167
|
+
.mockResolvedValueOnce(["index.html", "main.js"])
|
|
168
|
+
.mockResolvedValueOnce(["index.html", "styles.css"]);
|
|
169
|
+
|
|
170
|
+
const plugin = createPlugin({
|
|
171
|
+
pwa: {
|
|
172
|
+
workbox: { globPatterns: ["**/*.{html,js}", "**/*.{html,css}"] },
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
initPlugin(plugin);
|
|
176
|
+
await (plugin.closeBundle as Function)();
|
|
177
|
+
|
|
178
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
179
|
+
// Extract only the PRECACHE_URLS array declaration
|
|
180
|
+
const precacheMatch = swContent.match(
|
|
181
|
+
/const PRECACHE_URLS = \[([\s\S]*?)\];/,
|
|
182
|
+
);
|
|
183
|
+
expect(precacheMatch).toBeDefined();
|
|
184
|
+
const precacheBlock = precacheMatch![1];
|
|
185
|
+
const htmlMatches = precacheBlock.match(/"index\.html"/g);
|
|
186
|
+
expect(htmlMatches).toHaveLength(1);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("sdPwaPlugin — sw.js generation", () => {
|
|
191
|
+
// Unit: version injected from package.json
|
|
192
|
+
it("injects APP_VERSION from package.json", async () => {
|
|
193
|
+
vi.mocked(fs.readFileSync).mockReturnValue(
|
|
194
|
+
JSON.stringify({ version: "14.0.16" }),
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const plugin = createPlugin();
|
|
198
|
+
initPlugin(plugin);
|
|
199
|
+
await (plugin.closeBundle as Function)();
|
|
200
|
+
|
|
201
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
202
|
+
expect(swContent).toContain('const APP_VERSION = "14.0.16"');
|
|
203
|
+
expect(swContent).toContain('"precache-" + APP_VERSION');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Unit: base URL injected
|
|
207
|
+
it("injects BASE_URL from resolved config", async () => {
|
|
208
|
+
const plugin = createPlugin();
|
|
209
|
+
initPlugin(plugin);
|
|
210
|
+
await (plugin.closeBundle as Function)();
|
|
211
|
+
|
|
212
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
213
|
+
expect(swContent).toContain('const BASE_URL = "/test-app/"');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Unit: sw.js contains all 4 event listeners
|
|
217
|
+
it("contains install, activate, fetch, and message event listeners", async () => {
|
|
218
|
+
const plugin = createPlugin();
|
|
219
|
+
initPlugin(plugin);
|
|
220
|
+
await (plugin.closeBundle as Function)();
|
|
221
|
+
|
|
222
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
223
|
+
expect(swContent).toContain('self.addEventListener("install"');
|
|
224
|
+
expect(swContent).toContain('self.addEventListener("activate"');
|
|
225
|
+
expect(swContent).toContain('self.addEventListener("fetch"');
|
|
226
|
+
expect(swContent).toContain('self.addEventListener("message"');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Unit: install handler uses cache.addAll
|
|
230
|
+
it("install handler caches all precache URLs", async () => {
|
|
231
|
+
const plugin = createPlugin();
|
|
232
|
+
initPlugin(plugin);
|
|
233
|
+
await (plugin.closeBundle as Function)();
|
|
234
|
+
|
|
235
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
236
|
+
expect(swContent).toContain("caches.open(CACHE_NAME)");
|
|
237
|
+
expect(swContent).toContain("cache.addAll(PRECACHE_URLS)");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Unit: activate handler deletes old caches with precache- prefix only
|
|
241
|
+
it("activate handler filters by precache- prefix", async () => {
|
|
242
|
+
const plugin = createPlugin();
|
|
243
|
+
initPlugin(plugin);
|
|
244
|
+
await (plugin.closeBundle as Function)();
|
|
245
|
+
|
|
246
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
247
|
+
expect(swContent).toContain('name.startsWith("precache-")');
|
|
248
|
+
expect(swContent).toContain("name !== CACHE_NAME");
|
|
249
|
+
expect(swContent).toContain("self.clients.claim()");
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Unit: fetch handler has navigate fallback to index.html with network fallback
|
|
253
|
+
it("fetch handler falls back to index.html for navigate requests with network fallback", async () => {
|
|
254
|
+
const plugin = createPlugin();
|
|
255
|
+
initPlugin(plugin);
|
|
256
|
+
await (plugin.closeBundle as Function)();
|
|
257
|
+
|
|
258
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
259
|
+
expect(swContent).toContain('event.request.mode === "navigate"');
|
|
260
|
+
expect(swContent).toContain('BASE_URL + "index.html"');
|
|
261
|
+
// Network fallback when index.html is not in cache (storage pressure)
|
|
262
|
+
expect(swContent).toContain("resp || fetch(event.request)");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Unit: message handler responds to SKIP_WAITING
|
|
266
|
+
it("message handler calls self.skipWaiting on SKIP_WAITING", async () => {
|
|
267
|
+
const plugin = createPlugin();
|
|
268
|
+
initPlugin(plugin);
|
|
269
|
+
await (plugin.closeBundle as Function)();
|
|
270
|
+
|
|
271
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
272
|
+
expect(swContent).toContain('"SKIP_WAITING"');
|
|
273
|
+
expect(swContent).toContain("self.skipWaiting()");
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Unit: backslash normalization in file paths
|
|
277
|
+
it("normalizes backslashes in precache URLs", async () => {
|
|
278
|
+
mockGlob.mockResolvedValue(["assets\\main.js"]);
|
|
279
|
+
|
|
280
|
+
const plugin = createPlugin();
|
|
281
|
+
initPlugin(plugin);
|
|
282
|
+
await (plugin.closeBundle as Function)();
|
|
283
|
+
|
|
284
|
+
const swContent = getWriteCall("sw.js")![1];
|
|
285
|
+
expect(swContent).toContain('"assets/main.js"');
|
|
286
|
+
expect(swContent).not.toContain("\\\\");
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe("sdPwaPlugin — transformIndexHtml", () => {
|
|
291
|
+
// Unit: manifest link tag
|
|
292
|
+
it("injects manifest link tag", () => {
|
|
293
|
+
const plugin = createPlugin();
|
|
294
|
+
initPlugin(plugin);
|
|
295
|
+
const tags = (plugin.transformIndexHtml as Function)() as Array<{
|
|
296
|
+
tag: string;
|
|
297
|
+
attrs?: Record<string, string>;
|
|
298
|
+
injectTo?: string;
|
|
299
|
+
}>;
|
|
300
|
+
|
|
301
|
+
const linkTag = tags.find((t) => t.tag === "link");
|
|
302
|
+
expect(linkTag).toBeDefined();
|
|
303
|
+
expect(linkTag!.attrs!["rel"]).toBe("manifest");
|
|
304
|
+
expect(linkTag!.attrs!["href"]).toBe("manifest.webmanifest");
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Unit: SW registration script tag
|
|
308
|
+
it("injects SW registration script", () => {
|
|
309
|
+
const plugin = createPlugin();
|
|
310
|
+
initPlugin(plugin);
|
|
311
|
+
const tags = (plugin.transformIndexHtml as Function)() as Array<{
|
|
312
|
+
tag: string;
|
|
313
|
+
children?: string;
|
|
314
|
+
injectTo?: string;
|
|
315
|
+
}>;
|
|
316
|
+
|
|
317
|
+
const scriptTag = tags.find((t) => t.tag === "script");
|
|
318
|
+
expect(scriptTag).toBeDefined();
|
|
319
|
+
expect(scriptTag!.children).toContain("serviceWorker");
|
|
320
|
+
expect(scriptTag!.children).toContain('register("sw.js")');
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Unit: registration script dispatches sd-pwa-update-ready event
|
|
324
|
+
it("dispatches sd-pwa-update-ready CustomEvent", () => {
|
|
325
|
+
const plugin = createPlugin();
|
|
326
|
+
initPlugin(plugin);
|
|
327
|
+
const tags = (plugin.transformIndexHtml as Function)() as Array<{
|
|
328
|
+
tag: string;
|
|
329
|
+
children?: string;
|
|
330
|
+
}>;
|
|
331
|
+
|
|
332
|
+
const scriptTag = tags.find((t) => t.tag === "script");
|
|
333
|
+
expect(scriptTag!.children).toContain("sd-pwa-update-ready");
|
|
334
|
+
expect(scriptTag!.children).toContain("SKIP_WAITING");
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// Unit: registration script reloads on controllerchange
|
|
338
|
+
it("reloads page on controllerchange", () => {
|
|
339
|
+
const plugin = createPlugin();
|
|
340
|
+
initPlugin(plugin);
|
|
341
|
+
const tags = (plugin.transformIndexHtml as Function)() as Array<{
|
|
342
|
+
tag: string;
|
|
343
|
+
children?: string;
|
|
344
|
+
}>;
|
|
345
|
+
|
|
346
|
+
const scriptTag = tags.find((t) => t.tag === "script");
|
|
347
|
+
expect(scriptTag!.children).toContain("controllerchange");
|
|
348
|
+
expect(scriptTag!.children).toContain("location.reload");
|
|
349
|
+
});
|
|
350
|
+
});
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import { setupWorkerConsola, createOnceGuard, registerCleanupHandlers } from "../../src/utils/worker-utils";
|
|
3
3
|
import consola, { LogLevels } from "consola";
|
|
4
4
|
|
|
5
|
-
describe("
|
|
5
|
+
describe("setupWorkerConsola", () => {
|
|
6
6
|
const originalLevel = consola.level;
|
|
7
|
+
const originalReporters = [...consola.options.reporters];
|
|
7
8
|
const originalEnv = process.env["SD_DEBUG"];
|
|
8
9
|
|
|
9
10
|
afterEach(() => {
|
|
10
11
|
consola.level = originalLevel;
|
|
12
|
+
consola.options.reporters = originalReporters;
|
|
11
13
|
if (originalEnv === undefined) {
|
|
12
14
|
delete process.env["SD_DEBUG"];
|
|
13
15
|
} else {
|
|
@@ -17,15 +19,14 @@ describe("applyDebugLevel", () => {
|
|
|
17
19
|
|
|
18
20
|
it("sets consola level to debug when SD_DEBUG is 'true'", () => {
|
|
19
21
|
process.env["SD_DEBUG"] = "true";
|
|
20
|
-
|
|
22
|
+
setupWorkerConsola();
|
|
21
23
|
expect(consola.level).toBe(LogLevels.debug);
|
|
22
24
|
});
|
|
23
25
|
|
|
24
|
-
it("
|
|
26
|
+
it("sets consola level to debug even when SD_DEBUG is not set", () => {
|
|
25
27
|
delete process.env["SD_DEBUG"];
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
expect(consola.level).toBe(before);
|
|
28
|
+
setupWorkerConsola();
|
|
29
|
+
expect(consola.level).toBe(LogLevels.debug);
|
|
29
30
|
});
|
|
30
31
|
});
|
|
31
32
|
|
|
@@ -75,7 +75,7 @@ vi.mock("consola", () => ({
|
|
|
75
75
|
|
|
76
76
|
vi.mock("../../src/utils/worker-utils.js", () => ({
|
|
77
77
|
registerCleanupHandlers: vi.fn(),
|
|
78
|
-
|
|
78
|
+
setupWorkerConsola: vi.fn(),
|
|
79
79
|
}));
|
|
80
80
|
|
|
81
81
|
//#endregion
|
|
@@ -522,6 +522,55 @@ describe("client.worker", () => {
|
|
|
522
522
|
});
|
|
523
523
|
});
|
|
524
524
|
|
|
525
|
+
// Acceptance: Scenario "Vite dev server 포트 확정 시 포트 파일 기록"
|
|
526
|
+
describe("dev port file", () => {
|
|
527
|
+
it("writes .dev-port after serverReady in dev mode", async () => {
|
|
528
|
+
const mockServer = {
|
|
529
|
+
listen: vi.fn().mockResolvedValue(undefined),
|
|
530
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
531
|
+
httpServer: { address: () => ({ port: 5173 }) },
|
|
532
|
+
};
|
|
533
|
+
mockCreateServer.mockResolvedValue(mockServer);
|
|
534
|
+
|
|
535
|
+
await workerFns["startWatch"]({
|
|
536
|
+
...createBaseInfo(),
|
|
537
|
+
port: 5173,
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
expect(mockWriteFileSync).toHaveBeenCalledWith(
|
|
541
|
+
expect.stringContaining(".dev-port"),
|
|
542
|
+
"5173",
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
await workerFns["stopWatch"]();
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it("writes .dev-port after serverReady in legacy mode", async () => {
|
|
549
|
+
const mockWatcher = createMockWatcher();
|
|
550
|
+
mockViteBuild.mockImplementation(() => {
|
|
551
|
+
setTimeout(() => mockWatcher.emit("event", { code: "END" }), 0);
|
|
552
|
+
return Promise.resolve(mockWatcher);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
mockExistsSync.mockReturnValue(false);
|
|
556
|
+
|
|
557
|
+
await workerFns["startWatch"]({
|
|
558
|
+
...createBaseInfo(),
|
|
559
|
+
browserSupport: { legacyModule: true },
|
|
560
|
+
port: 0,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
const port = mockSend.mock.calls.find((c: any[]) => c[0] === "serverReady")?.[1]?.port;
|
|
564
|
+
|
|
565
|
+
expect(mockWriteFileSync).toHaveBeenCalledWith(
|
|
566
|
+
expect.stringContaining(".dev-port"),
|
|
567
|
+
String(port),
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
await workerFns["stopWatch"]();
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
525
574
|
describe("startWatch — legacy live reload (Feature 1.3)", () => {
|
|
526
575
|
// Acceptance: Scenario "index.html 서빙 시 reload 스크립트 자동 삽입"
|
|
527
576
|
it("injects live reload script into HTML responses", async () => {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# 포트 파일 기록/삭제 — LLM 검증
|
|
2
|
+
|
|
3
|
+
## 검증 항목
|
|
4
|
+
- [x] client.worker에서 dev 모드 serverReady 후 writeDevPort 호출: `client.worker.ts:250-253` — serverReady(248) 직후 `fs.writeFileSync(path.join(distDir, ".dev-port"), String(actualPort))` 확인
|
|
5
|
+
- [x] client.worker에서 legacy 모드 serverReady 후 writeDevPort 호출: `client.worker.ts:331-332` — serverReady(329) 직후 `fs.writeFileSync(path.join(info.pkgDir, "dist", ".dev-port"), String(serverPort))` 확인
|
|
6
|
+
- [x] ViteEngine.stop()에서 deleteDevPort 호출: `ViteEngine.ts:202-204` — `fs.unlinkSync(portFile)` with try-catch 확인
|
|
@@ -35,7 +35,7 @@ vi.mock("../../src/utils/tsc-build", () => ({
|
|
|
35
35
|
vi.mock("../../src/utils/worker-utils", () => ({
|
|
36
36
|
registerCleanupHandlers: vi.fn(),
|
|
37
37
|
createOnceGuard: vi.fn(() => vi.fn()),
|
|
38
|
-
|
|
38
|
+
setupWorkerConsola: vi.fn(),
|
|
39
39
|
}));
|
|
40
40
|
|
|
41
41
|
const mockConsolaLogger = {
|
|
@@ -72,7 +72,7 @@ vi.mock("typescript", async (importOriginal) => {
|
|
|
72
72
|
vi.mock("../../src/utils/worker-utils", () => ({
|
|
73
73
|
registerCleanupHandlers: vi.fn(),
|
|
74
74
|
createOnceGuard: vi.fn(() => vi.fn()),
|
|
75
|
-
|
|
75
|
+
setupWorkerConsola: vi.fn(),
|
|
76
76
|
}));
|
|
77
77
|
|
|
78
78
|
vi.mock("../../src/utils/package-utils", () => ({
|
|
@@ -53,7 +53,7 @@ vi.mock("esbuild", () => ({
|
|
|
53
53
|
vi.mock("../../src/utils/worker-utils", () => ({
|
|
54
54
|
registerCleanupHandlers: vi.fn(),
|
|
55
55
|
createOnceGuard: vi.fn(() => vi.fn()),
|
|
56
|
-
|
|
56
|
+
setupWorkerConsola: vi.fn(),
|
|
57
57
|
}));
|
|
58
58
|
|
|
59
59
|
vi.mock("../../src/utils/copy-public", () => ({
|
|
@@ -132,7 +132,7 @@ vi.mock("../../src/utils/worker-utils", () => ({
|
|
|
132
132
|
if (guardCalled) throw new Error("startWatch has already been called");
|
|
133
133
|
guardCalled = true;
|
|
134
134
|
}),
|
|
135
|
-
|
|
135
|
+
setupWorkerConsola: vi.fn(),
|
|
136
136
|
}));
|
|
137
137
|
|
|
138
138
|
vi.mock("../../src/utils/package-utils", () => ({
|
|
@@ -16,6 +16,13 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
18
|
vi.mock("@simplysm/core-common", () => ({
|
|
19
|
+
env: vi.fn((key: string, value?: string) => {
|
|
20
|
+
if (value !== undefined) {
|
|
21
|
+
process.env[key] = value;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
return process.env[key];
|
|
25
|
+
}),
|
|
19
26
|
err: { message: (e: any) => e?.message ?? String(e) },
|
|
20
27
|
}));
|
|
21
28
|
|
|
@@ -32,7 +39,7 @@ vi.mock("consola", () => ({
|
|
|
32
39
|
|
|
33
40
|
vi.mock("../../src/utils/worker-utils", () => ({
|
|
34
41
|
registerCleanupHandlers: vi.fn(),
|
|
35
|
-
|
|
42
|
+
setupWorkerConsola: vi.fn(),
|
|
36
43
|
}));
|
|
37
44
|
|
|
38
45
|
// @fastify/http-proxy mock
|