@simplysm/sd-cli 14.0.13 → 14.0.14

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 (35) hide show
  1. package/dist/angular/vite-angular-plugin.d.ts +3 -1
  2. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  3. package/dist/angular/vite-angular-plugin.js +4 -3
  4. package/dist/angular/vite-angular-plugin.js.map +1 -1
  5. package/dist/capacitor/capacitor.d.ts.map +1 -1
  6. package/dist/capacitor/capacitor.js +7 -0
  7. package/dist/capacitor/capacitor.js.map +1 -1
  8. package/dist/engines/ViteEngine.d.ts +2 -2
  9. package/dist/engines/ViteEngine.d.ts.map +1 -1
  10. package/dist/engines/ViteEngine.js +4 -2
  11. package/dist/engines/ViteEngine.js.map +1 -1
  12. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  13. package/dist/orchestrators/BuildOrchestrator.js +3 -39
  14. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  15. package/dist/sd-config.types.d.ts +2 -0
  16. package/dist/sd-config.types.d.ts.map +1 -1
  17. package/dist/utils/vite-config.d.ts +2 -0
  18. package/dist/utils/vite-config.d.ts.map +1 -1
  19. package/dist/utils/vite-config.js +16 -9
  20. package/dist/utils/vite-config.js.map +1 -1
  21. package/dist/workers/client.worker.d.ts +2 -0
  22. package/dist/workers/client.worker.d.ts.map +1 -1
  23. package/dist/workers/client.worker.js +6 -3
  24. package/dist/workers/client.worker.js.map +1 -1
  25. package/package.json +6 -5
  26. package/src/angular/vite-angular-plugin.ts +7 -4
  27. package/src/capacitor/capacitor.ts +11 -0
  28. package/src/engines/ViteEngine.ts +4 -2
  29. package/src/orchestrators/BuildOrchestrator.ts +3 -39
  30. package/src/sd-config.types.ts +2 -0
  31. package/src/utils/vite-config.ts +27 -16
  32. package/src/workers/client.worker.ts +8 -3
  33. package/tests/orchestrators/build-orchestrator.spec.ts +7 -145
  34. package/tests/utils/vite-config.spec.ts +99 -0
  35. package/tests/workers/client-worker.spec.ts +2 -1
@@ -271,7 +271,7 @@ describe("BuildOrchestrator.start", () => {
271
271
 
272
272
  // BuildEngine should be created and run() called
273
273
  expect(createBuildEngine).toHaveBeenCalledOnce();
274
- expect(mockEngines[0].run).toHaveBeenCalledWith({ js: true, dts: true, lint: true });
274
+ expect(mockEngines[0].run).toHaveBeenCalledWith({ js: true, dts: true, lint: false });
275
275
  expect(mockEngines[0].stop).toHaveBeenCalled();
276
276
  });
277
277
 
@@ -624,7 +624,7 @@ describe("BuildOrchestrator client build", () => {
624
624
  expect.any(Object),
625
625
  );
626
626
  const engineMock = vi.mocked(createBuildEngine).mock.results[0].value;
627
- expect(engineMock.run).toHaveBeenCalledWith({ js: true, dts: false, lint: true });
627
+ expect(engineMock.run).toHaveBeenCalledWith({ js: true, dts: false, lint: false });
628
628
  expect(engineMock.stop).toHaveBeenCalled();
629
629
  });
630
630
 
@@ -852,7 +852,7 @@ describe("BuildOrchestrator native build integration (Slice 1)", () => {
852
852
  // ViteEngine should have been called
853
853
  expect(createBuildEngine).toHaveBeenCalled();
854
854
  const engineMock = vi.mocked(createBuildEngine).mock.results[0].value;
855
- expect(engineMock.run).toHaveBeenCalledWith({ js: true, dts: false, lint: true });
855
+ expect(engineMock.run).toHaveBeenCalledWith({ js: true, dts: false, lint: false });
856
856
 
857
857
  // Capacitor should have been created, initialized, and built
858
858
  expect(Capacitor.create).toHaveBeenCalledWith(
@@ -980,8 +980,8 @@ describe("BuildOrchestrator native build integration (Slice 1)", () => {
980
980
  const { runLintInWorker } = await import("../../src/utils/lint-utils");
981
981
 
982
982
  describe("BuildOrchestrator lint integration", () => {
983
- // Scenario: build에서 패키지 빌드 lint 함께 실행된다
984
- it("passes lint:true to engine.run for all package types", async () => {
983
+ // build에서 lint를 실행하지 않는다 (lint check에서만 실행)
984
+ it("passes lint:false to engine.run for all package types", async () => {
985
985
  setupDefaults({
986
986
  packages: {
987
987
  "core-common": { target: "neutral", publish: { type: "npm" } },
@@ -994,15 +994,13 @@ describe("BuildOrchestrator lint integration", () => {
994
994
  await orchestrator.initialize();
995
995
  await orchestrator.start();
996
996
 
997
- // All 3 engines should receive lint: true
998
997
  expect(mockEngines).toHaveLength(3);
999
998
  for (const engine of mockEngines) {
1000
999
  const runArgs = engine.run.mock.calls[0][0];
1001
- expect(runArgs.lint).toBe(true);
1000
+ expect(runArgs.lint).toBe(false);
1002
1001
  }
1003
1002
  });
1004
1003
 
1005
- // Scenario: build에서 기존 runLint() 병렬 태스크가 제거된다
1006
1004
  it("does not call runLint (separate lint worker)", async () => {
1007
1005
  setupDefaults({
1008
1006
  packages: {
@@ -1017,32 +1015,7 @@ describe("BuildOrchestrator lint integration", () => {
1017
1015
  expect(runLintInWorker).not.toHaveBeenCalled();
1018
1016
  });
1019
1017
 
1020
- // Scenario: build lint 에러가 빌드 결과 출력에 포함된다
1021
- it("sets hasError when lint fails in engine result", async () => {
1022
- setupDefaults({
1023
- packages: {
1024
- "core-common": { target: "neutral", publish: { type: "npm" } },
1025
- },
1026
- });
1027
- vi.mocked(createBuildEngine).mockReturnValue({
1028
- run: vi.fn().mockResolvedValue({
1029
- success: true,
1030
- build: { success: true, errors: [], warnings: [], diagnostics: [] },
1031
- lint: { success: false, errorCount: 3, warningCount: 0, formattedOutput: "lint errors here" },
1032
- }),
1033
- startWatch: vi.fn(),
1034
- stop: vi.fn().mockResolvedValue(undefined),
1035
- } as any);
1036
-
1037
- const orchestrator = new BuildOrchestrator({ targets: [], options: [] });
1038
- await orchestrator.initialize();
1039
- const hasError = await orchestrator.start();
1040
-
1041
- expect(hasError).toBe(true);
1042
- });
1043
-
1044
- // Scenario: build에서 scripts 패키지는 제외된다
1045
- it("excludes scripts packages (no lint for scripts)", async () => {
1018
+ it("excludes scripts packages", async () => {
1046
1019
  setupDefaults({
1047
1020
  packages: {
1048
1021
  "core-common": { target: "neutral", publish: { type: "npm" } },
@@ -1054,7 +1027,6 @@ describe("BuildOrchestrator lint integration", () => {
1054
1027
  await orchestrator.initialize();
1055
1028
  await orchestrator.start();
1056
1029
 
1057
- // Only core-common should have engine (scripts excluded)
1058
1030
  expect(mockEngines).toHaveLength(1);
1059
1031
  expect(runLintInWorker).not.toHaveBeenCalled();
1060
1032
  });
@@ -1062,114 +1034,4 @@ describe("BuildOrchestrator lint integration", () => {
1062
1034
 
1063
1035
  //#endregion
1064
1036
 
1065
- //#region Feature 2.1 Slice 3: build typeLabel에 lint 분기 추가
1066
-
1067
- describe("Feature 2.1: build에서 lint 에러가 'lint' 라벨로 출력", () => {
1068
- it("build에서 lint 에러가 'lint' 라벨로 출력된다", async () => {
1069
- setupDefaults({
1070
- packages: {
1071
- "core-common": { target: "neutral", publish: { type: "npm" } },
1072
- },
1073
- });
1074
- vi.mocked(createBuildEngine).mockReturnValue({
1075
- run: vi.fn().mockResolvedValue({
1076
- success: true,
1077
- build: { success: true, errors: [], warnings: [], diagnostics: [] },
1078
- lint: { success: false, errorCount: 1, warningCount: 0, formattedOutput: "no-unused-vars" },
1079
- }),
1080
- startWatch: vi.fn(),
1081
- stop: vi.fn().mockResolvedValue(undefined),
1082
- } as any);
1083
-
1084
- const orchestrator = new BuildOrchestrator({ targets: [], options: [] });
1085
- await orchestrator.initialize();
1086
- await orchestrator.start();
1087
-
1088
- // formatBuildMessages should have been called with "lint" label
1089
- const { formatBuildMessages } = await import("../../src/utils/output-utils");
1090
- const fmtCalls = vi.mocked(formatBuildMessages).mock.calls;
1091
- const lintCall = fmtCalls.find((c) => c[1] === "lint");
1092
- expect(lintCall).toBeDefined();
1093
- expect(lintCall![0]).toBe("core-common");
1094
- });
1095
-
1096
- it("build에서 JS 빌드 에러는 target 라벨(neutral) 유지", async () => {
1097
- setupDefaults({
1098
- packages: {
1099
- "core-common": { target: "neutral", publish: { type: "npm" } },
1100
- },
1101
- });
1102
- vi.mocked(createBuildEngine).mockReturnValue({
1103
- run: vi.fn().mockResolvedValue({
1104
- success: false,
1105
- build: { success: false, errors: ["Module not found"], warnings: [], diagnostics: [] },
1106
- }),
1107
- startWatch: vi.fn(),
1108
- stop: vi.fn().mockResolvedValue(undefined),
1109
- } as any);
1110
-
1111
- const orchestrator = new BuildOrchestrator({ targets: [], options: [] });
1112
- await orchestrator.initialize();
1113
- await orchestrator.start();
1114
-
1115
- const { formatBuildMessages } = await import("../../src/utils/output-utils");
1116
- const fmtCalls = vi.mocked(formatBuildMessages).mock.calls;
1117
- const jsCall = fmtCalls.find((c) => c[1] === "neutral");
1118
- expect(jsCall).toBeDefined();
1119
- });
1120
-
1121
- it("build에서 빌드 에러는 target 라벨 유지", async () => {
1122
- setupDefaults({
1123
- packages: {
1124
- "core-common": { target: "neutral", publish: { type: "npm" } },
1125
- },
1126
- });
1127
- vi.mocked(createBuildEngine).mockReturnValue({
1128
- run: vi.fn().mockResolvedValue({
1129
- success: false,
1130
- build: { success: false, errors: ["Type error"], warnings: [], diagnostics: [] },
1131
- }),
1132
- startWatch: vi.fn(),
1133
- stop: vi.fn().mockResolvedValue(undefined),
1134
- } as any);
1135
-
1136
- const orchestrator = new BuildOrchestrator({ targets: [], options: [] });
1137
- await orchestrator.initialize();
1138
- await orchestrator.start();
1139
-
1140
- const { formatBuildMessages } = await import("../../src/utils/output-utils");
1141
- const fmtCalls = vi.mocked(formatBuildMessages).mock.calls;
1142
- const buildCall = fmtCalls.find((c) => c[1] === "neutral");
1143
- expect(buildCall).toBeDefined();
1144
- });
1145
-
1146
- it("build에서 lint 성공 시 에러 출력 없음", async () => {
1147
- setupDefaults({
1148
- packages: {
1149
- "core-common": { target: "neutral", publish: { type: "npm" } },
1150
- },
1151
- });
1152
- vi.mocked(createBuildEngine).mockReturnValue({
1153
- run: vi.fn().mockResolvedValue({
1154
- success: true,
1155
- build: { success: true, errors: [], warnings: [], diagnostics: [] },
1156
- lint: { success: true, errorCount: 0, warningCount: 0, formattedOutput: "" },
1157
- }),
1158
- startWatch: vi.fn(),
1159
- stop: vi.fn().mockResolvedValue(undefined),
1160
- } as any);
1161
-
1162
- const orchestrator = new BuildOrchestrator({ targets: [], options: [] });
1163
- await orchestrator.initialize();
1164
- await orchestrator.start();
1165
-
1166
- // No error log about lint
1167
- const errorCalls = mockLogger.error.mock.calls;
1168
- const lintErrorCall = errorCalls.find(
1169
- (c: unknown[]) => typeof c[0] === "string" && (c[0]).includes("lint"),
1170
- );
1171
- expect(lintErrorCall).toBeUndefined();
1172
- });
1173
- });
1174
-
1175
1037
  //#endregion
@@ -11,6 +11,11 @@ vi.mock("../../src/angular/vite-angular-plugin.js", () => ({
11
11
  sdAngularPlugin: mockSdAngularPlugin,
12
12
  }));
13
13
 
14
+ const mockSolidPlugin = vi.fn(() => ({ name: "vite-plugin-solid" }));
15
+ vi.mock("vite-plugin-solid", () => ({
16
+ default: mockSolidPlugin,
17
+ }));
18
+
14
19
  vi.mock("../../src/utils/vite-scope-watch-plugin.js", () => ({
15
20
  sdScopeWatchPlugin: vi.fn(() => ({ name: "sd-scope-watch-plugin" })),
16
21
  }));
@@ -577,6 +582,55 @@ describe("createClientViteConfig", () => {
577
582
  expect(scopePlugin).toBeUndefined();
578
583
  });
579
584
 
585
+ // --- framework selection (Feature 1.1: client-framework-selection) ---
586
+
587
+ // Acceptance: Scenario "Solid 프레임워크 선택"
588
+ it("uses solidPlugin when framework is 'solid'", async () => {
589
+ const config = await createClientViteConfig({
590
+ ...createDefaultOptions(),
591
+ framework: "solid",
592
+ });
593
+
594
+ const plugins = config.plugins as Array<{ name: string }>;
595
+ expect(plugins.find((p) => p.name === "vite-plugin-solid")).toBeDefined();
596
+ expect(mockSolidPlugin).toHaveBeenCalled();
597
+ expect(mockSdAngularPlugin).not.toHaveBeenCalled();
598
+ });
599
+
600
+ // Acceptance: Scenario "framework 미지정 시 기본값"
601
+ it("uses sdAngularPlugin when framework is not specified", async () => {
602
+ await createClientViteConfig(createDefaultOptions());
603
+
604
+ expect(mockSdAngularPlugin).toHaveBeenCalled();
605
+ expect(mockSolidPlugin).not.toHaveBeenCalled();
606
+ });
607
+
608
+ // Acceptance: Scenario "Angular 프레임워크 명시 선택"
609
+ it("uses sdAngularPlugin when framework is 'angular'", async () => {
610
+ await createClientViteConfig({
611
+ ...createDefaultOptions(),
612
+ framework: "angular",
613
+ });
614
+
615
+ expect(mockSdAngularPlugin).toHaveBeenCalled();
616
+ expect(mockSolidPlugin).not.toHaveBeenCalled();
617
+ });
618
+
619
+ // Acceptance: Scenario "Solid 빌드에서 PostCSS inline 플러그인 미적용"
620
+ it("does not add sdPostCssInlinePlugin when framework is 'solid' even with postCssPlugins", async () => {
621
+ const fakePlugin = { postcssPlugin: "autoprefixer" };
622
+ const config = await createClientViteConfig({
623
+ ...createDefaultOptions(),
624
+ framework: "solid",
625
+ postCssPlugins: [fakePlugin],
626
+ });
627
+
628
+ const plugins = config.plugins as Array<{ name: string }>;
629
+ expect(plugins.find((p) => p.name === "sd-postcss-inline")).toBeUndefined();
630
+ // 하지만 css.postcss는 여전히 설정된다 (외부 CSS 파일용)
631
+ expect(config.css?.postcss).toEqual({ plugins: [fakePlugin] });
632
+ });
633
+
580
634
  // Acceptance: Scenario "pwa 필드 미설정 시 기본값"
581
635
  it("uses default manifest values from pkgName when pwa is undefined", async () => {
582
636
  await createClientViteConfig(createDefaultOptions());
@@ -595,4 +649,49 @@ describe("createClientViteConfig", () => {
595
649
  }),
596
650
  );
597
651
  });
652
+
653
+ // --- legacyModule dev mode (Feature: fix-legacy-ngdevmode) ---
654
+
655
+ // Acceptance: Scenario "legacyModule: true + dev 명령 실행 시 sdAngularPlugin에 dev: true 전달"
656
+ it("passes dev: true to sdAngularPlugin when mode is dev with legacyModule", async () => {
657
+ await createClientViteConfig({
658
+ ...createDefaultOptions(),
659
+ mode: "dev",
660
+ legacyModule: true,
661
+ watch: true,
662
+ });
663
+
664
+ expect(mockSdAngularPlugin).toHaveBeenCalledWith(
665
+ expect.objectContaining({ dev: true }),
666
+ );
667
+ });
668
+
669
+ // Acceptance: Scenario "legacyModule dev에서 build output 설정이 적용된다"
670
+ it("applies build output settings when mode is dev with legacyModule", async () => {
671
+ const config = await createClientViteConfig({
672
+ ...createDefaultOptions(),
673
+ mode: "dev",
674
+ legacyModule: true,
675
+ watch: true,
676
+ });
677
+
678
+ expect(config.build?.outDir).toMatch(/my-client[\\/]dist$/);
679
+ expect(config.build?.watch).toEqual({});
680
+ expect(config.build?.emptyOutDir).toBe(false);
681
+ expect(config.build?.minify).toBe(false);
682
+ });
683
+
684
+ // Unit: legacyModule dev에서 PWA가 추가되지 않는다
685
+ it("does not add VitePWA plugin when mode is dev with legacyModule", async () => {
686
+ const config = await createClientViteConfig({
687
+ ...createDefaultOptions(),
688
+ mode: "dev",
689
+ legacyModule: true,
690
+ watch: true,
691
+ });
692
+
693
+ const plugins = config.plugins as Array<{ name: string }>;
694
+ const pwaPlugin = plugins.find((p) => p.name === "vite-plugin-pwa");
695
+ expect(pwaPlugin).toBeUndefined();
696
+ });
598
697
  });
@@ -228,9 +228,10 @@ describe("client.worker", () => {
228
228
 
229
229
  expect(mockCreateClientViteConfig).toHaveBeenCalledWith(
230
230
  expect.objectContaining({
231
- mode: "build",
231
+ mode: "dev",
232
232
  watch: true,
233
233
  pwa: false,
234
+ legacyModule: true,
234
235
  }),
235
236
  );
236
237
  });