@simplysm/sd-cli 13.0.68 → 13.0.70

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 (201) hide show
  1. package/README.md +10 -957
  2. package/dist/builders/BaseBuilder.d.ts +23 -23
  3. package/dist/builders/BaseBuilder.d.ts.map +1 -1
  4. package/dist/builders/BaseBuilder.js +15 -15
  5. package/dist/builders/DtsBuilder.d.ts +4 -4
  6. package/dist/builders/DtsBuilder.js +1 -1
  7. package/dist/builders/LibraryBuilder.d.ts +3 -3
  8. package/dist/builders/types.d.ts +10 -10
  9. package/dist/capacitor/capacitor.d.ts +36 -36
  10. package/dist/capacitor/capacitor.js +63 -63
  11. package/dist/capacitor/capacitor.js.map +1 -1
  12. package/dist/commands/add-client.d.ts +8 -8
  13. package/dist/commands/add-client.js +15 -15
  14. package/dist/commands/add-client.js.map +1 -1
  15. package/dist/commands/add-server.d.ts +9 -9
  16. package/dist/commands/add-server.js +13 -13
  17. package/dist/commands/add-server.js.map +1 -1
  18. package/dist/commands/build.d.ts +9 -9
  19. package/dist/commands/check.js +3 -3
  20. package/dist/commands/check.js.map +1 -1
  21. package/dist/commands/dev.d.ts +9 -9
  22. package/dist/commands/device.d.ts +9 -9
  23. package/dist/commands/device.d.ts.map +1 -1
  24. package/dist/commands/device.js +17 -17
  25. package/dist/commands/device.js.map +1 -1
  26. package/dist/commands/init.d.ts +6 -6
  27. package/dist/commands/init.js +12 -12
  28. package/dist/commands/init.js.map +1 -1
  29. package/dist/commands/lint.d.ts +23 -23
  30. package/dist/commands/lint.d.ts.map +1 -1
  31. package/dist/commands/lint.js +25 -25
  32. package/dist/commands/lint.js.map +1 -1
  33. package/dist/commands/publish.d.ts +13 -13
  34. package/dist/commands/publish.d.ts.map +1 -1
  35. package/dist/commands/publish.js +61 -61
  36. package/dist/commands/publish.js.map +1 -1
  37. package/dist/commands/replace-deps.d.ts +3 -3
  38. package/dist/commands/replace-deps.d.ts.map +1 -1
  39. package/dist/commands/replace-deps.js +1 -1
  40. package/dist/commands/replace-deps.js.map +1 -1
  41. package/dist/commands/typecheck.d.ts +20 -20
  42. package/dist/commands/typecheck.d.ts.map +1 -1
  43. package/dist/commands/typecheck.js +20 -20
  44. package/dist/commands/typecheck.js.map +1 -1
  45. package/dist/commands/watch.d.ts +7 -7
  46. package/dist/electron/electron.d.ts +27 -27
  47. package/dist/electron/electron.js +32 -32
  48. package/dist/electron/electron.js.map +1 -1
  49. package/dist/infra/ResultCollector.d.ts +9 -9
  50. package/dist/infra/ResultCollector.js +5 -5
  51. package/dist/infra/SignalHandler.d.ts +7 -7
  52. package/dist/infra/SignalHandler.js +4 -4
  53. package/dist/infra/WorkerManager.d.ts +14 -14
  54. package/dist/infra/WorkerManager.js +11 -11
  55. package/dist/orchestrators/BuildOrchestrator.d.ts +19 -19
  56. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  57. package/dist/orchestrators/BuildOrchestrator.js +26 -26
  58. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  59. package/dist/orchestrators/DevOrchestrator.d.ts +25 -25
  60. package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
  61. package/dist/orchestrators/DevOrchestrator.js +30 -30
  62. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  63. package/dist/orchestrators/WatchOrchestrator.d.ts +13 -13
  64. package/dist/orchestrators/WatchOrchestrator.js +17 -17
  65. package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
  66. package/dist/sd-cli-entry.d.ts +2 -2
  67. package/dist/sd-cli-entry.js +38 -38
  68. package/dist/sd-cli-entry.js.map +1 -1
  69. package/dist/sd-cli.d.ts +2 -2
  70. package/dist/sd-cli.js +1 -1
  71. package/dist/sd-cli.js.map +1 -1
  72. package/dist/sd-config.types.d.ts +84 -84
  73. package/dist/sd-config.types.d.ts.map +1 -1
  74. package/dist/utils/build-env.d.ts +1 -1
  75. package/dist/utils/config-editor.d.ts +5 -5
  76. package/dist/utils/config-editor.js +2 -2
  77. package/dist/utils/config-editor.js.map +1 -1
  78. package/dist/utils/copy-public.d.ts +9 -9
  79. package/dist/utils/copy-src.d.ts +9 -9
  80. package/dist/utils/esbuild-config.d.ts +30 -30
  81. package/dist/utils/esbuild-config.d.ts.map +1 -1
  82. package/dist/utils/output-utils.d.ts +6 -6
  83. package/dist/utils/package-utils.d.ts +6 -6
  84. package/dist/utils/package-utils.js +1 -1
  85. package/dist/utils/package-utils.js.map +1 -1
  86. package/dist/utils/rebuild-manager.js +3 -3
  87. package/dist/utils/rebuild-manager.js.map +1 -1
  88. package/dist/utils/replace-deps.d.ts +25 -25
  89. package/dist/utils/replace-deps.js +3 -3
  90. package/dist/utils/replace-deps.js.map +1 -1
  91. package/dist/utils/sd-config.d.ts +3 -3
  92. package/dist/utils/sd-config.js +3 -3
  93. package/dist/utils/sd-config.js.map +1 -1
  94. package/dist/utils/tailwind-config-deps.d.ts +3 -3
  95. package/dist/utils/template.d.ts +8 -8
  96. package/dist/utils/tsconfig.d.ts +16 -16
  97. package/dist/utils/tsconfig.js +2 -2
  98. package/dist/utils/tsconfig.js.map +1 -1
  99. package/dist/utils/typecheck-serialization.d.ts +8 -8
  100. package/dist/utils/vite-config.d.ts +8 -8
  101. package/dist/utils/vite-config.d.ts.map +1 -1
  102. package/dist/utils/vite-config.js +3 -3
  103. package/dist/utils/worker-events.d.ts +12 -12
  104. package/dist/utils/worker-events.d.ts.map +1 -1
  105. package/dist/utils/worker-utils.d.ts +3 -3
  106. package/dist/utils/worker-utils.js +2 -2
  107. package/dist/utils/worker-utils.js.map +1 -1
  108. package/dist/workers/client.worker.d.ts +14 -14
  109. package/dist/workers/client.worker.d.ts.map +1 -1
  110. package/dist/workers/client.worker.js +1 -1
  111. package/dist/workers/client.worker.js.map +1 -1
  112. package/dist/workers/dts.worker.d.ts +13 -13
  113. package/dist/workers/dts.worker.d.ts.map +1 -1
  114. package/dist/workers/dts.worker.js +3 -3
  115. package/dist/workers/dts.worker.js.map +1 -1
  116. package/dist/workers/library.worker.d.ts +12 -12
  117. package/dist/workers/library.worker.js +1 -1
  118. package/dist/workers/library.worker.js.map +1 -1
  119. package/dist/workers/lint.worker.d.ts +1 -1
  120. package/dist/workers/server-runtime.worker.d.ts +6 -6
  121. package/dist/workers/server-runtime.worker.js +6 -6
  122. package/dist/workers/server-runtime.worker.js.map +1 -1
  123. package/dist/workers/server.worker.d.ts +20 -20
  124. package/dist/workers/server.worker.d.ts.map +1 -1
  125. package/dist/workers/server.worker.js +6 -6
  126. package/dist/workers/server.worker.js.map +1 -1
  127. package/package.json +8 -7
  128. package/src/builders/BaseBuilder.ts +33 -33
  129. package/src/builders/DtsBuilder.ts +5 -5
  130. package/src/builders/LibraryBuilder.ts +9 -9
  131. package/src/builders/types.ts +10 -10
  132. package/src/capacitor/capacitor.ts +119 -119
  133. package/src/commands/add-client.ts +31 -31
  134. package/src/commands/add-server.ts +34 -34
  135. package/src/commands/build.ts +9 -9
  136. package/src/commands/check.ts +5 -5
  137. package/src/commands/dev.ts +9 -9
  138. package/src/commands/device.ts +30 -30
  139. package/src/commands/init.ts +25 -25
  140. package/src/commands/lint.ts +64 -64
  141. package/src/commands/publish.ts +139 -139
  142. package/src/commands/replace-deps.ts +4 -4
  143. package/src/commands/typecheck.ts +74 -74
  144. package/src/commands/watch.ts +7 -7
  145. package/src/electron/electron.ts +51 -51
  146. package/src/infra/ResultCollector.ts +9 -9
  147. package/src/infra/SignalHandler.ts +7 -7
  148. package/src/infra/WorkerManager.ts +14 -14
  149. package/src/orchestrators/BuildOrchestrator.ts +76 -76
  150. package/src/orchestrators/DevOrchestrator.ts +88 -88
  151. package/src/orchestrators/WatchOrchestrator.ts +39 -39
  152. package/src/sd-cli-entry.ts +43 -43
  153. package/src/sd-cli.ts +15 -15
  154. package/src/sd-config.types.ts +85 -85
  155. package/src/utils/build-env.ts +1 -1
  156. package/src/utils/config-editor.ts +19 -19
  157. package/src/utils/copy-public.ts +17 -17
  158. package/src/utils/copy-src.ts +11 -11
  159. package/src/utils/esbuild-config.ts +33 -33
  160. package/src/utils/output-utils.ts +11 -11
  161. package/src/utils/package-utils.ts +12 -12
  162. package/src/utils/rebuild-manager.ts +3 -3
  163. package/src/utils/replace-deps.ts +361 -361
  164. package/src/utils/sd-config.ts +44 -44
  165. package/src/utils/tailwind-config-deps.ts +98 -98
  166. package/src/utils/template.ts +56 -56
  167. package/src/utils/tsconfig.ts +127 -127
  168. package/src/utils/typecheck-serialization.ts +86 -86
  169. package/src/utils/vite-config.ts +341 -341
  170. package/src/utils/worker-events.ts +16 -16
  171. package/src/utils/worker-utils.ts +45 -45
  172. package/src/workers/client.worker.ts +34 -34
  173. package/src/workers/dts.worker.ts +467 -467
  174. package/src/workers/library.worker.ts +314 -314
  175. package/src/workers/lint.worker.ts +16 -16
  176. package/src/workers/server-runtime.worker.ts +157 -157
  177. package/src/workers/server.worker.ts +572 -572
  178. package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
  179. package/templates/add-server/__SERVER__/package.json.hbs +2 -2
  180. package/templates/init/package.json.hbs +3 -3
  181. package/tests/config-editor.spec.ts +160 -0
  182. package/tests/copy-src.spec.ts +50 -0
  183. package/tests/get-compiler-options-for-package.spec.ts +139 -0
  184. package/tests/get-package-source-files.spec.ts +181 -0
  185. package/tests/get-types-from-package-json.spec.ts +107 -0
  186. package/tests/infra/ResultCollector.spec.ts +39 -0
  187. package/tests/infra/SignalHandler.spec.ts +38 -0
  188. package/tests/infra/WorkerManager.spec.ts +97 -0
  189. package/tests/load-ignore-patterns.spec.ts +188 -0
  190. package/tests/load-sd-config.spec.ts +137 -0
  191. package/tests/package-utils.spec.ts +188 -0
  192. package/tests/parse-root-tsconfig.spec.ts +89 -0
  193. package/tests/replace-deps.spec.ts +308 -0
  194. package/tests/run-lint.spec.ts +415 -0
  195. package/tests/run-typecheck.spec.ts +653 -0
  196. package/tests/run-watch.spec.ts +75 -0
  197. package/tests/sd-cli.spec.ts +330 -0
  198. package/tests/tailwind-config-deps.spec.ts +30 -0
  199. package/tests/template.spec.ts +70 -0
  200. package/tests/utils/rebuild-manager.spec.ts +43 -0
  201. package/tests/write-changed-output-files.spec.ts +97 -0
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
7
- "@simplysm/solid": "~13.0.68",
7
+ "@simplysm/solid": "~13.0.70",
8
8
  {{#if router}}
9
9
  "@solidjs/router": "^0.15.4",
10
10
  {{/if}}
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
7
- "@simplysm/core-common": "~13.0.68",
8
- "@simplysm/service-server": "~13.0.68"
7
+ "@simplysm/core-common": "~13.0.70",
8
+ "@simplysm/service-server": "~13.0.70"
9
9
  }
10
10
  }
@@ -15,9 +15,9 @@
15
15
  "vitest": "vitest"
16
16
  },
17
17
  "devDependencies": {
18
- "@simplysm/sd-cli": "~13.0.68",
19
- "@simplysm/sd-claude": "~13.0.68",
20
- "@simplysm/lint": "~13.0.68",
18
+ "@simplysm/sd-cli": "~13.0.70",
19
+ "@simplysm/sd-claude": "~13.0.70",
20
+ "@simplysm/lint": "~13.0.70",
21
21
  "@types/node": "^20.19.33",
22
22
  "eslint": "^9.39.2",
23
23
  "prettier": "^3.8.1",
@@ -0,0 +1,160 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import os from "os";
5
+ import {
6
+ addPackageToSdConfig,
7
+ setClientServerInSdConfig,
8
+ addTailwindToEslintConfig,
9
+ } from "../src/utils/config-editor";
10
+
11
+ describe("config-editor", () => {
12
+ let tmpDir: string;
13
+
14
+ beforeEach(() => {
15
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sd-cli-test-"));
16
+ });
17
+
18
+ afterEach(() => {
19
+ fs.rmSync(tmpDir, { recursive: true });
20
+ });
21
+
22
+ describe("addPackageToSdConfig", () => {
23
+ test("adds client package to empty packages object", () => {
24
+ const configPath = path.join(tmpDir, "sd.config.ts");
25
+ fs.writeFileSync(
26
+ configPath,
27
+ [
28
+ 'import type { SdConfigFn } from "@simplysm/sd-cli";',
29
+ "",
30
+ "const config: SdConfigFn = () => ({",
31
+ " packages: {},",
32
+ "});",
33
+ "",
34
+ "export default config;",
35
+ ].join("\n"),
36
+ );
37
+
38
+ addPackageToSdConfig(configPath, "client-web", { target: "client" });
39
+
40
+ const result = fs.readFileSync(configPath, "utf-8");
41
+ expect(result).toContain('"client-web"');
42
+ expect(result).toContain('"client"');
43
+ });
44
+
45
+ test("adds server package to existing packages", () => {
46
+ const configPath = path.join(tmpDir, "sd.config.ts");
47
+ fs.writeFileSync(
48
+ configPath,
49
+ [
50
+ 'import type { SdConfigFn } from "@simplysm/sd-cli";',
51
+ "",
52
+ "const config: SdConfigFn = () => ({",
53
+ " packages: {",
54
+ ' "client-web": { target: "client" },',
55
+ " },",
56
+ "});",
57
+ "",
58
+ "export default config;",
59
+ ].join("\n"),
60
+ );
61
+
62
+ addPackageToSdConfig(configPath, "server", { target: "server" });
63
+
64
+ const result = fs.readFileSync(configPath, "utf-8");
65
+ expect(result).toContain('"client-web"');
66
+ expect(result).toContain('"server"');
67
+ });
68
+
69
+ test("returns false if package already exists", () => {
70
+ const configPath = path.join(tmpDir, "sd.config.ts");
71
+ fs.writeFileSync(
72
+ configPath,
73
+ [
74
+ 'import type { SdConfigFn } from "@simplysm/sd-cli";',
75
+ "",
76
+ "const config: SdConfigFn = () => ({",
77
+ " packages: {",
78
+ ' "client-web": { target: "client" },',
79
+ " },",
80
+ "});",
81
+ "",
82
+ "export default config;",
83
+ ].join("\n"),
84
+ );
85
+
86
+ const result = addPackageToSdConfig(configPath, "client-web", { target: "client" });
87
+ expect(result).toBe(false);
88
+ });
89
+ });
90
+
91
+ describe("setClientServerInSdConfig", () => {
92
+ test("adds server field to client config", () => {
93
+ const configPath = path.join(tmpDir, "sd.config.ts");
94
+ fs.writeFileSync(
95
+ configPath,
96
+ [
97
+ 'import type { SdConfigFn } from "@simplysm/sd-cli";',
98
+ "",
99
+ "const config: SdConfigFn = () => ({",
100
+ " packages: {",
101
+ ' "client-web": { target: "client" },',
102
+ " },",
103
+ "});",
104
+ "",
105
+ "export default config;",
106
+ ].join("\n"),
107
+ );
108
+
109
+ setClientServerInSdConfig(configPath, "client-web", "server");
110
+
111
+ const result = fs.readFileSync(configPath, "utf-8");
112
+ expect(result).toContain('server: "server"');
113
+ });
114
+ });
115
+
116
+ describe("addTailwindToEslintConfig", () => {
117
+ test("adds tailwind settings to eslint config without tailwind", () => {
118
+ const configPath = path.join(tmpDir, "eslint.config.ts");
119
+ fs.writeFileSync(
120
+ configPath,
121
+ [
122
+ 'import simplysmEslintRecommended from "@simplysm/lint/eslint-recommended";',
123
+ "",
124
+ "export default [",
125
+ " ...simplysmEslintRecommended,",
126
+ "];",
127
+ ].join("\n"),
128
+ );
129
+
130
+ addTailwindToEslintConfig(configPath, "client-web");
131
+
132
+ const result = fs.readFileSync(configPath, "utf-8");
133
+ expect(result).toContain("tailwindcss");
134
+ expect(result).toContain("packages/client-web/tailwind.config.ts");
135
+ });
136
+
137
+ test("does nothing if tailwind settings already exist", () => {
138
+ const configPath = path.join(tmpDir, "eslint.config.ts");
139
+ const original = [
140
+ 'import simplysmEslintRecommended from "@simplysm/lint/eslint-recommended";',
141
+ "",
142
+ "export default [",
143
+ " ...simplysmEslintRecommended,",
144
+ " {",
145
+ ' files: ["**/*.{ts,tsx}"],',
146
+ " settings: {",
147
+ " tailwindcss: {",
148
+ ' config: "packages/client-old/tailwind.config.ts",',
149
+ " },",
150
+ " },",
151
+ " },",
152
+ "];",
153
+ ].join("\n");
154
+ fs.writeFileSync(configPath, original);
155
+
156
+ const result = addTailwindToEslintConfig(configPath, "client-web");
157
+ expect(result).toBe(false);
158
+ });
159
+ });
160
+ });
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import path from "path";
3
+ import os from "os";
4
+ import fs from "fs/promises";
5
+ import { copySrcFiles } from "../src/utils/copy-src";
6
+
7
+ describe("copySrcFiles", () => {
8
+ let tmpDir: string;
9
+
10
+ beforeEach(async () => {
11
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "copysrc-"));
12
+ // Create src/ structure
13
+ await fs.mkdir(path.join(tmpDir, "src", "components"), { recursive: true });
14
+ await fs.mkdir(path.join(tmpDir, "dist"), { recursive: true });
15
+ await fs.writeFile(path.join(tmpDir, "src", "base.css"), "body {}");
16
+ await fs.writeFile(path.join(tmpDir, "src", "components", "Card.css"), ".card {}");
17
+ await fs.writeFile(path.join(tmpDir, "src", "index.ts"), "export {}");
18
+ });
19
+
20
+ afterEach(async () => {
21
+ await fs.rm(tmpDir, { recursive: true, force: true });
22
+ });
23
+
24
+ it("copies files matching glob pattern from src/ to dist/", async () => {
25
+ await copySrcFiles(tmpDir, ["**/*.css"]);
26
+
27
+ const base = await fs.readFile(path.join(tmpDir, "dist", "base.css"), "utf-8");
28
+ expect(base).toBe("body {}");
29
+
30
+ const card = await fs.readFile(path.join(tmpDir, "dist", "components", "Card.css"), "utf-8");
31
+ expect(card).toBe(".card {}");
32
+ });
33
+
34
+ it("does not copy files that don't match pattern", async () => {
35
+ await copySrcFiles(tmpDir, ["**/*.css"]);
36
+
37
+ const exists = await fs.access(path.join(tmpDir, "dist", "index.ts")).then(
38
+ () => true,
39
+ () => false,
40
+ );
41
+ expect(exists).toBe(false);
42
+ });
43
+
44
+ it("copies nothing if pattern is empty array", async () => {
45
+ await copySrcFiles(tmpDir, []);
46
+
47
+ const files = await fs.readdir(path.join(tmpDir, "dist"));
48
+ expect(files).toHaveLength(0);
49
+ });
50
+ });
@@ -0,0 +1,139 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
+ import type ts from "typescript";
3
+
4
+ // Mock core-node functions
5
+ vi.mock("@simplysm/core-node", () => ({
6
+ fsExists: vi.fn(),
7
+ fsReadJson: vi.fn(),
8
+ pathPosix: vi.fn((p: string) => p.replace(/\\/g, "/")),
9
+ }));
10
+
11
+ import { fsExists, fsReadJson } from "@simplysm/core-node";
12
+ import { getCompilerOptionsForPackage } from "../src/utils/tsconfig";
13
+
14
+ describe("getCompilerOptionsForPackage", () => {
15
+ beforeEach(() => {
16
+ vi.clearAllMocks();
17
+ });
18
+
19
+ afterEach(() => {
20
+ vi.restoreAllMocks();
21
+ });
22
+
23
+ const baseOptions: ts.CompilerOptions = {
24
+ lib: ["ES2024", "DOM", "DOM.Iterable", "WebWorker"],
25
+ types: [],
26
+ strict: true,
27
+ };
28
+
29
+ it("node target: removes DOM lib, includes node in types", async () => {
30
+ const packageDir = "/project/packages/core-node";
31
+ vi.mocked(fsExists).mockResolvedValue(true);
32
+ vi.mocked(fsReadJson).mockResolvedValue({
33
+ devDependencies: {
34
+ "@types/express": "^4.17.0",
35
+ },
36
+ });
37
+
38
+ const result = await getCompilerOptionsForPackage(baseOptions, "node", packageDir);
39
+
40
+ // DOM, WebWorker lib removed
41
+ expect(result.lib).toEqual(["ES2024"]);
42
+ // types includes node and express
43
+ expect(result.types).toContain("node");
44
+ expect(result.types).toContain("express");
45
+ });
46
+
47
+ it("browser target: keeps lib, removes node from types", async () => {
48
+ const packageDir = "/project/packages/core-browser";
49
+ vi.mocked(fsExists).mockResolvedValue(true);
50
+ vi.mocked(fsReadJson).mockResolvedValue({
51
+ devDependencies: {
52
+ "@types/node": "^20.0.0",
53
+ "@types/react": "^18.0.0",
54
+ },
55
+ });
56
+
57
+ const result = await getCompilerOptionsForPackage(baseOptions, "browser", packageDir);
58
+
59
+ // lib is preserved
60
+ expect(result.lib).toEqual(["ES2024", "DOM", "DOM.Iterable", "WebWorker"]);
61
+ // types removes node, includes react only
62
+ expect(result.types).not.toContain("node");
63
+ expect(result.types).toContain("react");
64
+ });
65
+
66
+ it("neutral target: keeps lib, includes node in types", async () => {
67
+ const packageDir = "/project/packages/core-common";
68
+ vi.mocked(fsExists).mockResolvedValue(true);
69
+ vi.mocked(fsReadJson).mockResolvedValue({
70
+ devDependencies: {
71
+ "@types/lodash": "^4.0.0",
72
+ },
73
+ });
74
+
75
+ const result = await getCompilerOptionsForPackage(baseOptions, "neutral", packageDir);
76
+
77
+ // lib is preserved (includes DOM)
78
+ expect(result.lib).toEqual(["ES2024", "DOM", "DOM.Iterable", "WebWorker"]);
79
+ // types includes node and lodash
80
+ expect(result.types).toContain("node");
81
+ expect(result.types).toContain("lodash");
82
+ });
83
+
84
+ it("node target: removes duplicate node types", async () => {
85
+ const packageDir = "/project/packages/core-node";
86
+ vi.mocked(fsExists).mockResolvedValue(true);
87
+ vi.mocked(fsReadJson).mockResolvedValue({
88
+ devDependencies: {
89
+ "@types/node": "^20.0.0",
90
+ },
91
+ });
92
+
93
+ const result = await getCompilerOptionsForPackage(baseOptions, "node", packageDir);
94
+
95
+ // node type is included only once without duplicates
96
+ expect(result.types?.filter((t) => t === "node")).toHaveLength(1);
97
+ });
98
+
99
+ it("handles missing package.json with empty types", async () => {
100
+ const packageDir = "/project/packages/unknown";
101
+ vi.mocked(fsExists).mockResolvedValue(false);
102
+
103
+ const result = await getCompilerOptionsForPackage(baseOptions, "node", packageDir);
104
+
105
+ // includes only node (types from package are empty array)
106
+ expect(result.types).toEqual(["node"]);
107
+ });
108
+
109
+ it("handles undefined lib correctly", async () => {
110
+ const optionsWithoutLib: ts.CompilerOptions = {
111
+ strict: true,
112
+ };
113
+ const packageDir = "/project/packages/core-node";
114
+ vi.mocked(fsExists).mockResolvedValue(false);
115
+
116
+ const result = await getCompilerOptionsForPackage(optionsWithoutLib, "node", packageDir);
117
+
118
+ // handles undefined lib without error
119
+ expect(result.lib).toBeUndefined();
120
+ expect(result.types).toEqual(["node"]);
121
+ });
122
+
123
+ it("does not mutate original baseOptions (immutability)", async () => {
124
+ const originalOptions: ts.CompilerOptions = {
125
+ lib: ["ES2024", "DOM"],
126
+ types: ["original"],
127
+ strict: true,
128
+ };
129
+ const packageDir = "/project/packages/core-node";
130
+ vi.mocked(fsExists).mockResolvedValue(false);
131
+
132
+ await getCompilerOptionsForPackage(originalOptions, "node", packageDir);
133
+
134
+ // original options are not changed
135
+ expect(originalOptions.lib).toEqual(["ES2024", "DOM"]);
136
+ expect(originalOptions.types).toEqual(["original"]);
137
+ expect(originalOptions.noEmit).toBeUndefined();
138
+ });
139
+ });
@@ -0,0 +1,181 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import path from "path";
3
+ import type ts from "typescript";
4
+ import { getPackageSourceFiles, getPackageFiles } from "../src/utils/tsconfig";
5
+
6
+ describe("getPackageSourceFiles", () => {
7
+ const sep = path.sep;
8
+
9
+ it("filters files within package src directory only", () => {
10
+ const pkgDir = `/project/packages/core-common`;
11
+ const parsedConfig = {
12
+ fileNames: [
13
+ `/project/packages/core-common/src/index.ts`,
14
+ `/project/packages/core-common/src/utils/string.ts`,
15
+ `/project/packages/core-common/tests/utils.spec.ts`,
16
+ `/project/packages/core-node/src/index.ts`,
17
+ ],
18
+ } as ts.ParsedCommandLine;
19
+
20
+ const result = getPackageSourceFiles(pkgDir, parsedConfig);
21
+
22
+ expect(result).toEqual([
23
+ `/project/packages/core-common/src/index.ts`,
24
+ `/project/packages/core-common/src/utils/string.ts`,
25
+ ]);
26
+ });
27
+
28
+ it("excludes files from similar package names (core vs core-common)", () => {
29
+ const pkgDir = `/project/packages/core`;
30
+ const parsedConfig = {
31
+ fileNames: [
32
+ `/project/packages/core/src/index.ts`,
33
+ `/project/packages/core-common/src/index.ts`,
34
+ `/project/packages/core-node/src/index.ts`,
35
+ ],
36
+ } as ts.ParsedCommandLine;
37
+
38
+ const result = getPackageSourceFiles(pkgDir, parsedConfig);
39
+
40
+ // core-common, core-node are excluded, only core is included
41
+ expect(result).toEqual([`/project/packages/core/src/index.ts`]);
42
+ });
43
+
44
+ it("excludes files outside src directory (tests, scripts, etc)", () => {
45
+ const pkgDir = `/project/packages/cli`;
46
+ const parsedConfig = {
47
+ fileNames: [
48
+ `/project/packages/cli/src/index.ts`,
49
+ `/project/packages/cli/src/commands/lint.ts`,
50
+ `/project/packages/cli/tests/lint.spec.ts`,
51
+ `/project/packages/cli/scripts/build.ts`,
52
+ ],
53
+ } as ts.ParsedCommandLine;
54
+
55
+ const result = getPackageSourceFiles(pkgDir, parsedConfig);
56
+
57
+ expect(result).toEqual([
58
+ `/project/packages/cli/src/index.ts`,
59
+ `/project/packages/cli/src/commands/lint.ts`,
60
+ ]);
61
+ });
62
+
63
+ it("returns empty array if no files", () => {
64
+ const pkgDir = `/project/packages/empty`;
65
+ const parsedConfig = {
66
+ fileNames: [
67
+ `/project/packages/core/src/index.ts`,
68
+ `/project/packages/core-common/src/index.ts`,
69
+ ],
70
+ } as ts.ParsedCommandLine;
71
+
72
+ const result = getPackageSourceFiles(pkgDir, parsedConfig);
73
+
74
+ expect(result).toEqual([]);
75
+ });
76
+
77
+ it("handles path separators correctly", () => {
78
+ // use path.sep for platform-independent test
79
+ const pkgDir = `${sep}project${sep}packages${sep}core`;
80
+ const parsedConfig = {
81
+ fileNames: [
82
+ `${sep}project${sep}packages${sep}core${sep}src${sep}index.ts`,
83
+ `${sep}project${sep}packages${sep}core-common${sep}src${sep}index.ts`,
84
+ ],
85
+ } as ts.ParsedCommandLine;
86
+
87
+ const result = getPackageSourceFiles(pkgDir, parsedConfig);
88
+
89
+ expect(result).toEqual([`${sep}project${sep}packages${sep}core${sep}src${sep}index.ts`]);
90
+ });
91
+
92
+ it("handles forward slash paths from TypeScript API correctly (Windows compatible)", () => {
93
+ // TypeScript API returns forward slash paths even on Windows
94
+ // pkgDir is created with path.join using OS-native separator
95
+ const pkgDir = path.resolve("/project/packages/core-common");
96
+ const parsedConfig = {
97
+ fileNames: [
98
+ "/project/packages/core-common/src/index.ts",
99
+ "/project/packages/core-common/src/utils/string.ts",
100
+ "/project/packages/core-common/tests/utils.spec.ts",
101
+ "/project/packages/core-node/src/index.ts",
102
+ ],
103
+ } as ts.ParsedCommandLine;
104
+
105
+ const result = getPackageSourceFiles(pkgDir, parsedConfig);
106
+
107
+ expect(result).toHaveLength(2);
108
+ expect(result.every((f) => f.includes("core-common") && f.includes("src"))).toBe(true);
109
+ });
110
+ });
111
+
112
+ describe("getPackageFiles", () => {
113
+ it("filters all files within package directory (src + tests)", () => {
114
+ const pkgDir = `/project/packages/core-common`;
115
+ const parsedConfig = {
116
+ fileNames: [
117
+ `/project/packages/core-common/src/index.ts`,
118
+ `/project/packages/core-common/src/utils/string.ts`,
119
+ `/project/packages/core-common/tests/utils.spec.ts`,
120
+ `/project/packages/core-common/tests/setup/helpers.ts`,
121
+ `/project/packages/core-node/src/index.ts`,
122
+ `/project/packages/core-node/tests/fs.spec.ts`,
123
+ ],
124
+ } as ts.ParsedCommandLine;
125
+
126
+ const result = getPackageFiles(pkgDir, parsedConfig);
127
+
128
+ expect(result).toEqual([
129
+ `/project/packages/core-common/src/index.ts`,
130
+ `/project/packages/core-common/src/utils/string.ts`,
131
+ `/project/packages/core-common/tests/utils.spec.ts`,
132
+ `/project/packages/core-common/tests/setup/helpers.ts`,
133
+ ]);
134
+ });
135
+
136
+ it("excludes files from similar package names (core vs core-common)", () => {
137
+ const pkgDir = `/project/packages/core`;
138
+ const parsedConfig = {
139
+ fileNames: [
140
+ `/project/packages/core/src/index.ts`,
141
+ `/project/packages/core/tests/utils.spec.ts`,
142
+ `/project/packages/core-common/src/index.ts`,
143
+ `/project/packages/core-common/tests/utils.spec.ts`,
144
+ ],
145
+ } as ts.ParsedCommandLine;
146
+
147
+ const result = getPackageFiles(pkgDir, parsedConfig);
148
+
149
+ expect(result).toEqual([
150
+ `/project/packages/core/src/index.ts`,
151
+ `/project/packages/core/tests/utils.spec.ts`,
152
+ ]);
153
+ });
154
+
155
+ it("returns empty array if no files", () => {
156
+ const pkgDir = `/project/packages/empty`;
157
+ const parsedConfig = {
158
+ fileNames: [`/project/packages/core/src/index.ts`],
159
+ } as ts.ParsedCommandLine;
160
+
161
+ const result = getPackageFiles(pkgDir, parsedConfig);
162
+
163
+ expect(result).toEqual([]);
164
+ });
165
+
166
+ it("handles forward slash paths from TypeScript API correctly (Windows compatible)", () => {
167
+ const pkgDir = path.resolve("/project/packages/core-common");
168
+ const parsedConfig = {
169
+ fileNames: [
170
+ "/project/packages/core-common/src/index.ts",
171
+ "/project/packages/core-common/tests/utils.spec.ts",
172
+ "/project/packages/core-node/src/index.ts",
173
+ ],
174
+ } as ts.ParsedCommandLine;
175
+
176
+ const result = getPackageFiles(pkgDir, parsedConfig);
177
+
178
+ expect(result).toHaveLength(2);
179
+ expect(result.every((f) => f.includes("core-common"))).toBe(true);
180
+ });
181
+ });
@@ -0,0 +1,107 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
+ import path from "path";
3
+
4
+ // Mock core-node functions
5
+ vi.mock("@simplysm/core-node", () => ({
6
+ fsExists: vi.fn(),
7
+ fsReadJson: vi.fn(),
8
+ pathPosix: vi.fn((p: string) => p.replace(/\\/g, "/")),
9
+ }));
10
+
11
+ import { fsExists, fsReadJson } from "@simplysm/core-node";
12
+ import { getTypesFromPackageJson } from "../src/utils/tsconfig";
13
+
14
+ describe("getTypesFromPackageJson", () => {
15
+ beforeEach(() => {
16
+ vi.clearAllMocks();
17
+ });
18
+
19
+ afterEach(() => {
20
+ vi.restoreAllMocks();
21
+ });
22
+
23
+ it("converts @types/* devDependencies to types list", async () => {
24
+ const packageDir = "/project/packages/core-common";
25
+ const mockFsExists = vi.mocked(fsExists);
26
+ const mockFsReadJson = vi.mocked(fsReadJson);
27
+
28
+ mockFsExists.mockResolvedValue(true);
29
+ mockFsReadJson.mockResolvedValue({
30
+ devDependencies: {
31
+ "@types/node": "^20.0.0",
32
+ "@types/express": "^4.17.0",
33
+ "typescript": "^5.0.0",
34
+ "vitest": "^1.0.0",
35
+ },
36
+ });
37
+
38
+ const result = await getTypesFromPackageJson(packageDir);
39
+
40
+ expect(mockFsExists).toHaveBeenCalledWith(path.join(packageDir, "package.json"));
41
+ expect(result).toEqual(["node", "express"]);
42
+ });
43
+
44
+ it("returns empty array if package.json does not exist", async () => {
45
+ const packageDir = "/project/packages/unknown";
46
+ const mockFsExists = vi.mocked(fsExists);
47
+
48
+ mockFsExists.mockResolvedValue(false);
49
+
50
+ const result = await getTypesFromPackageJson(packageDir);
51
+
52
+ expect(result).toEqual([]);
53
+ });
54
+
55
+ it("returns empty array if devDependencies does not exist", async () => {
56
+ const packageDir = "/project/packages/core-common";
57
+ const mockFsExists = vi.mocked(fsExists);
58
+ const mockFsReadJson = vi.mocked(fsReadJson);
59
+
60
+ mockFsExists.mockResolvedValue(true);
61
+ mockFsReadJson.mockResolvedValue({
62
+ name: "@simplysm/core-common",
63
+ version: "1.0.0",
64
+ });
65
+
66
+ const result = await getTypesFromPackageJson(packageDir);
67
+
68
+ expect(result).toEqual([]);
69
+ });
70
+
71
+ it("filters out dependencies that are not @types/*", async () => {
72
+ const packageDir = "/project/packages/core-common";
73
+ const mockFsExists = vi.mocked(fsExists);
74
+ const mockFsReadJson = vi.mocked(fsReadJson);
75
+
76
+ mockFsExists.mockResolvedValue(true);
77
+ mockFsReadJson.mockResolvedValue({
78
+ devDependencies: {
79
+ typescript: "^5.0.0",
80
+ vitest: "^1.0.0",
81
+ eslint: "^9.0.0",
82
+ },
83
+ });
84
+
85
+ const result = await getTypesFromPackageJson(packageDir);
86
+
87
+ expect(result).toEqual([]);
88
+ });
89
+
90
+ it("handles scoped @types packages correctly", async () => {
91
+ const packageDir = "/project/packages/core-common";
92
+ const mockFsExists = vi.mocked(fsExists);
93
+ const mockFsReadJson = vi.mocked(fsReadJson);
94
+
95
+ mockFsExists.mockResolvedValue(true);
96
+ mockFsReadJson.mockResolvedValue({
97
+ devDependencies: {
98
+ "@types/node": "^20.0.0",
99
+ "@types/babel__core": "^7.0.0",
100
+ },
101
+ });
102
+
103
+ const result = await getTypesFromPackageJson(packageDir);
104
+
105
+ expect(result).toEqual(["node", "babel__core"]);
106
+ });
107
+ });