@simplysm/sd-cli 13.0.96 → 13.0.98

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 (54) hide show
  1. package/README.md +132 -292
  2. package/dist/capacitor/capacitor.d.ts +2 -1
  3. package/dist/capacitor/capacitor.d.ts.map +1 -1
  4. package/dist/capacitor/capacitor.js +11 -3
  5. package/dist/capacitor/capacitor.js.map +1 -1
  6. package/dist/commands/device.js +2 -2
  7. package/dist/commands/device.js.map +1 -1
  8. package/dist/electron/electron.d.ts +2 -1
  9. package/dist/electron/electron.d.ts.map +1 -1
  10. package/dist/electron/electron.js +12 -3
  11. package/dist/electron/electron.js.map +1 -1
  12. package/dist/orchestrators/BuildOrchestrator.js +2 -2
  13. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  14. package/dist/orchestrators/DevOrchestrator.js +1 -1
  15. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  16. package/dist/orchestrators/WatchOrchestrator.d.ts +7 -0
  17. package/dist/orchestrators/WatchOrchestrator.d.ts.map +1 -1
  18. package/dist/orchestrators/WatchOrchestrator.js +93 -49
  19. package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
  20. package/dist/sd-config.types.d.ts +16 -1
  21. package/dist/sd-config.types.d.ts.map +1 -1
  22. package/dist/utils/package-utils.js +1 -1
  23. package/dist/utils/package-utils.js.map +1 -1
  24. package/dist/utils/vite-config.d.ts +6 -0
  25. package/dist/utils/vite-config.d.ts.map +1 -1
  26. package/dist/utils/vite-config.js +7 -1
  27. package/dist/utils/vite-config.js.map +1 -1
  28. package/dist/workers/client.worker.d.ts.map +1 -1
  29. package/dist/workers/client.worker.js +10 -3
  30. package/dist/workers/client.worker.js.map +1 -1
  31. package/package.json +6 -6
  32. package/src/capacitor/capacitor.ts +16 -2
  33. package/src/commands/device.ts +2 -2
  34. package/src/electron/electron.ts +17 -2
  35. package/src/orchestrators/BuildOrchestrator.ts +2 -2
  36. package/src/orchestrators/DevOrchestrator.ts +1 -1
  37. package/src/orchestrators/WatchOrchestrator.ts +124 -67
  38. package/src/sd-config.types.ts +17 -1
  39. package/src/utils/package-utils.ts +2 -2
  40. package/src/utils/vite-config.ts +15 -1
  41. package/src/workers/client.worker.ts +11 -1
  42. package/templates/init/.gitignore.hbs +1 -2
  43. package/templates/init/package.json.hbs +5 -6
  44. package/templates/init/packages/client-admin/package.json.hbs +7 -7
  45. package/templates/init/packages/db-main/package.json.hbs +2 -2
  46. package/templates/init/packages/server/package.json.hbs +5 -5
  47. package/templates/init/tests-e2e/package.json.hbs +1 -1
  48. package/tests/capacitor-exclude.spec.ts +78 -0
  49. package/tests/electron-exclude.spec.ts +61 -0
  50. package/tests/run-watch.spec.ts +35 -0
  51. package/tests/vite-config-exclude.spec.ts +35 -0
  52. package/tests/vite-config-outdir.spec.ts +38 -0
  53. package/docs/architecture.md +0 -311
  54. package/docs/config-types.md +0 -263
@@ -293,6 +293,12 @@ export interface ViteConfigOptions {
293
293
  replaceDeps?: string[];
294
294
  /** Callback when replaceDeps package dist changes */
295
295
  onScopeRebuild?: () => void;
296
+ /** Override build.outDir (e.g. ".capacitor/www" for Capacitor builds) */
297
+ outDir?: string;
298
+ /** Override base path (e.g. "./" for Capacitor builds) */
299
+ base?: string;
300
+ /** Packages to exclude from Vite optimizeDeps pre-bundling */
301
+ exclude?: string[];
296
302
  }
297
303
 
298
304
  /**
@@ -319,7 +325,7 @@ export function createViteConfig(options: ViteConfigOptions): ViteUserConfig {
319
325
 
320
326
  const config: ViteUserConfig = {
321
327
  root: pkgDir,
322
- base: `/${name}/`,
328
+ base: options.base ?? `/${name}/`,
323
329
  plugins: [
324
330
  tsconfigPaths({ projects: [tsconfigPath] }),
325
331
  solidPlugin(),
@@ -358,6 +364,14 @@ export function createViteConfig(options: ViteConfigOptions): ViteUserConfig {
358
364
  // Process.env substitution (applied to both build and dev modes)
359
365
  config.define = envDefine;
360
366
 
367
+ if (options.outDir != null) {
368
+ config.build = { outDir: options.outDir };
369
+ }
370
+
371
+ if (options.exclude != null && options.exclude.length > 0) {
372
+ config.optimizeDeps = { exclude: options.exclude };
373
+ }
374
+
361
375
  if (mode === "build") {
362
376
  config.logLevel = "silent";
363
377
  } else {
@@ -126,6 +126,11 @@ async function build(info: ClientBuildInfo): Promise<ClientBuildResult> {
126
126
  );
127
127
 
128
128
  // Create Vite configuration and build
129
+ const isCapacitor = info.config.capacitor != null;
130
+ const outDir = isCapacitor
131
+ ? path.join(info.pkgDir, ".capacitor", "www")
132
+ : undefined;
133
+
129
134
  const viteConfig = createViteConfig({
130
135
  pkgDir: info.pkgDir,
131
136
  name: info.name,
@@ -133,12 +138,16 @@ async function build(info: ClientBuildInfo): Promise<ClientBuildResult> {
133
138
  compilerOptions,
134
139
  env: info.config.env,
135
140
  mode: "build",
141
+ outDir,
142
+ base: isCapacitor ? "./" : undefined,
143
+ exclude: info.config.exclude,
136
144
  });
137
145
 
138
146
  await viteBuild(viteConfig);
139
147
 
140
148
  // Generate .config.json
141
- const confDistPath = path.join(info.pkgDir, "dist", ".config.json");
149
+ const confDistDir = outDir ?? path.join(info.pkgDir, "dist");
150
+ const confDistPath = path.join(confDistDir, ".config.json");
142
151
  fs.writeFileSync(confDistPath, JSON.stringify(info.config.configs ?? {}, undefined, 2));
143
152
 
144
153
  return { success: true };
@@ -190,6 +199,7 @@ async function startWatch(info: ClientWatchInfo): Promise<void> {
190
199
  serverPort,
191
200
  replaceDeps,
192
201
  onScopeRebuild: () => sender.send("scopeRebuild", {}),
202
+ exclude: info.config.exclude,
193
203
  });
194
204
 
195
205
  // Start Vite dev server
@@ -1,6 +1,5 @@
1
1
  .tmp
2
- .tasks
3
- .playwright
2
+ .playwright-cli
4
3
  .coverage
5
4
  __pycache__
6
5
  .claude/worktrees
@@ -16,19 +16,18 @@
16
16
  "check": "sd-cli check",
17
17
  "test": "vitest run",
18
18
  "test:e2e": "vitest run -c vitest-e2e.config.ts",
19
- "postinstall": "playwright install && playwright-cli install --skills"
19
+ "postinstall": "playwright-cli install --skills"
20
20
  },
21
21
  "devDependencies": {
22
- "@simplysm/lint": "~13.0.96",
23
- "@simplysm/sd-cli": "~13.0.96",
24
- "@simplysm/sd-claude": "~13.0.96",
22
+ "@simplysm/lint": "~13.0.98",
23
+ "@simplysm/sd-cli": "~13.0.98",
24
+ "@simplysm/sd-claude": "~13.0.98",
25
+ "@playwright/cli": "^0.1.1",
25
26
  "@types/node": "^20.19.37",
26
27
  "eslint": "^9.39.4",
27
28
  "prettier": "^3.8.1",
28
29
  "typescript": "^5.9.3",
29
30
  "vite-tsconfig-paths": "^6.1.1",
30
31
  "vitest": "^4.1.0",
31
- "@playwright/cli": "^0.1.1",
32
- "playwright": "^1.58.2"
33
32
  }
34
33
  }
@@ -6,13 +6,13 @@
6
6
  "private": true,
7
7
  "dependencies": {
8
8
  "@{{projectName}}/db-main": "workspace:*",
9
- "@simplysm/core-browser": "~13.0.96",
10
- "@simplysm/core-common": "~13.0.96",
11
- "@simplysm/excel": "~13.0.96",
12
- "@simplysm/orm-common": "~13.0.96",
13
- "@simplysm/service-client": "~13.0.96",
14
- "@simplysm/service-common": "~13.0.96",
15
- "@simplysm/solid": "~13.0.96",
9
+ "@simplysm/core-browser": "~13.0.98",
10
+ "@simplysm/core-common": "~13.0.98",
11
+ "@simplysm/excel": "~13.0.98",
12
+ "@simplysm/orm-common": "~13.0.98",
13
+ "@simplysm/service-client": "~13.0.98",
14
+ "@simplysm/service-common": "~13.0.98",
15
+ "@simplysm/solid": "~13.0.98",
16
16
  "@solid-primitives/event-listener": "^2.4.5",
17
17
  "@solidjs/router": "^0.15.4",
18
18
  "@tabler/icons-solidjs": "^3.40.0",
@@ -7,7 +7,7 @@
7
7
  ".": "./src/index.ts"
8
8
  },
9
9
  "dependencies": {
10
- "@simplysm/core-common": "~13.0.96",
11
- "@simplysm/orm-common": "~13.0.96"
10
+ "@simplysm/core-common": "~13.0.98",
11
+ "@simplysm/orm-common": "~13.0.98"
12
12
  }
13
13
  }
@@ -5,11 +5,11 @@
5
5
  "private": true,
6
6
  "dependencies": {
7
7
  "@{{projectName}}/db-main": "workspace:*",
8
- "@simplysm/core-common": "~13.0.96",
9
- "@simplysm/excel": "~13.0.96",
10
- "@simplysm/orm-common": "~13.0.96",
11
- "@simplysm/orm-node": "~13.0.96",
12
- "@simplysm/service-server": "~13.0.96",
8
+ "@simplysm/core-common": "~13.0.98",
9
+ "@simplysm/excel": "~13.0.98",
10
+ "@simplysm/orm-common": "~13.0.98",
11
+ "@simplysm/orm-node": "~13.0.98",
12
+ "@simplysm/service-server": "~13.0.98",
13
13
  "bcrypt": "^6.0.0",
14
14
  "pg": "^8.20.0",
15
15
  "pg-copy-streams": "^7.0.0"
@@ -6,7 +6,7 @@
6
6
  "private": true,
7
7
  "dependencies": {
8
8
  "@{{projectName}}/db-main": "workspace:*",
9
- "@simplysm/orm-node": "~13.0.96",
9
+ "@simplysm/orm-node": "~13.0.98",
10
10
  "bcrypt": "^6.0.0",
11
11
  "playwright": "^1.58.2"
12
12
  },
@@ -0,0 +1,78 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const { mockWriteJson, mockReadJson, mockExists } = vi.hoisted(() => ({
4
+ mockWriteJson: vi.fn().mockResolvedValue(undefined),
5
+ mockReadJson: vi.fn(),
6
+ mockExists: vi.fn(),
7
+ }));
8
+
9
+ vi.mock("@simplysm/core-node", () => ({
10
+ fsx: {
11
+ readJson: mockReadJson,
12
+ exists: mockExists,
13
+ mkdir: vi.fn().mockResolvedValue(undefined),
14
+ writeJson: mockWriteJson,
15
+ write: vi.fn().mockResolvedValue(undefined),
16
+ },
17
+ }));
18
+
19
+ vi.mock("consola", () => {
20
+ const withTag = () => ({ debug: vi.fn(), warn: vi.fn() });
21
+ const consola = { withTag };
22
+ return { default: consola, consola };
23
+ });
24
+
25
+ vi.mock("sharp", () => ({ default: vi.fn() }));
26
+ vi.mock("execa", () => ({ execa: vi.fn() }));
27
+
28
+ import { Capacitor } from "../src/capacitor/capacitor";
29
+
30
+ describe("Capacitor exclude packages", () => {
31
+ beforeEach(() => {
32
+ vi.clearAllMocks();
33
+ mockReadJson.mockImplementation((filePath: string) => {
34
+ if (filePath.includes(".capacitor")) {
35
+ return Promise.resolve({ name: "", version: "", dependencies: {} });
36
+ }
37
+ return Promise.resolve({
38
+ name: "@test/app",
39
+ version: "1.0.0",
40
+ dependencies: { "jeep-sqlite": "^0.0.1" },
41
+ });
42
+ });
43
+ mockExists.mockImplementation((filePath: string) => {
44
+ if (filePath.endsWith("package.json")) return Promise.resolve(true);
45
+ return Promise.resolve(false);
46
+ });
47
+ });
48
+
49
+ it("exclude 패키지가 .capacitor/package.json dependencies에 추가된다", async () => {
50
+ const cap = await Capacitor.create("/test/packages/app", {
51
+ appId: "com.test.app",
52
+ appName: "Test App",
53
+ }, ["jeep-sqlite"]);
54
+
55
+ await (cap as unknown as { _setupNpmConf(): Promise<boolean> })._setupNpmConf();
56
+
57
+ expect(mockWriteJson).toHaveBeenCalled();
58
+ const writtenConfig = mockWriteJson.mock.calls[0][1] as {
59
+ dependencies: Record<string, string>;
60
+ };
61
+ expect(writtenConfig.dependencies["jeep-sqlite"]).toBe("^0.0.1");
62
+ });
63
+
64
+ it("exclude 없이 생성해도 정상 동작한다", async () => {
65
+ const cap = await Capacitor.create("/test/packages/app", {
66
+ appId: "com.test.app",
67
+ appName: "Test App",
68
+ });
69
+
70
+ await (cap as unknown as { _setupNpmConf(): Promise<boolean> })._setupNpmConf();
71
+
72
+ expect(mockWriteJson).toHaveBeenCalled();
73
+ const writtenConfig = mockWriteJson.mock.calls[0][1] as {
74
+ dependencies: Record<string, string>;
75
+ };
76
+ expect(writtenConfig.dependencies["jeep-sqlite"]).toBeUndefined();
77
+ });
78
+ });
@@ -0,0 +1,61 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const { mockWriteJson, mockReadJson } = vi.hoisted(() => ({
4
+ mockWriteJson: vi.fn().mockResolvedValue(undefined),
5
+ mockReadJson: vi.fn(),
6
+ }));
7
+
8
+ vi.mock("@simplysm/core-node", () => ({
9
+ fsx: {
10
+ readJson: mockReadJson,
11
+ mkdir: vi.fn().mockResolvedValue(undefined),
12
+ writeJson: mockWriteJson,
13
+ },
14
+ }));
15
+
16
+ vi.mock("consola", () => {
17
+ const withTag = () => ({ debug: vi.fn(), warn: vi.fn() });
18
+ const consola = { withTag };
19
+ return { default: consola, consola };
20
+ });
21
+
22
+ import { Electron } from "../src/electron/electron";
23
+
24
+ describe("Electron exclude packages", () => {
25
+ beforeEach(() => {
26
+ vi.clearAllMocks();
27
+ mockReadJson.mockResolvedValue({
28
+ name: "@test/app",
29
+ version: "1.0.0",
30
+ dependencies: { "jeep-sqlite": "^0.0.1" },
31
+ });
32
+ });
33
+
34
+ it("exclude 패키지가 .electron/src/package.json dependencies에 추가된다", async () => {
35
+ const electron = await Electron.create("/test/packages/app", {
36
+ appId: "com.test.app",
37
+ }, ["jeep-sqlite"]);
38
+
39
+ await (electron as unknown as { _setupPackageJson(p: string): Promise<void> })._setupPackageJson("/test/packages/app/.electron/src");
40
+
41
+ expect(mockWriteJson).toHaveBeenCalled();
42
+ const writtenConfig = mockWriteJson.mock.calls[0][1] as {
43
+ dependencies: Record<string, string>;
44
+ };
45
+ expect(writtenConfig.dependencies["jeep-sqlite"]).toBe("^0.0.1");
46
+ });
47
+
48
+ it("exclude 없이 생성해도 정상 동작한다", async () => {
49
+ const electron = await Electron.create("/test/packages/app", {
50
+ appId: "com.test.app",
51
+ });
52
+
53
+ await (electron as unknown as { _setupPackageJson(p: string): Promise<void> })._setupPackageJson("/test/packages/app/.electron/src");
54
+
55
+ expect(mockWriteJson).toHaveBeenCalled();
56
+ const writtenConfig = mockWriteJson.mock.calls[0][1] as {
57
+ dependencies: Record<string, string>;
58
+ };
59
+ expect(writtenConfig.dependencies["jeep-sqlite"]).toBeUndefined();
60
+ });
61
+ });
@@ -38,4 +38,39 @@ describe("filterPackagesByTargets", () => {
38
38
  expect(result["core-node"]).toEqual({ target: "node" });
39
39
  });
40
40
 
41
+ it("includes scripts target when watch option is present", () => {
42
+ const packagesWithWatch: Record<string, SdPackageConfig | undefined> = {
43
+ ...mockPackages,
44
+ "claude-with-watch": {
45
+ target: "scripts",
46
+ watch: {
47
+ target: ["../../.claude/rules/sd-*"],
48
+ cmd: "node",
49
+ args: ["scripts/sync.mjs"],
50
+ },
51
+ },
52
+ };
53
+ const result = filterPackagesByTargets(packagesWithWatch, []);
54
+
55
+ expect(result["claude-with-watch"]).toBeDefined();
56
+ expect(result["claude"]).toBeUndefined(); // scripts without watch still excluded
57
+ });
58
+
59
+ it("includes scripts+watch target when specified in targets", () => {
60
+ const packagesWithWatch: Record<string, SdPackageConfig | undefined> = {
61
+ ...mockPackages,
62
+ "claude-with-watch": {
63
+ target: "scripts",
64
+ watch: {
65
+ target: ["../../.claude/rules/sd-*"],
66
+ cmd: "node",
67
+ },
68
+ },
69
+ };
70
+ const result = filterPackagesByTargets(packagesWithWatch, ["claude-with-watch"]);
71
+
72
+ expect(Object.keys(result)).toHaveLength(1);
73
+ expect(result["claude-with-watch"]).toBeDefined();
74
+ });
75
+
41
76
  });
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createViteConfig } from "../src/utils/vite-config";
3
+ import path from "path";
4
+
5
+ describe("createViteConfig exclude option", () => {
6
+ const baseOptions = {
7
+ pkgDir: path.resolve(__dirname, ".."),
8
+ name: "test-app",
9
+ tsconfigPath: path.resolve(__dirname, "../../..", "tsconfig.json"),
10
+ compilerOptions: {},
11
+ mode: "build" as const,
12
+ };
13
+
14
+ it("should set optimizeDeps.exclude when exclude option is provided", () => {
15
+ const config = createViteConfig({ ...baseOptions, exclude: ["jeep-sqlite"] });
16
+
17
+ expect(config.optimizeDeps?.exclude).toContain("jeep-sqlite");
18
+ });
19
+
20
+ it("should not set optimizeDeps.exclude when exclude option is not provided", () => {
21
+ const config = createViteConfig(baseOptions);
22
+
23
+ expect(config.optimizeDeps?.exclude).toBeUndefined();
24
+ });
25
+
26
+ it("should handle multiple exclude packages", () => {
27
+ const config = createViteConfig({
28
+ ...baseOptions,
29
+ exclude: ["jeep-sqlite", "electron"],
30
+ });
31
+
32
+ expect(config.optimizeDeps?.exclude).toContain("jeep-sqlite");
33
+ expect(config.optimizeDeps?.exclude).toContain("electron");
34
+ });
35
+ });
@@ -0,0 +1,38 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createViteConfig } from "../src/utils/vite-config";
3
+ import path from "path";
4
+
5
+ describe("createViteConfig capacitor overrides", () => {
6
+ const baseOptions = {
7
+ pkgDir: path.resolve(__dirname, ".."),
8
+ name: "test-app",
9
+ tsconfigPath: path.resolve(__dirname, "../../..", "tsconfig.json"),
10
+ compilerOptions: {},
11
+ mode: "build" as const,
12
+ };
13
+
14
+ it("should set build.outDir when outDir option is provided", () => {
15
+ const outDir = path.join(baseOptions.pkgDir, ".capacitor", "www");
16
+ const config = createViteConfig({ ...baseOptions, outDir });
17
+
18
+ expect(config.build?.outDir).toBe(outDir);
19
+ });
20
+
21
+ it("should not set build.outDir when outDir option is not provided", () => {
22
+ const config = createViteConfig(baseOptions);
23
+
24
+ expect(config.build?.outDir).toBeUndefined();
25
+ });
26
+
27
+ it("should override base when base option is provided", () => {
28
+ const config = createViteConfig({ ...baseOptions, base: "./" });
29
+
30
+ expect(config.base).toBe("./");
31
+ });
32
+
33
+ it("should use /{name}/ as base when base option is not provided", () => {
34
+ const config = createViteConfig(baseOptions);
35
+
36
+ expect(config.base).toBe("/test-app/");
37
+ });
38
+ });