@simplysm/sd-cli 14.0.95 → 14.0.97

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.
Files changed (99) hide show
  1. package/dist/commands/init/generators/client.d.ts.map +1 -1
  2. package/dist/commands/init/generators/client.js +12 -0
  3. package/dist/commands/init/generators/client.js.map +1 -1
  4. package/dist/commands/init/generators/server.d.ts.map +1 -1
  5. package/dist/commands/init/generators/server.js +1 -0
  6. package/dist/commands/init/generators/server.js.map +1 -1
  7. package/dist/commands/init/normalize.d.ts.map +1 -1
  8. package/dist/commands/init/normalize.js +1 -0
  9. package/dist/commands/init/normalize.js.map +1 -1
  10. package/dist/commands/init/prompts.d.ts.map +1 -1
  11. package/dist/commands/init/prompts.js +8 -1
  12. package/dist/commands/init/prompts.js.map +1 -1
  13. package/dist/commands/init/types.d.ts +3 -0
  14. package/dist/commands/init/types.d.ts.map +1 -1
  15. package/dist/engines/EsbuildClientEngine.d.ts.map +1 -1
  16. package/dist/engines/EsbuildClientEngine.js +1 -0
  17. package/dist/engines/EsbuildClientEngine.js.map +1 -1
  18. package/dist/esbuild/esbuild-client-config.d.ts.map +1 -1
  19. package/dist/esbuild/esbuild-client-config.js +2 -11
  20. package/dist/esbuild/esbuild-client-config.js.map +1 -1
  21. package/dist/esbuild/esbuild-postcss-plugin.d.ts +4 -0
  22. package/dist/esbuild/esbuild-postcss-plugin.d.ts.map +1 -1
  23. package/dist/esbuild/esbuild-postcss-plugin.js +15 -0
  24. package/dist/esbuild/esbuild-postcss-plugin.js.map +1 -1
  25. package/dist/esbuild/esbuild-ssr-config.d.ts +27 -0
  26. package/dist/esbuild/esbuild-ssr-config.d.ts.map +1 -0
  27. package/dist/esbuild/esbuild-ssr-config.js +113 -0
  28. package/dist/esbuild/esbuild-ssr-config.js.map +1 -0
  29. package/dist/sd-config.types.d.ts +7 -0
  30. package/dist/sd-config.types.d.ts.map +1 -1
  31. package/dist/ssg/prerender.d.ts +19 -0
  32. package/dist/ssg/prerender.d.ts.map +1 -0
  33. package/dist/ssg/prerender.js +43 -0
  34. package/dist/ssg/prerender.js.map +1 -0
  35. package/dist/workers/client.worker.d.ts +2 -0
  36. package/dist/workers/client.worker.d.ts.map +1 -1
  37. package/dist/workers/client.worker.js +21 -0
  38. package/dist/workers/client.worker.js.map +1 -1
  39. package/package.json +6 -6
  40. package/src/commands/init/generators/client.ts +53 -0
  41. package/src/commands/init/generators/server.ts +5 -0
  42. package/src/commands/init/normalize.ts +1 -0
  43. package/src/commands/init/prompts.ts +9 -1
  44. package/src/commands/init/templates/client/package.json.hbs +2 -1
  45. package/src/commands/init/templates/client/src/app/home/home.view.ts.hbs +1 -1
  46. package/src/commands/init/templates/client/src/app/home/master/role-permission/role-permission.detail.ts.hbs +221 -0
  47. package/src/commands/init/templates/client/src/app/home/master/role-permission/role-permission.view.ts.hbs +106 -0
  48. package/src/commands/init/templates/client/src/app/home/master/role-permission/role.detail.ts.hbs +277 -0
  49. package/src/commands/init/templates/client/src/app/home/master/role-permission/role.list.ts.hbs +537 -0
  50. package/src/commands/init/templates/client/src/app/home/master/user.detail.ts.hbs +337 -0
  51. package/src/commands/init/templates/client/src/app/home/master/user.list.ts.hbs +540 -0
  52. package/src/commands/init/templates/client/src/app/home/my-info/my-info.detail.ts.hbs +4 -6
  53. package/src/commands/init/templates/client/src/app/home/system/data-log/data-log.list.ts.hbs +355 -0
  54. package/src/commands/init/templates/client/src/app/home/system/system-log/system-log.list.ts.hbs +382 -0
  55. package/src/commands/init/templates/client/src/app/login/login.view.ts.hbs +3 -4
  56. package/src/commands/init/templates/client/src/app.root.ts.hbs +9 -3
  57. package/src/commands/init/templates/client/src/index.html.hbs +1 -0
  58. package/src/commands/init/templates/client/src/main.server.ts.hbs +24 -0
  59. package/src/commands/init/templates/client/src/main.ts.hbs +36 -18
  60. package/src/commands/init/templates/client/src/modals/text-view.modal.ts.hbs +30 -0
  61. package/src/commands/init/templates/client/src/routes.ts.hbs +22 -0
  62. package/src/commands/init/templates/client-common/src/index.ts.hbs +6 -4
  63. package/src/commands/init/templates/client-common/src/providers/app-auth.provider.ts.hbs +3 -3
  64. package/src/commands/init/templates/client-common/src/providers/app-orm.provider.ts.hbs +0 -11
  65. package/src/commands/init/templates/client-common/src/providers/app-service.provider.ts.hbs +24 -10
  66. package/src/commands/init/templates/client-common/src/providers/app-shared-data.provider.ts.hbs +2 -2
  67. package/src/commands/init/templates/common/package.json.hbs +2 -1
  68. package/src/commands/init/templates/common/src/app-structure.ts.hbs +7 -2
  69. package/src/commands/init/templates/common/src/auth-info-changed.event.ts.hbs +3 -1
  70. package/src/commands/init/templates/common/src/db/db-context.ts.hbs +2 -2
  71. package/src/commands/init/templates/common/src/db/tables/master/user.ts.hbs +2 -2
  72. package/src/commands/init/templates/common/src/db/tables/system/role.ts.hbs +1 -0
  73. package/src/commands/init/templates/common/src/index.ts.hbs +1 -1
  74. package/src/commands/init/templates/server/src/index.ts.hbs +3 -0
  75. package/src/commands/init/templates/server/src/main.ts.hbs +15 -1
  76. package/src/commands/init/templates/server/src/services/auth.service.ts.hbs +28 -22
  77. package/src/commands/init/templates/server/src/services/dev.service.ts.hbs +5 -5
  78. package/src/commands/init/templates/server/src/services/user.service.ts.hbs +191 -0
  79. package/src/commands/init/templates/workspace-root/sd.config.ts.hbs +3 -0
  80. package/src/commands/init/types.ts +3 -0
  81. package/src/engines/EsbuildClientEngine.ts +1 -0
  82. package/src/esbuild/esbuild-client-config.ts +2 -12
  83. package/src/esbuild/esbuild-postcss-plugin.ts +18 -0
  84. package/src/esbuild/esbuild-ssr-config.ts +149 -0
  85. package/src/sd-config.types.ts +7 -0
  86. package/src/ssg/prerender.ts +65 -0
  87. package/src/workers/client.worker.ts +26 -0
  88. package/tests/engines/base-engine.spec.ts +1 -26
  89. package/tests/init/__snapshots__/render.spec.ts.snap +38 -20
  90. package/tests/init/render.spec.ts +113 -33
  91. package/tests/utils/hmr-client-script.acc.spec.ts +0 -21
  92. package/tests/angular/vite-angular-plugin.spec.ts +0 -102
  93. package/tests/engines/engine-adapter-isolation.spec.ts +0 -79
  94. package/tests/runtime/signal-handler.spec.ts +0 -21
  95. package/tests/utils/angular-build.spec.ts +0 -109
  96. package/tests/utils/esbuild-client-config.acc.spec.ts +0 -438
  97. package/tests/utils/esbuild-client-config.spec.ts +0 -659
  98. package/tests/utils/hmr-client-script.spec.ts +0 -44
  99. package/tests/utils/tsconfig-angular.spec.ts +0 -9
@@ -1,438 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import path from "path";
3
-
4
- // 외부 npm + Node 표준 모듈은 ESM namespace immutable이라 vi.mock 유지
5
- const mockContext = {
6
- rebuild: vi.fn(),
7
- watch: vi.fn(),
8
- dispose: vi.fn(),
9
- };
10
-
11
- vi.mock("esbuild", () => ({
12
- default: {
13
- context: vi.fn(() => Promise.resolve(mockContext)),
14
- },
15
- }));
16
-
17
- vi.mock("browserslist-to-esbuild", () => ({
18
- default: vi.fn(() => ["chrome61"]),
19
- }));
20
-
21
- vi.mock("module", async (importOriginal) => {
22
- const actual = await importOriginal<typeof import("module")>();
23
- return {
24
- ...actual,
25
- createRequire: vi.fn(() => (name: string) => {
26
- if (name === "nonexistent-plugin") {
27
- throw new Error(`Cannot find module '${name}'`);
28
- }
29
- return (..._args: any[]) => ({ postcssPlugin: name });
30
- }),
31
- };
32
- });
33
-
34
- import * as angularPluginMod from "../../src/esbuild/esbuild-angular-compiler-plugin";
35
- import * as transformStylesheetMod from "../../src/angular/client-transform-stylesheet";
36
-
37
- const mockAngularPlugin = { name: "sd-angular-compiler" };
38
- const mockTransformStylesheet = vi.fn();
39
-
40
- vi.spyOn(angularPluginMod, "createAngularCompilerPlugin").mockReturnValue(mockAngularPlugin as any);
41
- vi.spyOn(transformStylesheetMod, "createClientTransformStylesheet").mockReturnValue(mockTransformStylesheet as any);
42
-
43
- const { createClientEsbuildContext, ClientSourceFileCache } = await import(
44
- "../../src/esbuild/esbuild-client-config"
45
- );
46
- const esbuild = (await import("esbuild")).default;
47
- const { createAngularCompilerPlugin } = await import(
48
- "../../src/esbuild/esbuild-angular-compiler-plugin"
49
- );
50
- const { createClientTransformStylesheet } = await import(
51
- "../../src/angular/client-transform-stylesheet"
52
- );
53
- const browserslistToEsbuild = (await import("browserslist-to-esbuild")).default;
54
-
55
- describe("createClientEsbuildContext — Acceptance", () => {
56
- beforeEach(() => {
57
- vi.clearAllMocks();
58
- });
59
-
60
- // Scenario: Angular main.ts를 ESM 번들로 빌드
61
- // + AngularCompilerPluginOptions로 플러그인 생성
62
- // + ClientSourceFileCache로 증분 캐시
63
- // + dev 모드 Angular 플래그 + 소스맵
64
- it("dev 모드: ESM 번들 설정, Angular 플래그, 소스맵, ClientSourceFileCache로 esbuild context 생성", async () => {
65
- const result = await createClientEsbuildContext({
66
- pkgDir: "/workspace/packages/my-app",
67
- cwd: "/workspace",
68
- mode: "dev",
69
- });
70
-
71
- // esbuild.context가 호출됨
72
- expect(esbuild.context).toHaveBeenCalledOnce();
73
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
74
-
75
- // ESM 번들 설정
76
- expect(esbuildOptions.entryPoints).toEqual([
77
- path.join("/workspace/packages/my-app", "src", "main.ts"),
78
- ]);
79
- expect(esbuildOptions.bundle).toBe(true);
80
- expect(esbuildOptions.splitting).toBe(true);
81
- expect(esbuildOptions.format).toBe("esm");
82
- expect(esbuildOptions.platform).toBe("browser");
83
- expect(esbuildOptions.outdir).toBe(
84
- path.join("/workspace/packages/my-app", "dist"),
85
- );
86
- expect(esbuildOptions.metafile).toBe(true);
87
- expect(esbuildOptions.write).toBe(true);
88
-
89
- // dev 소스맵: linked
90
- expect(esbuildOptions.sourcemap).toBe("linked");
91
-
92
- // Angular 플래그 (dev)
93
- expect(esbuildOptions.define).toBeDefined();
94
- expect(esbuildOptions.define!["ngJitMode"]).toBe("false");
95
- expect(esbuildOptions.define!["ngDevMode"]).toBeUndefined();
96
- expect(esbuildOptions.define!["ngHmrMode"]).toBeUndefined();
97
-
98
- // ClientSourceFileCache 인스턴스
99
- expect(result.sourceFileCache).toBeInstanceOf(ClientSourceFileCache);
100
-
101
- // createAngularCompilerPlugin 호출 검증
102
- expect(createAngularCompilerPlugin).toHaveBeenCalledOnce();
103
- const pluginOpts = vi.mocked(createAngularCompilerPlugin).mock.calls[0][0];
104
-
105
- // AngularCompilerPluginOptions
106
- expect(pluginOpts.tsconfig).toBe(
107
- path.join("/workspace/packages/my-app", "tsconfig.json"),
108
- );
109
- expect(pluginOpts.sourcemap).toBe(true);
110
- expect(pluginOpts.advancedOptimizations).toBe(false);
111
- expect(pluginOpts.thirdPartySourcemaps).toBe(true);
112
- expect(pluginOpts.incremental).toBe(true);
113
- expect(pluginOpts.sourceFileCache).toBe(result.sourceFileCache);
114
- expect(pluginOpts.typeScriptFileCache).toBe(result.sourceFileCache.typeScriptFileCache);
115
- expect(pluginOpts.loadResultCache).toBe(result.sourceFileCache.loadResultCache);
116
- expect(pluginOpts.includeTestMetadata).toBe(true);
117
- expect(pluginOpts.persistentCachePath).toBe(
118
- path.join("/workspace/packages/my-app", ".angular", "cache"),
119
- );
120
-
121
- // transformStylesheet 콜백 전달
122
- expect(pluginOpts.transformStylesheet).toBe(mockTransformStylesheet);
123
- expect(createClientTransformStylesheet).toHaveBeenCalledOnce();
124
-
125
- // 반환값
126
- expect(result.context).toBe(mockContext);
127
-
128
- // angularPlugin이 plugins에 포함됨
129
- expect(esbuildOptions.plugins).toContainEqual(mockAngularPlugin);
130
- });
131
-
132
- // Scenario: dev 모드 + templateUpdates + non-legacy → ngHmrMode가 "true"
133
- it("dev 모드 + templateUpdates + non-legacy: ngHmrMode가 true로 정의된다", async () => {
134
- await createClientEsbuildContext({
135
- pkgDir: "/workspace/packages/my-app",
136
- cwd: "/workspace",
137
- mode: "dev",
138
- templateUpdates: new Map<string, string>(),
139
- });
140
-
141
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
142
- expect(esbuildOptions.define!["ngHmrMode"]).toBe("true");
143
- });
144
-
145
- // Scenario: build 모드 Angular 플래그 + 소스맵
146
- it("build 모드: Angular 플래그 모두 false, 소스맵 비활성화, advancedOptimizations", async () => {
147
- await createClientEsbuildContext({
148
- pkgDir: "/workspace/packages/my-app",
149
- cwd: "/workspace",
150
- mode: "build",
151
- });
152
-
153
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
154
-
155
- // build 소스맵: false
156
- expect(esbuildOptions.sourcemap).toBe(false);
157
-
158
- // Angular 플래그 (build)
159
- expect(esbuildOptions.define!["ngDevMode"]).toBe("false");
160
- expect(esbuildOptions.define!["ngJitMode"]).toBe("false");
161
- expect(esbuildOptions.define!["ngHmrMode"]).toBe("false");
162
-
163
- // AngularCompilerPluginOptions
164
- const pluginOpts = vi.mocked(createAngularCompilerPlugin).mock.calls[0][0];
165
- expect(pluginOpts.sourcemap).toBe(false);
166
- expect(pluginOpts.advancedOptimizations).toBe(true);
167
- expect(pluginOpts.thirdPartySourcemaps).toBe(false);
168
- expect(pluginOpts.incremental).toBe(false);
169
- expect(pluginOpts.includeTestMetadata).toBe(false);
170
- });
171
-
172
- // Scenario: 커스텀 env 주입
173
- it("env 설정 시 import.meta.env 객체로 define에 주입", async () => {
174
- await createClientEsbuildContext({
175
- pkgDir: "/workspace/packages/my-app",
176
- cwd: "/workspace",
177
- mode: "build",
178
- env: { API_URL: "https://api.example.com", DEBUG: "true" },
179
- });
180
-
181
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
182
- expect(esbuildOptions.define!["import.meta.env"]).toBe(
183
- JSON.stringify({ API_URL: "https://api.example.com", DEBUG: "true" }),
184
- );
185
- });
186
-
187
- // Scenario: PostCSS 설정 — sd-postcss 등록 + transformStylesheet에 postcssPlugins 전달
188
- it("postcssPlugins 전달 시 sd-postcss 플러그인이 등록되고 transformStylesheet에도 전달된다", async () => {
189
- await createClientEsbuildContext({
190
- pkgDir: "/workspace/packages/my-app",
191
- cwd: "/workspace",
192
- mode: "build",
193
- postcssPlugins: [["autoprefixer", {}]],
194
- });
195
-
196
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
197
- const pluginNames = esbuildOptions.plugins!.map((p: any) => p.name);
198
- expect(pluginNames).toContain("sd-postcss");
199
-
200
- const transformOpts = vi.mocked(createClientTransformStylesheet).mock.calls[0][0];
201
- expect(transformOpts.postcssPlugins).toBeDefined();
202
- expect(transformOpts.postcssPlugins).toHaveLength(1);
203
- });
204
-
205
- // Scenario: 프로덕션 일회성 빌드
206
- it("context.rebuild() 호출 후 결과 반환, context.dispose()로 정리", async () => {
207
- const mockBuildResult = {
208
- metafile: { outputs: { "dist/main.js": {} } },
209
- errors: [],
210
- warnings: [],
211
- };
212
- mockContext.rebuild.mockResolvedValueOnce(mockBuildResult);
213
-
214
- const { context } = await createClientEsbuildContext({
215
- pkgDir: "/workspace/packages/my-app",
216
- cwd: "/workspace",
217
- mode: "build",
218
- });
219
-
220
- const buildResult = await context.rebuild();
221
- expect(buildResult.metafile).toBeDefined();
222
-
223
- await context.dispose();
224
- expect(mockContext.dispose).toHaveBeenCalledOnce();
225
- });
226
-
227
- // Scenario: watch 모드 증분 빌드 — onEnd 콜백으로 빌드 결과 수신
228
- it("onEnd 콜백이 sd-on-end 플러그인으로 등록되고, context.watch()로 watch 시작 가능", async () => {
229
- const onEndSpy = vi.fn();
230
-
231
- const { context } = await createClientEsbuildContext({
232
- pkgDir: "/workspace/packages/my-app",
233
- cwd: "/workspace",
234
- mode: "dev",
235
- onEnd: onEndSpy,
236
- });
237
-
238
- // sd-on-end 플러그인이 등록됨
239
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
240
- const onEndPlugin = esbuildOptions.plugins!.find(
241
- (p: any) => p.name === "sd-on-end",
242
- );
243
- expect(onEndPlugin).toBeDefined();
244
-
245
- // watch 시작 가능
246
- await context.watch();
247
- expect(mockContext.watch).toHaveBeenCalledOnce();
248
- });
249
-
250
- // Scenario: async onEnd 콜백의 Promise가 sd-on-end 플러그인을 통해 esbuild에 전달된다
251
- it("async onEnd 콜백의 Promise가 sd-on-end 플러그인을 통해 esbuild에 전달된다", async () => {
252
- const asyncOnEnd = vi.fn().mockResolvedValue(undefined);
253
- await createClientEsbuildContext({
254
- pkgDir: "/workspace/packages/my-app",
255
- cwd: "/workspace",
256
- mode: "dev",
257
- onEnd: asyncOnEnd,
258
- });
259
-
260
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
261
- const sdOnEndPlugin = esbuildOptions.plugins!.find(
262
- (p: any) => p.name === "sd-on-end",
263
- )!;
264
-
265
- let capturedCallback!: (result: any) => any;
266
- (sdOnEndPlugin as any).setup({
267
- onEnd(cb: (result: any) => any) {
268
- capturedCallback = cb;
269
- },
270
- });
271
-
272
- const fakeResult = { errors: [], warnings: [] };
273
- const returnValue = capturedCallback(fakeResult);
274
-
275
- expect(returnValue).toBeInstanceOf(Promise);
276
- await returnValue;
277
- expect(asyncOnEnd).toHaveBeenCalledWith(fakeResult);
278
- });
279
-
280
- // onEnd가 없으면 sd-on-end 플러그인 미등록
281
- it("onEnd 미전달 시 sd-on-end 플러그인이 없음", async () => {
282
- await createClientEsbuildContext({
283
- pkgDir: "/workspace/packages/my-app",
284
- cwd: "/workspace",
285
- mode: "dev",
286
- });
287
-
288
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
289
- const onEndPlugin = esbuildOptions.plugins!.find(
290
- (p: any) => p.name === "sd-on-end",
291
- );
292
- expect(onEndPlugin).toBeUndefined();
293
- });
294
-
295
- // Scenario: browserslist 미설정 시 기본 target "es2022"
296
- it("browserslist 미설정 시 esbuild target이 [es2022]", async () => {
297
- await createClientEsbuildContext({
298
- pkgDir: "/workspace/packages/my-app",
299
- cwd: "/workspace",
300
- mode: "build",
301
- });
302
-
303
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
304
- expect(esbuildOptions.target).toEqual(["es2022"]);
305
- });
306
-
307
- // Scenario: browserslist 문자열 설정 시 변환
308
- it("browserslist 문자열 설정 시 browserslistToEsbuild 결과가 target에 적용", async () => {
309
- vi.mocked(browserslistToEsbuild).mockReturnValueOnce(["chrome61"]);
310
-
311
- await createClientEsbuildContext({
312
- pkgDir: "/workspace/packages/my-app",
313
- cwd: "/workspace",
314
- mode: "build",
315
- browserslist: "Chrome 61",
316
- });
317
-
318
- expect(browserslistToEsbuild).toHaveBeenCalledWith(["Chrome 61"]);
319
-
320
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
321
- expect(esbuildOptions.target).toEqual(["chrome61"]);
322
- });
323
-
324
- // Scenario: polyfills 경로 전달 시 entryPoints에 추가
325
- it("polyfills 전달 시 entryPoints에 main.ts와 함께 절대경로로 추가", async () => {
326
- await createClientEsbuildContext({
327
- pkgDir: "/workspace/packages/my-app",
328
- cwd: "/workspace",
329
- mode: "build",
330
- polyfills: ["src/polyfills.ts"],
331
- });
332
-
333
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
334
- expect(esbuildOptions.entryPoints).toEqual([
335
- path.join("/workspace/packages/my-app", "src", "main.ts"),
336
- path.join("/workspace/packages/my-app", "src/polyfills.ts"),
337
- ]);
338
- });
339
-
340
- // Scenario: polyfills 미전달 시 entryPoints 변경 없음
341
- it("polyfills 미전달 시 entryPoints는 main.ts만 포함", async () => {
342
- await createClientEsbuildContext({
343
- pkgDir: "/workspace/packages/my-app",
344
- cwd: "/workspace",
345
- mode: "build",
346
- });
347
-
348
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
349
- expect(esbuildOptions.entryPoints).toEqual([
350
- path.join("/workspace/packages/my-app", "src", "main.ts"),
351
- ]);
352
- });
353
-
354
- // Scenario: dev 모드 출력 네이밍 (entry/asset은 해시 없음, chunk은 해시 포함)
355
- it("dev 모드: entryNames, assetNames는 [name], chunkNames는 [name]-[hash]", async () => {
356
- await createClientEsbuildContext({
357
- pkgDir: "/workspace/packages/my-app",
358
- cwd: "/workspace",
359
- mode: "dev",
360
- });
361
-
362
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
363
- expect(esbuildOptions.entryNames).toBe("[name]");
364
- expect(esbuildOptions.chunkNames).toBe("[name]-[hash]");
365
- expect(esbuildOptions.assetNames).toBe("[name]");
366
- });
367
-
368
- // Scenario: build 모드 출력 네이밍 (해시 포함)
369
- it("build 모드: entryNames, chunkNames, assetNames 모두 [name]-[hash]", async () => {
370
- await createClientEsbuildContext({
371
- pkgDir: "/workspace/packages/my-app",
372
- cwd: "/workspace",
373
- mode: "build",
374
- });
375
-
376
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
377
- expect(esbuildOptions.entryNames).toBe("[name]-[hash]");
378
- expect(esbuildOptions.chunkNames).toBe("[name]-[hash]");
379
- expect(esbuildOptions.assetNames).toBe("[name]-[hash]");
380
- });
381
-
382
- // Scenario: browserslist 배열 설정 시 변환
383
- it("browserslist 배열 설정 시 browserslistToEsbuild 결과가 target에 적용", async () => {
384
- vi.mocked(browserslistToEsbuild).mockReturnValueOnce(["chrome61", "firefox60"]);
385
-
386
- await createClientEsbuildContext({
387
- pkgDir: "/workspace/packages/my-app",
388
- cwd: "/workspace",
389
- mode: "build",
390
- browserslist: ["Chrome 61", "Firefox 60"],
391
- });
392
-
393
- expect(browserslistToEsbuild).toHaveBeenCalledWith(["Chrome 61", "Firefox 60"]);
394
-
395
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
396
- expect(esbuildOptions.target).toEqual(["chrome61", "firefox60"]);
397
- });
398
-
399
- // Scenario: esbuild context에 tsconfig 옵션이 전달된다
400
- it("esbuild context에 tsconfig 옵션이 기본값으로 pkgDir/tsconfig.json 전달된다", async () => {
401
- await createClientEsbuildContext({
402
- pkgDir: "/workspace/packages/my-app",
403
- cwd: "/workspace",
404
- mode: "dev",
405
- });
406
-
407
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
408
- expect(esbuildOptions.tsconfig).toBe(
409
- path.join("/workspace/packages/my-app", "tsconfig.json"),
410
- );
411
- });
412
-
413
- // Scenario: customPlugins가 angularPlugin 이전에 배치된다 (onStart에서 sourceFileCache 무효화 선행 필요)
414
- it("plugins 배열이 [customPlugins, angularPlugin, scssPlugin, onEndPlugin] 순서로 구성된다", async () => {
415
- const customPlugin = { name: "custom", setup: vi.fn() };
416
- await createClientEsbuildContext({
417
- pkgDir: "/workspace/packages/my-app",
418
- cwd: "/workspace",
419
- mode: "dev",
420
- plugins: [customPlugin],
421
- onEnd: vi.fn(),
422
- });
423
-
424
- const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
425
- const pluginNames = esbuildOptions.plugins!.map((p: any) => p.name);
426
-
427
- expect(pluginNames).toContain("custom");
428
- expect(pluginNames).toContain("sd-angular-compiler");
429
- expect(pluginNames).toContain("sd-scss");
430
- expect(pluginNames[pluginNames.length - 1]).toBe("sd-on-end");
431
-
432
- const customIdx = pluginNames.indexOf("custom");
433
- const angularIdx = pluginNames.indexOf("sd-angular-compiler");
434
- const scssIdx = pluginNames.indexOf("sd-scss");
435
- expect(customIdx).toBeLessThan(angularIdx);
436
- expect(angularIdx).toBeLessThan(scssIdx);
437
- });
438
- });