@simplysm/sd-cli 14.0.63 → 14.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capacitor/capacitor-android.d.ts +2 -0
- package/dist/capacitor/capacitor-android.d.ts.map +1 -1
- package/dist/capacitor/capacitor-android.js +13 -0
- package/dist/capacitor/capacitor-android.js.map +1 -1
- package/dist/capacitor/capacitor-npm-config.d.ts.map +1 -1
- package/dist/capacitor/capacitor-npm-config.js +2 -6
- package/dist/capacitor/capacitor-npm-config.js.map +1 -1
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +1 -2
- package/dist/electron/electron.js.map +1 -1
- package/package.json +8 -8
- package/src/capacitor/capacitor-android.ts +14 -0
- package/src/capacitor/capacitor-npm-config.ts +2 -6
- package/src/electron/electron.ts +1 -2
- package/tests/angular/ngtsc-build-core.acc.spec.ts +36 -94
- package/tests/capacitor/capacitor-android.spec.ts +65 -28
- package/tests/capacitor/capacitor-build.spec.ts +40 -385
- package/tests/capacitor/capacitor-config-writer.acc.spec.ts +3 -17
- package/tests/capacitor/capacitor-config-writer.spec.ts +3 -17
- package/tests/capacitor/capacitor-init.spec.ts +40 -636
- package/tests/capacitor/capacitor-npm-config.acc.spec.ts +38 -168
- package/tests/capacitor/capacitor-npm-config.spec.ts +33 -71
- package/tests/commands/check.spec.ts +25 -36
- package/tests/commands/deployment-phase.acc.spec.ts +17 -26
- package/tests/commands/git-phase.acc.spec.ts +13 -112
- package/tests/commands/lint.spec.ts +7 -24
- package/tests/commands/post-publish-phase.acc.spec.ts +5 -10
- package/tests/commands/typecheck.spec.ts +43 -65
- package/tests/electron/electron.spec.ts +22 -46
- package/tests/engines/base-engine.spec.ts +4 -13
- package/tests/engines/engine-selection.spec.ts +14 -17
- package/tests/engines/engine-typecheck-selection.acc.spec.ts +13 -16
- package/tests/engines/esbuild-client-engine.acc.spec.ts +36 -40
- package/tests/engines/esbuild-client-engine.spec.ts +4 -23
- package/tests/engines/ngtsc-engine.spec.ts +3 -10
- package/tests/engines/server-esbuild-engine.spec.ts +3 -10
- package/tests/engines/tsc-engine.spec.ts +3 -10
- package/tests/esbuild/esbuild-tsc-plugin.acc.spec.ts +3 -8
- package/tests/esbuild/esbuild-tsc-plugin.spec.ts +3 -8
- package/tests/orchestrators/build-orchestrator.spec.ts +57 -102
- package/tests/orchestrators/dev-orchestrator.spec.ts +68 -109
- package/tests/orchestrators/typecheck-orchestrator.spec.ts +25 -57
- package/tests/orchestrators/watch-orchestrator.spec.ts +73 -99
- package/tests/sd-cli-entry.spec.ts +17 -20
- package/tests/utils/angular-source-file-cache.spec.ts +4 -8
- package/tests/utils/copy-src.spec.ts +9 -20
- package/tests/utils/esbuild-client-config.acc.spec.ts +9 -15
- package/tests/utils/esbuild-client-config.spec.ts +12 -24
- package/tests/utils/esbuild-config.spec.ts +51 -42
- package/tests/utils/lint-core.spec.ts +13 -19
- package/tests/utils/lint-utils.spec.ts +8 -15
- package/tests/utils/lint-with-program.spec.ts +3 -7
- package/tests/utils/ngtsc-build-core.spec.ts +2 -99
- package/tests/utils/orchestrator-utils.spec.ts +7 -20
- package/tests/utils/output-utils.spec.ts +5 -11
- package/tests/utils/sd-config.spec.ts +4 -12
- package/tests/utils/typecheck-env.spec.ts +49 -77
- package/tests/utils/typecheck-non-package.spec.ts +23 -16
- package/tests/workers/build-watch-paths.acc.spec.ts +4 -10
- package/tests/workers/build-watch-paths.spec.ts +4 -9
- package/tests/workers/client-worker.acc.spec.ts +64 -137
- package/tests/workers/client-worker.spec.ts +63 -89
- package/tests/workers/library-build-lint.spec.ts +19 -30
- package/tests/workers/library-build-worker.spec.ts +28 -55
- package/tests/workers/server-esbuild-context.acc.spec.ts +6 -15
- package/tests/workers/server-esbuild-context.spec.ts +7 -16
- package/tests/workers/server-runtime-worker.spec.ts +8 -10
- package/tests/workers/shared-worker-lifecycle.acc.spec.ts +3 -5
- package/tests/workers/shared-worker-lifecycle.spec.ts +4 -5
- package/tests/capacitor/capacitor-icon.spec.ts +0 -285
- package/tests/capacitor/capacitor-run.spec.ts +0 -256
- package/tests/capacitor/capacitor-workspace.spec.ts +0 -203
- package/tests/commands/device.spec.ts +0 -237
- package/tests/commands/publish.spec.ts +0 -1183
- package/tests/utils/external-modules.spec.ts +0 -217
- package/tests/workers/server-build-lint.spec.ts +0 -201
- package/tests/workers/server-build-worker.spec.ts +0 -765
- package/tests/workers/server-watch-manager.acc.spec.ts +0 -162
- package/tests/workers/server-watch-manager.spec.ts +0 -199
|
@@ -1,765 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
//#region Mocks
|
|
5
|
-
|
|
6
|
-
let workerFns: Record<string, (...args: any[]) => any>;
|
|
7
|
-
let mockSend: ReturnType<typeof vi.fn>;
|
|
8
|
-
|
|
9
|
-
// Guard reset function (set by worker-utils mock factory)
|
|
10
|
-
let resetGuard: () => void;
|
|
11
|
-
|
|
12
|
-
// fs mock tracking
|
|
13
|
-
const writtenFiles = new Map<string, string>();
|
|
14
|
-
const mockWriteFileSync = vi.fn((filePath: string, content: string) => {
|
|
15
|
-
writtenFiles.set(filePath, content);
|
|
16
|
-
});
|
|
17
|
-
const mockReadFileSync = vi.fn();
|
|
18
|
-
const mockExistsSync = vi.fn();
|
|
19
|
-
|
|
20
|
-
// FsWatcher mock
|
|
21
|
-
const mockOnChange = vi.fn();
|
|
22
|
-
const mockWatcherClose = vi.fn();
|
|
23
|
-
|
|
24
|
-
// esbuild context mock
|
|
25
|
-
const mockRebuild = vi.fn();
|
|
26
|
-
const mockDispose = vi.fn();
|
|
27
|
-
let mockMetafileInputs: Record<string, unknown> = {};
|
|
28
|
-
|
|
29
|
-
// SdTsCompiler mock (js=false path)
|
|
30
|
-
const mockCompileAsync = vi.fn(() => Promise.resolve({
|
|
31
|
-
program: { getSourceFiles: () => [] },
|
|
32
|
-
builderProgram: {},
|
|
33
|
-
isForAngular: false,
|
|
34
|
-
affectedFiles: undefined,
|
|
35
|
-
diagnostics: [] as unknown[],
|
|
36
|
-
errorCount: 0,
|
|
37
|
-
warningCount: 0,
|
|
38
|
-
errors: undefined as string[] | undefined,
|
|
39
|
-
emitResults: undefined,
|
|
40
|
-
lint: undefined,
|
|
41
|
-
scssErrors: [],
|
|
42
|
-
scssDependencies: new Map(),
|
|
43
|
-
}));
|
|
44
|
-
const MockSdTsCompiler = vi.fn().mockImplementation(function () {
|
|
45
|
-
return { compileAsync: mockCompileAsync };
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const mockCpxSpawnSync = vi.fn().mockReturnValue({ stdout: "v20.11.0", stderr: "", exitCode: 0 });
|
|
49
|
-
|
|
50
|
-
vi.mock("@simplysm/core-node", () => ({
|
|
51
|
-
createWorker: vi.fn((fns: Record<string, Function>) => {
|
|
52
|
-
workerFns = fns as any;
|
|
53
|
-
mockSend = vi.fn();
|
|
54
|
-
return { send: mockSend };
|
|
55
|
-
}),
|
|
56
|
-
FsWatcher: {
|
|
57
|
-
watch: vi.fn(() => Promise.resolve({
|
|
58
|
-
onChange: mockOnChange,
|
|
59
|
-
close: mockWatcherClose,
|
|
60
|
-
})),
|
|
61
|
-
},
|
|
62
|
-
pathx: {
|
|
63
|
-
posix: vi.fn((p: string) => p.replace(/\\/g, "/")),
|
|
64
|
-
posixResolve: vi.fn((...args: string[]) => path.resolve(...args).replace(/\\/g, "/")),
|
|
65
|
-
},
|
|
66
|
-
cpx: {
|
|
67
|
-
spawn: vi.fn().mockResolvedValue({ stdout: "", stderr: "", exitCode: 0 }),
|
|
68
|
-
spawnSync: mockCpxSpawnSync,
|
|
69
|
-
},
|
|
70
|
-
}));
|
|
71
|
-
|
|
72
|
-
vi.mock("esbuild", () => ({
|
|
73
|
-
default: {
|
|
74
|
-
context: vi.fn(() => {
|
|
75
|
-
mockRebuild.mockResolvedValue({
|
|
76
|
-
errors: [],
|
|
77
|
-
warnings: [],
|
|
78
|
-
outputFiles: [],
|
|
79
|
-
metafile: { inputs: mockMetafileInputs, outputs: {} },
|
|
80
|
-
});
|
|
81
|
-
return Promise.resolve({ rebuild: mockRebuild, dispose: mockDispose });
|
|
82
|
-
}),
|
|
83
|
-
build: vi.fn(() => Promise.resolve({
|
|
84
|
-
errors: [],
|
|
85
|
-
warnings: [],
|
|
86
|
-
outputFiles: [{ path: "/workspace/packages/my-server/dist/main.js", text: "export {}" }],
|
|
87
|
-
})),
|
|
88
|
-
},
|
|
89
|
-
formatMessagesSync: (messages: Array<{ text: string }>, _opts: unknown) =>
|
|
90
|
-
messages.map((m) => m.text),
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
vi.mock("fs", () => ({
|
|
94
|
-
default: {
|
|
95
|
-
readFileSync: (...args: unknown[]) => mockReadFileSync(...(args as [string])),
|
|
96
|
-
writeFileSync: (...args: unknown[]) => mockWriteFileSync(...(args as [string, string])),
|
|
97
|
-
existsSync: (...args: unknown[]) => mockExistsSync(...(args as [string])),
|
|
98
|
-
},
|
|
99
|
-
readFileSync: (...args: unknown[]) => mockReadFileSync(...(args as [string])),
|
|
100
|
-
writeFileSync: (...args: unknown[]) => mockWriteFileSync(...(args as [string, string])),
|
|
101
|
-
existsSync: (...args: unknown[]) => mockExistsSync(...(args as [string])),
|
|
102
|
-
}));
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// Mock lockfile content for resolveLockedVersion
|
|
106
|
-
let mockLockfileContent = "";
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
vi.mock("../../src/utils/tsconfig", () => ({
|
|
110
|
-
parseTsconfig: vi.fn(() => ({
|
|
111
|
-
options: { target: 1, module: 99 },
|
|
112
|
-
fileNames: ["/workspace/packages/my-server/src/main.ts"],
|
|
113
|
-
errors: [],
|
|
114
|
-
})),
|
|
115
|
-
getPackageSourceFiles: vi.fn(() => ["/workspace/packages/my-server/src/main.ts"]),
|
|
116
|
-
}));
|
|
117
|
-
|
|
118
|
-
vi.mock("../../src/esbuild/esbuild-config", async (importOriginal) => {
|
|
119
|
-
const actual = await importOriginal<typeof import("../../src/esbuild/esbuild-config")>();
|
|
120
|
-
return {
|
|
121
|
-
...actual,
|
|
122
|
-
collectAllDependencyExternals: vi.fn(() => ({ optionalPeerDeps: [], nativeModules: [] })),
|
|
123
|
-
writeChangedOutputFiles: vi.fn(() => Promise.resolve(true)),
|
|
124
|
-
};
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
vi.mock("../../src/ts-compiler/SdTsCompiler", () => ({
|
|
128
|
-
SdTsCompiler: MockSdTsCompiler,
|
|
129
|
-
}));
|
|
130
|
-
|
|
131
|
-
// tsc plugin mock (build() js=true path uses createTscPlugin)
|
|
132
|
-
const mockTscPlugin = {
|
|
133
|
-
plugin: { name: "sd-tsc", setup: vi.fn() },
|
|
134
|
-
getProgram: vi.fn(),
|
|
135
|
-
getAffectedFiles: vi.fn(),
|
|
136
|
-
getDiagnostics: vi.fn((): unknown[] => []),
|
|
137
|
-
getErrors: vi.fn((): string[] | undefined => undefined),
|
|
138
|
-
getLintResult: vi.fn((): unknown => undefined),
|
|
139
|
-
resetBuilderProgram: vi.fn(),
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
vi.mock("../../src/esbuild/esbuild-tsc-plugin", () => ({
|
|
143
|
-
createTscPlugin: vi.fn(() => mockTscPlugin),
|
|
144
|
-
}));
|
|
145
|
-
|
|
146
|
-
vi.mock("../../src/workers/shared-worker-lifecycle", () => {
|
|
147
|
-
let guardCalled = false;
|
|
148
|
-
resetGuard = () => { guardCalled = false; };
|
|
149
|
-
return {
|
|
150
|
-
setupWorkerLifecycle: vi.fn(() => ({
|
|
151
|
-
logger: { debug: vi.fn(), warn: vi.fn() },
|
|
152
|
-
guardStartWatch: () => {
|
|
153
|
-
if (guardCalled) throw new Error("startWatch can only be called once per Worker");
|
|
154
|
-
guardCalled = true;
|
|
155
|
-
},
|
|
156
|
-
})),
|
|
157
|
-
};
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
vi.mock("../../src/deps/replace-deps/collect-deps", () => ({
|
|
161
|
-
collectDeps: vi.fn(() => ({ workspaceDeps: [], replaceDeps: [] })),
|
|
162
|
-
}));
|
|
163
|
-
|
|
164
|
-
vi.mock("../../src/utils/copy-public", () => ({
|
|
165
|
-
copyPublicFiles: vi.fn(() => Promise.resolve()),
|
|
166
|
-
watchPublicFiles: vi.fn(() => Promise.resolve(undefined)),
|
|
167
|
-
}));
|
|
168
|
-
|
|
169
|
-
//#endregion
|
|
170
|
-
|
|
171
|
-
// Import triggers createWorker, capturing the functions
|
|
172
|
-
await import("../../src/workers/server-build.worker");
|
|
173
|
-
|
|
174
|
-
const esbuild = (await import("esbuild")).default;
|
|
175
|
-
const { FsWatcher } = await import("@simplysm/core-node");
|
|
176
|
-
const { copyPublicFiles, watchPublicFiles } = await import("../../src/utils/copy-public");
|
|
177
|
-
const { collectAllDependencyExternals } =
|
|
178
|
-
await import("../../src/esbuild/esbuild-config");
|
|
179
|
-
|
|
180
|
-
describe("server-build.worker build()", () => {
|
|
181
|
-
const baseBuildInfo = {
|
|
182
|
-
name: "my-server",
|
|
183
|
-
cwd: "/workspace",
|
|
184
|
-
pkgDir: "/workspace/packages/my-server",
|
|
185
|
-
output: { js: true, dts: false },
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
beforeEach(() => {
|
|
189
|
-
writtenFiles.clear();
|
|
190
|
-
mockWriteFileSync.mockClear();
|
|
191
|
-
mockReadFileSync.mockReset();
|
|
192
|
-
mockExistsSync.mockReset();
|
|
193
|
-
vi.mocked(esbuild.build).mockClear();
|
|
194
|
-
vi.mocked(copyPublicFiles).mockClear();
|
|
195
|
-
vi.mocked(collectAllDependencyExternals).mockReturnValue({
|
|
196
|
-
optionalPeerDeps: [],
|
|
197
|
-
nativeModules: [],
|
|
198
|
-
});
|
|
199
|
-
mockCompileAsync.mockResolvedValue({
|
|
200
|
-
program: { getSourceFiles: () => [] },
|
|
201
|
-
builderProgram: {},
|
|
202
|
-
isForAngular: false,
|
|
203
|
-
affectedFiles: undefined,
|
|
204
|
-
diagnostics: [],
|
|
205
|
-
errorCount: 0,
|
|
206
|
-
warningCount: 0,
|
|
207
|
-
errors: undefined,
|
|
208
|
-
emitResults: undefined,
|
|
209
|
-
lint: undefined,
|
|
210
|
-
scssErrors: [],
|
|
211
|
-
scssDependencies: new Map(),
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// Reset tsc plugin mock (used for js=true path)
|
|
215
|
-
mockTscPlugin.getProgram.mockReset();
|
|
216
|
-
mockTscPlugin.getAffectedFiles.mockReset();
|
|
217
|
-
mockTscPlugin.getDiagnostics.mockReset().mockReturnValue([]);
|
|
218
|
-
mockTscPlugin.getErrors.mockReset().mockReturnValue(undefined);
|
|
219
|
-
mockTscPlugin.getLintResult.mockReset().mockReturnValue(undefined);
|
|
220
|
-
mockTscPlugin.resetBuilderProgram.mockReset();
|
|
221
|
-
|
|
222
|
-
// Reset lockfile content and cache
|
|
223
|
-
mockLockfileContent = "";
|
|
224
|
-
// Clear lockfile cache (module-level variable in worker)
|
|
225
|
-
// The cache is per-import, so re-importing clears it
|
|
226
|
-
|
|
227
|
-
mockExistsSync.mockImplementation((fp: string) => {
|
|
228
|
-
if (String(fp).endsWith("pnpm-lock.yaml")) return mockLockfileContent !== "";
|
|
229
|
-
return false;
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
mockReadFileSync.mockImplementation((filePath: string) => {
|
|
233
|
-
const fp = String(filePath);
|
|
234
|
-
if (fp.endsWith("pnpm-lock.yaml")) {
|
|
235
|
-
return mockLockfileContent;
|
|
236
|
-
}
|
|
237
|
-
if (fp.endsWith("package.json")) {
|
|
238
|
-
return JSON.stringify({
|
|
239
|
-
name: "@simplysm/my-server",
|
|
240
|
-
version: "1.0.0",
|
|
241
|
-
type: "module",
|
|
242
|
-
dependencies: {},
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
return "";
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// Acceptance: production build includes worker bundle plugin
|
|
250
|
-
it("includes worker bundle plugin in esbuild.build() plugins", async () => {
|
|
251
|
-
await workerFns["build"](baseBuildInfo);
|
|
252
|
-
|
|
253
|
-
expect(esbuild.build).toHaveBeenCalledWith(
|
|
254
|
-
expect.objectContaining({
|
|
255
|
-
plugins: [
|
|
256
|
-
expect.objectContaining({ name: "sd-worker-bundle" }),
|
|
257
|
-
mockTscPlugin.plugin,
|
|
258
|
-
],
|
|
259
|
-
}),
|
|
260
|
-
);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// Acceptance: esbuild + typecheck parallel execution
|
|
264
|
-
it("runs esbuild and tsc in parallel for server build", async () => {
|
|
265
|
-
const result = await workerFns["build"](baseBuildInfo);
|
|
266
|
-
|
|
267
|
-
expect(result.build.success).toBe(true);
|
|
268
|
-
expect(result.mainJsPath).toBe(path.resolve(baseBuildInfo.pkgDir, "dist", "main.js").replace(/\\/g, "/"));
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// Acceptance: type error detected via tsc plugin (js=true)
|
|
272
|
-
it("reports typecheck error in build field", async () => {
|
|
273
|
-
mockTscPlugin.getErrors.mockReturnValue(["TS2345: type error"]);
|
|
274
|
-
mockTscPlugin.getDiagnostics.mockReturnValue([{ code: 2345, category: 1 }]);
|
|
275
|
-
|
|
276
|
-
const result = await workerFns["build"](baseBuildInfo);
|
|
277
|
-
|
|
278
|
-
expect(result.build.success).toBe(false);
|
|
279
|
-
expect(result.build.errors).toContain("TS2345: type error");
|
|
280
|
-
expect(result.build.diagnostics).toHaveLength(1);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
// Acceptance: esbuild + tsc both error — merged
|
|
284
|
-
it("merges esbuild and tsc errors when both fail", async () => {
|
|
285
|
-
vi.mocked(esbuild.build).mockResolvedValueOnce({
|
|
286
|
-
errors: [{ text: "esbuild syntax error" }],
|
|
287
|
-
warnings: [],
|
|
288
|
-
outputFiles: [],
|
|
289
|
-
} as any);
|
|
290
|
-
mockTscPlugin.getErrors.mockReturnValue(["TS2322: type mismatch"]);
|
|
291
|
-
|
|
292
|
-
const result = await workerFns["build"](baseBuildInfo);
|
|
293
|
-
|
|
294
|
-
expect(result.build.success).toBe(false);
|
|
295
|
-
expect(result.build.errors).toContain("esbuild syntax error");
|
|
296
|
-
expect(result.build.errors).toContain("TS2322: type mismatch");
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// Acceptance: diagnostics from tsc plugin (js=true)
|
|
300
|
-
it("includes diagnostics from tsc plugin in build result", async () => {
|
|
301
|
-
mockTscPlugin.getDiagnostics.mockReturnValue([
|
|
302
|
-
{ code: 2322, category: 1, messageText: "Type mismatch" },
|
|
303
|
-
]);
|
|
304
|
-
|
|
305
|
-
const result = await workerFns["build"](baseBuildInfo);
|
|
306
|
-
|
|
307
|
-
expect(result.build.diagnostics).toHaveLength(1);
|
|
308
|
-
expect(result.build.diagnostics[0]).toEqual(
|
|
309
|
-
expect.objectContaining({ code: 2322 }),
|
|
310
|
-
);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// Acceptance: js=false uses SdTsCompiler directly
|
|
314
|
-
it("uses SdTsCompiler directly when output.js=false", async () => {
|
|
315
|
-
mockCompileAsync.mockResolvedValueOnce({
|
|
316
|
-
program: { getSourceFiles: () => [] },
|
|
317
|
-
builderProgram: {},
|
|
318
|
-
isForAngular: false,
|
|
319
|
-
affectedFiles: undefined,
|
|
320
|
-
diagnostics: [{ code: 2345, category: 1 }],
|
|
321
|
-
errorCount: 1,
|
|
322
|
-
warningCount: 0,
|
|
323
|
-
errors: ["TS2345: type error"],
|
|
324
|
-
emitResults: undefined,
|
|
325
|
-
lint: undefined,
|
|
326
|
-
scssErrors: [],
|
|
327
|
-
scssDependencies: new Map(),
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
const result = await workerFns["build"]({
|
|
331
|
-
...baseBuildInfo,
|
|
332
|
-
output: { js: false, dts: true },
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
expect(result.build.success).toBe(false);
|
|
336
|
-
expect(result.build.errors).toContain("TS2345: type error");
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Acceptance: esbuild error detected
|
|
340
|
-
it("reports esbuild error in build field", async () => {
|
|
341
|
-
vi.mocked(esbuild.build).mockResolvedValueOnce({
|
|
342
|
-
errors: [{ text: "syntax error" }],
|
|
343
|
-
warnings: [],
|
|
344
|
-
outputFiles: [],
|
|
345
|
-
} as any);
|
|
346
|
-
|
|
347
|
-
const result = await workerFns["build"](baseBuildInfo);
|
|
348
|
-
|
|
349
|
-
expect(result.build.success).toBe(false);
|
|
350
|
-
expect(result.build.errors).toContain("syntax error");
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
// Unit: esbuild exception handling
|
|
354
|
-
it("handles esbuild exception gracefully", async () => {
|
|
355
|
-
vi.mocked(esbuild.build).mockRejectedValueOnce(new Error("esbuild crash"));
|
|
356
|
-
|
|
357
|
-
const result = await workerFns["build"](baseBuildInfo);
|
|
358
|
-
|
|
359
|
-
expect(result.build.success).toBe(false);
|
|
360
|
-
expect(result.build.errors).toContain("esbuild crash");
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// --- Production artifacts ---
|
|
364
|
-
|
|
365
|
-
describe("production artifacts", () => {
|
|
366
|
-
it("writes .config.json with configs data", async () => {
|
|
367
|
-
await workerFns["build"]({
|
|
368
|
-
...baseBuildInfo,
|
|
369
|
-
configs: { db: { host: "localhost", port: 5432 } },
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
const configPath = path.join(baseBuildInfo.pkgDir, "dist", ".config.json");
|
|
373
|
-
expect(writtenFiles.has(configPath)).toBe(true);
|
|
374
|
-
expect(JSON.parse(writtenFiles.get(configPath)!)).toEqual({ db: { host: "localhost", port: 5432 } });
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it("generates dist/package.json with externals using versions from pnpm-lock.yaml", async () => {
|
|
378
|
-
vi.mocked(collectAllDependencyExternals).mockReturnValue({
|
|
379
|
-
optionalPeerDeps: [],
|
|
380
|
-
nativeModules: ["bcrypt"],
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
mockLockfileContent = [
|
|
384
|
-
"packages:",
|
|
385
|
-
"",
|
|
386
|
-
" 'bcrypt@5.1.1':",
|
|
387
|
-
" resolution: {integrity: sha512-abc}",
|
|
388
|
-
"",
|
|
389
|
-
" 'some-pkg@2.0.3':",
|
|
390
|
-
" resolution: {integrity: sha512-def}",
|
|
391
|
-
].join("\n");
|
|
392
|
-
|
|
393
|
-
mockExistsSync.mockImplementation((fp: string) => {
|
|
394
|
-
if (String(fp).endsWith("pnpm-lock.yaml")) return true;
|
|
395
|
-
return false;
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
await workerFns["build"]({
|
|
399
|
-
...baseBuildInfo,
|
|
400
|
-
externals: ["some-pkg"],
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
const pkgJsonPath = path.join(baseBuildInfo.pkgDir, "dist", "package.json");
|
|
404
|
-
const pkg = JSON.parse(writtenFiles.get(pkgJsonPath)!);
|
|
405
|
-
expect(pkg.name).toBe("@simplysm/my-server");
|
|
406
|
-
expect(pkg.dependencies["bcrypt"]).toBe("5.1.1");
|
|
407
|
-
expect(pkg.dependencies["some-pkg"]).toBe("2.0.3");
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it("generates dist/openssl.cnf with legacy provider config", async () => {
|
|
411
|
-
await workerFns["build"](baseBuildInfo);
|
|
412
|
-
|
|
413
|
-
const opensslPath = path.join(baseBuildInfo.pkgDir, "dist", "openssl.cnf");
|
|
414
|
-
expect(writtenFiles.has(opensslPath)).toBe(true);
|
|
415
|
-
expect(writtenFiles.get(opensslPath)!).toContain("[legacy_sect]");
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
it("generates dist/pm2.config.cjs when pm2 option is provided", async () => {
|
|
419
|
-
await workerFns["build"]({
|
|
420
|
-
...baseBuildInfo,
|
|
421
|
-
pm2: { name: "my-app", ignoreWatchPaths: ["logs"] },
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
const pm2Path = path.join(baseBuildInfo.pkgDir, "dist", "pm2.config.cjs");
|
|
425
|
-
expect(writtenFiles.has(pm2Path)).toBe(true);
|
|
426
|
-
expect(writtenFiles.get(pm2Path)!).toContain('"my-app"');
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
it("generates dist/mise.toml when packageManager=mise", async () => {
|
|
430
|
-
mockExistsSync.mockImplementation((filePath: string) =>
|
|
431
|
-
String(filePath).endsWith("mise.toml"),
|
|
432
|
-
);
|
|
433
|
-
mockReadFileSync.mockImplementation((filePath: string) => {
|
|
434
|
-
if (String(filePath).endsWith("mise.toml")) return '[tools]\nnode = "22.5.0"';
|
|
435
|
-
return JSON.stringify({ name: "@simplysm/my-server", version: "1.0.0", type: "module" });
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
await workerFns["build"]({
|
|
439
|
-
...baseBuildInfo,
|
|
440
|
-
packageManager: "mise",
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
const misePath = path.join(baseBuildInfo.pkgDir, "dist", "mise.toml");
|
|
444
|
-
expect(writtenFiles.get(misePath)).toContain('node = "22.5.0"');
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
it("adds volta field to dist/package.json when packageManager=volta", async () => {
|
|
448
|
-
await workerFns["build"]({
|
|
449
|
-
...baseBuildInfo,
|
|
450
|
-
packageManager: "volta",
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
const pkgJsonPath = path.join(baseBuildInfo.pkgDir, "dist", "package.json");
|
|
454
|
-
const pkg = JSON.parse(writtenFiles.get(pkgJsonPath)!);
|
|
455
|
-
expect(pkg.volta).toBeDefined();
|
|
456
|
-
expect(pkg.volta.node).toBe("v20.11.0");
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
it("includes nativeModules and manual externals in dist/package.json but excludes missing optional peer deps", async () => {
|
|
460
|
-
vi.mocked(collectAllDependencyExternals).mockReturnValue({
|
|
461
|
-
optionalPeerDeps: ["opt-dep"],
|
|
462
|
-
nativeModules: ["native-mod"],
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
mockLockfileContent = [
|
|
466
|
-
"packages:",
|
|
467
|
-
"",
|
|
468
|
-
" 'native-mod@2.4.0':",
|
|
469
|
-
" resolution: {integrity: sha512-b}",
|
|
470
|
-
"",
|
|
471
|
-
" 'manual-ext@3.1.0':",
|
|
472
|
-
" resolution: {integrity: sha512-c}",
|
|
473
|
-
].join("\n");
|
|
474
|
-
|
|
475
|
-
mockExistsSync.mockImplementation((fp: string) => {
|
|
476
|
-
if (String(fp).endsWith("pnpm-lock.yaml")) return true;
|
|
477
|
-
return false;
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
await workerFns["build"]({
|
|
481
|
-
...baseBuildInfo,
|
|
482
|
-
externals: ["manual-ext"],
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
const pkgJsonPath = path.join(baseBuildInfo.pkgDir, "dist", "package.json");
|
|
486
|
-
const pkg = JSON.parse(writtenFiles.get(pkgJsonPath)!);
|
|
487
|
-
expect(pkg.dependencies["opt-dep"]).toBeUndefined();
|
|
488
|
-
expect(pkg.dependencies["native-mod"]).toBe("2.4.0");
|
|
489
|
-
expect(pkg.dependencies["manual-ext"]).toBe("3.1.0");
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
// Unit: reports error when a nativeModule/manual external is missing from lockfile
|
|
493
|
-
it("reports error for external dependency not in lockfile", async () => {
|
|
494
|
-
vi.mocked(collectAllDependencyExternals).mockReturnValue({
|
|
495
|
-
optionalPeerDeps: [],
|
|
496
|
-
nativeModules: ["unknown-native"],
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
mockLockfileContent = [
|
|
500
|
-
"packages:",
|
|
501
|
-
"",
|
|
502
|
-
" 'other-pkg@1.0.0':",
|
|
503
|
-
" resolution: {integrity: sha512-abc}",
|
|
504
|
-
].join("\n");
|
|
505
|
-
|
|
506
|
-
mockExistsSync.mockImplementation((fp: string) => {
|
|
507
|
-
if (String(fp).endsWith("pnpm-lock.yaml")) return true;
|
|
508
|
-
return false;
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
const result = await workerFns["build"](baseBuildInfo);
|
|
512
|
-
expect(result.build.success).toBe(false);
|
|
513
|
-
expect(result.build.errors[0]).toContain("unknown-native");
|
|
514
|
-
expect(result.build.errors[0]).toContain("not found in pnpm-lock.yaml");
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
// Unit: uses locked version for native module externals
|
|
518
|
-
it("uses locked version for native module externals", async () => {
|
|
519
|
-
vi.mocked(collectAllDependencyExternals).mockReturnValue({
|
|
520
|
-
optionalPeerDeps: [],
|
|
521
|
-
nativeModules: ["native-opt"],
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
mockLockfileContent = [
|
|
525
|
-
"packages:",
|
|
526
|
-
"",
|
|
527
|
-
" 'native-opt@4.2.1':",
|
|
528
|
-
" resolution: {integrity: sha512-xyz}",
|
|
529
|
-
].join("\n");
|
|
530
|
-
|
|
531
|
-
mockExistsSync.mockImplementation((fp: string) => {
|
|
532
|
-
if (String(fp).endsWith("pnpm-lock.yaml")) return true;
|
|
533
|
-
return false;
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
await workerFns["build"](baseBuildInfo);
|
|
537
|
-
|
|
538
|
-
const pkgJsonPath = path.join(baseBuildInfo.pkgDir, "dist", "package.json");
|
|
539
|
-
const pkg = JSON.parse(writtenFiles.get(pkgJsonPath)!);
|
|
540
|
-
expect(pkg.dependencies["native-opt"]).toBe("4.2.1");
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
describe("server-build.worker startWatch()", () => {
|
|
547
|
-
const watchInfo = {
|
|
548
|
-
name: "my-server",
|
|
549
|
-
cwd: "/workspace",
|
|
550
|
-
pkgDir: "/workspace/packages/my-server",
|
|
551
|
-
output: { js: true, dts: false },
|
|
552
|
-
};
|
|
553
|
-
|
|
554
|
-
beforeEach(() => {
|
|
555
|
-
resetGuard();
|
|
556
|
-
mockMetafileInputs = {};
|
|
557
|
-
writtenFiles.clear();
|
|
558
|
-
mockWriteFileSync.mockClear();
|
|
559
|
-
mockRebuild.mockClear();
|
|
560
|
-
mockDispose.mockClear();
|
|
561
|
-
mockOnChange.mockClear();
|
|
562
|
-
mockWatcherClose.mockClear();
|
|
563
|
-
mockSend.mockClear();
|
|
564
|
-
vi.mocked(esbuild.context).mockClear();
|
|
565
|
-
vi.mocked(FsWatcher.watch).mockClear();
|
|
566
|
-
vi.mocked(watchPublicFiles).mockClear();
|
|
567
|
-
mockCompileAsync.mockResolvedValue({
|
|
568
|
-
program: { getSourceFiles: () => [] },
|
|
569
|
-
builderProgram: {},
|
|
570
|
-
isForAngular: false,
|
|
571
|
-
affectedFiles: undefined,
|
|
572
|
-
diagnostics: [],
|
|
573
|
-
errorCount: 0,
|
|
574
|
-
warningCount: 0,
|
|
575
|
-
errors: undefined,
|
|
576
|
-
emitResults: undefined,
|
|
577
|
-
lint: undefined,
|
|
578
|
-
scssErrors: [],
|
|
579
|
-
scssDependencies: new Map(),
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
// Reset tsc plugin mock (used for watch mode rebuild)
|
|
583
|
-
mockTscPlugin.getProgram.mockReset();
|
|
584
|
-
mockTscPlugin.getAffectedFiles.mockReset();
|
|
585
|
-
mockTscPlugin.getDiagnostics.mockReset().mockReturnValue([]);
|
|
586
|
-
mockTscPlugin.getErrors.mockReset().mockReturnValue(undefined);
|
|
587
|
-
mockTscPlugin.resetBuilderProgram.mockReset();
|
|
588
|
-
|
|
589
|
-
mockReadFileSync.mockImplementation((filePath: string) => {
|
|
590
|
-
if (String(filePath).endsWith("package.json")) {
|
|
591
|
-
return JSON.stringify({
|
|
592
|
-
name: "@simplysm/my-server",
|
|
593
|
-
version: "1.0.0",
|
|
594
|
-
type: "module",
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
return "";
|
|
598
|
-
});
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
// Acceptance: initial build with typecheck
|
|
602
|
-
it("sends build event with build results after initial build", async () => {
|
|
603
|
-
await workerFns["startWatch"](watchInfo);
|
|
604
|
-
|
|
605
|
-
expect(mockSend).toHaveBeenCalledWith("build", expect.objectContaining({
|
|
606
|
-
build: expect.objectContaining({ success: true }),
|
|
607
|
-
mainJsPath: path.resolve(watchInfo.pkgDir, "dist", "main.js").replace(/\\/g, "/"),
|
|
608
|
-
}));
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
// Acceptance: metafile-based filtering
|
|
612
|
-
it("skips rebuild when changed file is not in metafile.inputs", async () => {
|
|
613
|
-
mockMetafileInputs = { "packages/my-server/src/main.ts": {} };
|
|
614
|
-
|
|
615
|
-
await workerFns["startWatch"](watchInfo);
|
|
616
|
-
|
|
617
|
-
const onChangeHandler = mockOnChange.mock.calls[0][1] as (
|
|
618
|
-
changes: Array<{ event: string; path: string }>,
|
|
619
|
-
) => Promise<void>;
|
|
620
|
-
|
|
621
|
-
mockRebuild.mockClear();
|
|
622
|
-
mockSend.mockClear();
|
|
623
|
-
const absPath = path.resolve("/workspace", "packages/my-server/src/unrelated.ts").replace(/\\/g, "/");
|
|
624
|
-
await onChangeHandler([{ event: "change", path: absPath }]);
|
|
625
|
-
|
|
626
|
-
// buildStart must NOT be sent when rebuild is skipped (LOGIC-001 fix)
|
|
627
|
-
expect(mockSend).not.toHaveBeenCalledWith("buildStart", expect.anything());
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
// Acceptance: watch mode doesn't generate production artifacts
|
|
631
|
-
it("writes .config.json but not production files in watch mode", async () => {
|
|
632
|
-
await workerFns["startWatch"]({
|
|
633
|
-
...watchInfo,
|
|
634
|
-
configs: { key: "value" },
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
const configPath = path.join(watchInfo.pkgDir, "dist", ".config.json");
|
|
638
|
-
expect(writtenFiles.has(configPath)).toBe(true);
|
|
639
|
-
|
|
640
|
-
// Production files should NOT be generated
|
|
641
|
-
const pkgJsonPath = path.join(watchInfo.pkgDir, "dist", "package.json");
|
|
642
|
-
expect(writtenFiles.has(pkgJsonPath)).toBe(false);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
// Unit: guard prevents duplicate startWatch
|
|
646
|
-
it("prevents duplicate startWatch calls", async () => {
|
|
647
|
-
await workerFns["startWatch"](watchInfo);
|
|
648
|
-
await expect(workerFns["startWatch"](watchInfo)).rejects.toThrow("can only be called once");
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
// Acceptance: esbuild context creation failure leaves safe state (LOGIC-001)
|
|
652
|
-
it("allows tsc-only rebuilds after esbuild context recreation failure", async () => {
|
|
653
|
-
// Provide metafile inputs so subsequent change passes the metafile filter
|
|
654
|
-
mockMetafileInputs = { "packages/my-server/src/main.ts": {} };
|
|
655
|
-
|
|
656
|
-
await workerFns["startWatch"](watchInfo);
|
|
657
|
-
|
|
658
|
-
const onChangeHandler = mockOnChange.mock.calls[0][1] as (
|
|
659
|
-
changes: Array<{ event: string; path: string }>,
|
|
660
|
-
) => Promise<void>;
|
|
661
|
-
|
|
662
|
-
// After dispose, rebuild should throw (simulates real disposed context)
|
|
663
|
-
mockDispose.mockImplementation(() => {
|
|
664
|
-
mockRebuild.mockRejectedValue(new Error("Build context already disposed"));
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
// Make context() throw to simulate creation failure
|
|
668
|
-
vi.mocked(esbuild.context).mockRejectedValueOnce(new Error("context creation failed"));
|
|
669
|
-
mockSend.mockClear();
|
|
670
|
-
|
|
671
|
-
// File add triggers context recreation → fails → sends "error"
|
|
672
|
-
await onChangeHandler([{ event: "add", path: "/workspace/packages/my-server/src/new.ts" }]);
|
|
673
|
-
|
|
674
|
-
// Subsequent file change should work without "disposed" errors
|
|
675
|
-
mockSend.mockClear();
|
|
676
|
-
const absPath = path.resolve("/workspace", "packages/my-server/src/main.ts").replace(/\\/g, "/");
|
|
677
|
-
await onChangeHandler([{ event: "change", path: absPath }]);
|
|
678
|
-
|
|
679
|
-
// Should NOT get "disposed" error
|
|
680
|
-
const errorCalls = mockSend.mock.calls.filter((c) => c[0] === "error");
|
|
681
|
-
for (const [, data] of errorCalls) {
|
|
682
|
-
expect((data as { message: string }).message).not.toContain("disposed");
|
|
683
|
-
}
|
|
684
|
-
// Build event should be sent (tsc-only result)
|
|
685
|
-
const buildCalls = mockSend.mock.calls.filter((c) => c[0] === "build");
|
|
686
|
-
expect(buildCalls.length).toBeGreaterThanOrEqual(1);
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
// Acceptance: rebuildAll js=true — single esbuildCtx.rebuild() call, tsc not called directly
|
|
690
|
-
it("uses esbuildCtx.rebuild() without direct tsc call in watch mode rebuild", async () => {
|
|
691
|
-
mockMetafileInputs = { "packages/my-server/src/main.ts": {} };
|
|
692
|
-
|
|
693
|
-
await workerFns["startWatch"](watchInfo);
|
|
694
|
-
|
|
695
|
-
const onChangeHandler = mockOnChange.mock.calls[0][1] as (
|
|
696
|
-
changes: Array<{ event: string; path: string }>,
|
|
697
|
-
) => Promise<void>;
|
|
698
|
-
|
|
699
|
-
mockRebuild.mockClear();
|
|
700
|
-
mockSend.mockClear();
|
|
701
|
-
|
|
702
|
-
const absPath = path.resolve("/workspace", "packages/my-server/src/main.ts").replace(/\\/g, "/");
|
|
703
|
-
await onChangeHandler([{ event: "change", path: absPath }]);
|
|
704
|
-
|
|
705
|
-
// esbuild rebuild should have been called (tsc triggered by plugin inside)
|
|
706
|
-
expect(mockRebuild).toHaveBeenCalled();
|
|
707
|
-
// Build event should be sent
|
|
708
|
-
expect(mockSend).toHaveBeenCalledWith("build", expect.objectContaining({
|
|
709
|
-
build: expect.objectContaining({ success: true }),
|
|
710
|
-
}));
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
// Acceptance: startWatch passes tsc options to createContext with worker plugin
|
|
714
|
-
it("passes worker bundle plugin and tsc plugin to esbuildCtx.createContext", async () => {
|
|
715
|
-
await workerFns["startWatch"]({
|
|
716
|
-
...watchInfo,
|
|
717
|
-
output: { js: true, dts: true, env: "node" as any, includeTests: true },
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
expect(esbuild.context).toHaveBeenCalledWith(
|
|
721
|
-
expect.objectContaining({
|
|
722
|
-
plugins: [
|
|
723
|
-
expect.objectContaining({ name: "sd-worker-bundle" }),
|
|
724
|
-
mockTscPlugin.plugin,
|
|
725
|
-
],
|
|
726
|
-
}),
|
|
727
|
-
);
|
|
728
|
-
});
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
describe("server-build.worker stopWatch()", () => {
|
|
732
|
-
beforeEach(() => {
|
|
733
|
-
resetGuard();
|
|
734
|
-
mockDispose.mockClear();
|
|
735
|
-
mockWatcherClose.mockClear();
|
|
736
|
-
mockCompileAsync.mockResolvedValue({
|
|
737
|
-
program: { getSourceFiles: () => [] },
|
|
738
|
-
builderProgram: {},
|
|
739
|
-
isForAngular: false,
|
|
740
|
-
affectedFiles: undefined,
|
|
741
|
-
diagnostics: [],
|
|
742
|
-
errorCount: 0,
|
|
743
|
-
warningCount: 0,
|
|
744
|
-
errors: undefined,
|
|
745
|
-
emitResults: undefined,
|
|
746
|
-
lint: undefined,
|
|
747
|
-
scssErrors: [],
|
|
748
|
-
scssDependencies: new Map(),
|
|
749
|
-
});
|
|
750
|
-
mockReadFileSync.mockImplementation(() => JSON.stringify({ name: "x", version: "1.0.0", type: "module" }));
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
it("cleans up esbuild context and FsWatcher", async () => {
|
|
754
|
-
await workerFns["startWatch"]({
|
|
755
|
-
name: "my-server",
|
|
756
|
-
cwd: "/workspace",
|
|
757
|
-
pkgDir: "/workspace/packages/my-server",
|
|
758
|
-
output: { js: true, dts: false },
|
|
759
|
-
});
|
|
760
|
-
await workerFns["stopWatch"]();
|
|
761
|
-
|
|
762
|
-
expect(mockDispose).toHaveBeenCalled();
|
|
763
|
-
expect(mockWatcherClose).toHaveBeenCalled();
|
|
764
|
-
});
|
|
765
|
-
});
|