@simplysm/sd-cli 13.0.69 → 13.0.71
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/README.md +10 -957
- package/dist/builders/BaseBuilder.d.ts +23 -23
- package/dist/builders/BaseBuilder.d.ts.map +1 -1
- package/dist/builders/BaseBuilder.js +15 -15
- package/dist/builders/DtsBuilder.d.ts +4 -4
- package/dist/builders/DtsBuilder.js +1 -1
- package/dist/builders/LibraryBuilder.d.ts +3 -3
- package/dist/builders/types.d.ts +10 -10
- package/dist/capacitor/capacitor.d.ts +36 -36
- package/dist/capacitor/capacitor.js +63 -63
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/add-client.d.ts +8 -8
- package/dist/commands/add-client.js +15 -15
- package/dist/commands/add-client.js.map +1 -1
- package/dist/commands/add-server.d.ts +9 -9
- package/dist/commands/add-server.js +13 -13
- package/dist/commands/add-server.js.map +1 -1
- package/dist/commands/build.d.ts +9 -9
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +9 -9
- package/dist/commands/device.d.ts +9 -9
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +17 -17
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/init.d.ts +6 -6
- package/dist/commands/init.js +12 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +23 -23
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +25 -25
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts +13 -13
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +61 -61
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.d.ts +3 -3
- package/dist/commands/replace-deps.d.ts.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/replace-deps.js.map +1 -1
- package/dist/commands/typecheck.d.ts +20 -20
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +20 -20
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts +7 -7
- package/dist/electron/electron.d.ts +27 -27
- package/dist/electron/electron.js +32 -32
- package/dist/electron/electron.js.map +1 -1
- package/dist/infra/ResultCollector.d.ts +9 -9
- package/dist/infra/ResultCollector.js +5 -5
- package/dist/infra/SignalHandler.d.ts +7 -7
- package/dist/infra/SignalHandler.js +4 -4
- package/dist/infra/WorkerManager.d.ts +14 -14
- package/dist/infra/WorkerManager.js +11 -11
- package/dist/orchestrators/BuildOrchestrator.d.ts +19 -19
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +26 -26
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.d.ts +25 -25
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +30 -30
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.d.ts +13 -13
- package/dist/orchestrators/WatchOrchestrator.js +17 -17
- package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +2 -2
- package/dist/sd-cli-entry.js +38 -38
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.d.ts +2 -2
- package/dist/sd-cli.js +1 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +84 -84
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/build-env.d.ts +1 -1
- package/dist/utils/config-editor.d.ts +5 -5
- package/dist/utils/config-editor.js +2 -2
- package/dist/utils/config-editor.js.map +1 -1
- package/dist/utils/copy-public.d.ts +9 -9
- package/dist/utils/copy-src.d.ts +9 -9
- package/dist/utils/esbuild-config.d.ts +30 -30
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/output-utils.d.ts +6 -6
- package/dist/utils/package-utils.d.ts +6 -6
- package/dist/utils/package-utils.js +1 -1
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/rebuild-manager.js +3 -3
- package/dist/utils/rebuild-manager.js.map +1 -1
- package/dist/utils/replace-deps.d.ts +25 -25
- package/dist/utils/replace-deps.js +3 -3
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts +3 -3
- package/dist/utils/sd-config.js +3 -3
- package/dist/utils/sd-config.js.map +1 -1
- package/dist/utils/tailwind-config-deps.d.ts +3 -3
- package/dist/utils/template.d.ts +8 -8
- package/dist/utils/tsconfig.d.ts +16 -16
- package/dist/utils/tsconfig.js +2 -2
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts +8 -8
- package/dist/utils/vite-config.d.ts +8 -8
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +3 -3
- package/dist/utils/worker-events.d.ts +12 -12
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-utils.d.ts +3 -3
- package/dist/utils/worker-utils.js +2 -2
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts +14 -14
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +1 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/dts.worker.d.ts +13 -13
- package/dist/workers/dts.worker.d.ts.map +1 -1
- package/dist/workers/dts.worker.js +3 -3
- package/dist/workers/dts.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts +12 -12
- package/dist/workers/library.worker.js +1 -1
- package/dist/workers/library.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/server-runtime.worker.d.ts +6 -6
- package/dist/workers/server-runtime.worker.js +6 -6
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts +20 -20
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +6 -6
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +8 -7
- package/src/builders/BaseBuilder.ts +33 -33
- package/src/builders/DtsBuilder.ts +5 -5
- package/src/builders/LibraryBuilder.ts +9 -9
- package/src/builders/types.ts +10 -10
- package/src/capacitor/capacitor.ts +119 -119
- package/src/commands/add-client.ts +31 -31
- package/src/commands/add-server.ts +34 -34
- package/src/commands/build.ts +9 -9
- package/src/commands/check.ts +5 -5
- package/src/commands/dev.ts +9 -9
- package/src/commands/device.ts +30 -30
- package/src/commands/init.ts +25 -25
- package/src/commands/lint.ts +64 -64
- package/src/commands/publish.ts +139 -139
- package/src/commands/replace-deps.ts +4 -4
- package/src/commands/typecheck.ts +74 -74
- package/src/commands/watch.ts +7 -7
- package/src/electron/electron.ts +51 -51
- package/src/infra/ResultCollector.ts +9 -9
- package/src/infra/SignalHandler.ts +7 -7
- package/src/infra/WorkerManager.ts +14 -14
- package/src/orchestrators/BuildOrchestrator.ts +76 -76
- package/src/orchestrators/DevOrchestrator.ts +88 -88
- package/src/orchestrators/WatchOrchestrator.ts +39 -39
- package/src/sd-cli-entry.ts +43 -43
- package/src/sd-cli.ts +15 -15
- package/src/sd-config.types.ts +85 -85
- package/src/utils/build-env.ts +1 -1
- package/src/utils/config-editor.ts +19 -19
- package/src/utils/copy-public.ts +17 -17
- package/src/utils/copy-src.ts +11 -11
- package/src/utils/esbuild-config.ts +33 -33
- package/src/utils/output-utils.ts +11 -11
- package/src/utils/package-utils.ts +12 -12
- package/src/utils/rebuild-manager.ts +3 -3
- package/src/utils/replace-deps.ts +361 -361
- package/src/utils/sd-config.ts +44 -44
- package/src/utils/tailwind-config-deps.ts +98 -98
- package/src/utils/template.ts +56 -56
- package/src/utils/tsconfig.ts +127 -127
- package/src/utils/typecheck-serialization.ts +86 -86
- package/src/utils/vite-config.ts +341 -341
- package/src/utils/worker-events.ts +16 -16
- package/src/utils/worker-utils.ts +45 -45
- package/src/workers/client.worker.ts +34 -34
- package/src/workers/dts.worker.ts +467 -467
- package/src/workers/library.worker.ts +314 -314
- package/src/workers/lint.worker.ts +16 -16
- package/src/workers/server-runtime.worker.ts +157 -157
- package/src/workers/server.worker.ts +572 -572
- package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
- package/templates/add-server/__SERVER__/package.json.hbs +2 -2
- package/templates/init/package.json.hbs +3 -3
- package/tests/config-editor.spec.ts +160 -0
- package/tests/copy-src.spec.ts +50 -0
- package/tests/get-compiler-options-for-package.spec.ts +139 -0
- package/tests/get-package-source-files.spec.ts +181 -0
- package/tests/get-types-from-package-json.spec.ts +107 -0
- package/tests/infra/ResultCollector.spec.ts +39 -0
- package/tests/infra/SignalHandler.spec.ts +38 -0
- package/tests/infra/WorkerManager.spec.ts +97 -0
- package/tests/load-ignore-patterns.spec.ts +188 -0
- package/tests/load-sd-config.spec.ts +137 -0
- package/tests/package-utils.spec.ts +188 -0
- package/tests/parse-root-tsconfig.spec.ts +89 -0
- package/tests/replace-deps.spec.ts +308 -0
- package/tests/run-lint.spec.ts +415 -0
- package/tests/run-typecheck.spec.ts +653 -0
- package/tests/run-watch.spec.ts +75 -0
- package/tests/sd-cli.spec.ts +330 -0
- package/tests/tailwind-config-deps.spec.ts +30 -0
- package/tests/template.spec.ts +70 -0
- package/tests/utils/rebuild-manager.spec.ts +43 -0
- package/tests/write-changed-output-files.spec.ts +97 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { filterPackagesByTargets } from "../src/utils/package-utils";
|
|
3
|
+
import type { SdPackageConfig } from "../src/sd-config.types";
|
|
4
|
+
|
|
5
|
+
describe("filterPackagesByTargets", () => {
|
|
6
|
+
const mockPackages: Record<string, SdPackageConfig | undefined> = {
|
|
7
|
+
"core-common": { target: "neutral" },
|
|
8
|
+
"core-node": { target: "node" },
|
|
9
|
+
"core-browser": { target: "browser" },
|
|
10
|
+
"solid-demo": { target: "client", server: 3000 },
|
|
11
|
+
"claude": { target: "scripts" },
|
|
12
|
+
"empty-pkg": undefined,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
it("returns all packages except scripts when targets is empty", () => {
|
|
16
|
+
const result = filterPackagesByTargets(mockPackages, []);
|
|
17
|
+
|
|
18
|
+
expect(Object.keys(result)).toHaveLength(4);
|
|
19
|
+
expect(result["core-common"]).toBeDefined();
|
|
20
|
+
expect(result["core-node"]).toBeDefined();
|
|
21
|
+
expect(result["core-browser"]).toBeDefined();
|
|
22
|
+
expect(result["solid-demo"]).toBeDefined();
|
|
23
|
+
expect(result["claude"]).toBeUndefined();
|
|
24
|
+
expect(result["empty-pkg"]).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("excludes scripts target even when specified in targets", () => {
|
|
28
|
+
const result = filterPackagesByTargets(mockPackages, ["claude"]);
|
|
29
|
+
|
|
30
|
+
expect(Object.keys(result)).toHaveLength(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("filters specific packages only", () => {
|
|
34
|
+
const result = filterPackagesByTargets(mockPackages, ["core-common", "core-node"]);
|
|
35
|
+
|
|
36
|
+
expect(Object.keys(result)).toHaveLength(2);
|
|
37
|
+
expect(result["core-common"]).toEqual({ target: "neutral" });
|
|
38
|
+
expect(result["core-node"]).toEqual({ target: "node" });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("returns empty result when specifying non-existent package", () => {
|
|
42
|
+
const result = filterPackagesByTargets(mockPackages, ["non-existent"]);
|
|
43
|
+
|
|
44
|
+
expect(Object.keys(result)).toHaveLength(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("ignores undefined package config", () => {
|
|
48
|
+
const result = filterPackagesByTargets(mockPackages, ["empty-pkg"]);
|
|
49
|
+
|
|
50
|
+
expect(Object.keys(result)).toHaveLength(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("filters client target packages", () => {
|
|
54
|
+
const result = filterPackagesByTargets(mockPackages, ["solid-demo"]);
|
|
55
|
+
|
|
56
|
+
expect(Object.keys(result)).toHaveLength(1);
|
|
57
|
+
expect(result["solid-demo"]).toEqual({ target: "client", server: 3000 });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("handles empty packages object", () => {
|
|
61
|
+
const result = filterPackagesByTargets({}, []);
|
|
62
|
+
|
|
63
|
+
expect(Object.keys(result)).toHaveLength(0);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("handles case where all packages are scripts", () => {
|
|
67
|
+
const scriptsOnly: Record<string, SdPackageConfig> = {
|
|
68
|
+
pkg1: { target: "scripts" },
|
|
69
|
+
pkg2: { target: "scripts" },
|
|
70
|
+
};
|
|
71
|
+
const result = filterPackagesByTargets(scriptsOnly, []);
|
|
72
|
+
|
|
73
|
+
expect(Object.keys(result)).toHaveLength(0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { consola, LogLevels } from "consola";
|
|
3
|
+
|
|
4
|
+
// Mock runLint, runTypecheck, runWatch, runCheck
|
|
5
|
+
vi.mock("../src/commands/lint", () => ({
|
|
6
|
+
runLint: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock("../src/commands/typecheck", () => ({
|
|
10
|
+
runTypecheck: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock("../src/commands/check", () => ({
|
|
14
|
+
runCheck: vi.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock("../src/commands/watch", () => ({
|
|
18
|
+
runWatch: vi.fn(),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock("../src/commands/build", () => ({
|
|
22
|
+
runBuild: vi.fn(),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
vi.mock("../src/commands/publish", () => ({
|
|
26
|
+
runPublish: vi.fn(),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
import { createCliParser } from "../src/sd-cli-entry";
|
|
30
|
+
import { runLint } from "../src/commands/lint";
|
|
31
|
+
import { runTypecheck } from "../src/commands/typecheck";
|
|
32
|
+
import { runCheck } from "../src/commands/check";
|
|
33
|
+
import { runWatch } from "../src/commands/watch";
|
|
34
|
+
import { runBuild } from "../src/commands/build";
|
|
35
|
+
import { runPublish } from "../src/commands/publish";
|
|
36
|
+
|
|
37
|
+
describe("sd-cli", () => {
|
|
38
|
+
let originalConsolaLevel: number;
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
vi.clearAllMocks();
|
|
42
|
+
vi.mocked(runLint).mockResolvedValue(undefined);
|
|
43
|
+
vi.mocked(runTypecheck).mockResolvedValue(undefined);
|
|
44
|
+
vi.mocked(runCheck).mockResolvedValue(undefined);
|
|
45
|
+
vi.mocked(runWatch).mockResolvedValue(undefined);
|
|
46
|
+
vi.mocked(runBuild).mockResolvedValue(undefined);
|
|
47
|
+
vi.mocked(runPublish).mockResolvedValue(undefined);
|
|
48
|
+
originalConsolaLevel = consola.level;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
vi.restoreAllMocks();
|
|
53
|
+
consola.level = originalConsolaLevel;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("lint command", () => {
|
|
57
|
+
it("calls runLint with correct options", async () => {
|
|
58
|
+
await createCliParser(["lint", "packages/core-common", "--fix"]).parse();
|
|
59
|
+
|
|
60
|
+
expect(runLint).toHaveBeenCalledWith({
|
|
61
|
+
targets: ["packages/core-common"],
|
|
62
|
+
fix: true,
|
|
63
|
+
timing: false,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("passes --timing option correctly", async () => {
|
|
68
|
+
await createCliParser(["lint", "--timing"]).parse();
|
|
69
|
+
|
|
70
|
+
expect(runLint).toHaveBeenCalledWith({
|
|
71
|
+
targets: [],
|
|
72
|
+
fix: false,
|
|
73
|
+
timing: true,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("runs lint command without targets", async () => {
|
|
78
|
+
await createCliParser(["lint"]).parse();
|
|
79
|
+
|
|
80
|
+
expect(runLint).toHaveBeenCalledWith({
|
|
81
|
+
targets: [],
|
|
82
|
+
fix: false,
|
|
83
|
+
timing: false,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("typecheck command", () => {
|
|
89
|
+
it("calls runTypecheck with correct options", async () => {
|
|
90
|
+
await createCliParser(["typecheck", "packages/cli"]).parse();
|
|
91
|
+
|
|
92
|
+
expect(runTypecheck).toHaveBeenCalledWith({
|
|
93
|
+
targets: ["packages/cli"],
|
|
94
|
+
options: [],
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("passes multiple targets to typecheck command", async () => {
|
|
99
|
+
await createCliParser(["typecheck", "packages/core-common", "packages/core-node"]).parse();
|
|
100
|
+
|
|
101
|
+
expect(runTypecheck).toHaveBeenCalledWith({
|
|
102
|
+
targets: ["packages/core-common", "packages/core-node"],
|
|
103
|
+
options: [],
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("passes --options option correctly", async () => {
|
|
108
|
+
await createCliParser(["typecheck", "-o", "dev", "-o", "test"]).parse();
|
|
109
|
+
|
|
110
|
+
expect(runTypecheck).toHaveBeenCalledWith({
|
|
111
|
+
targets: [],
|
|
112
|
+
options: ["dev", "test"],
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("check command", () => {
|
|
118
|
+
it("calls runCheck with correct options", async () => {
|
|
119
|
+
await createCliParser(["check", "packages/core-common", "--type", "typecheck,lint"]).parse();
|
|
120
|
+
|
|
121
|
+
expect(runCheck).toHaveBeenCalledWith({
|
|
122
|
+
targets: ["packages/core-common"],
|
|
123
|
+
types: ["typecheck", "lint"],
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("runs check command without targets", async () => {
|
|
128
|
+
await createCliParser(["check"]).parse();
|
|
129
|
+
|
|
130
|
+
expect(runCheck).toHaveBeenCalledWith({
|
|
131
|
+
targets: [],
|
|
132
|
+
types: ["typecheck", "lint", "test"],
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("specifies single type using --type option", async () => {
|
|
137
|
+
await createCliParser(["check", "--type", "test"]).parse();
|
|
138
|
+
|
|
139
|
+
expect(runCheck).toHaveBeenCalledWith({
|
|
140
|
+
targets: [],
|
|
141
|
+
types: ["test"],
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("watch command", () => {
|
|
147
|
+
it("calls runWatch with correct options", async () => {
|
|
148
|
+
await createCliParser(["watch", "solid"]).parse();
|
|
149
|
+
|
|
150
|
+
expect(runWatch).toHaveBeenCalledWith({
|
|
151
|
+
targets: ["solid"],
|
|
152
|
+
options: [],
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("passes multiple targets to watch command", async () => {
|
|
157
|
+
await createCliParser(["watch", "solid", "solid-demo"]).parse();
|
|
158
|
+
|
|
159
|
+
expect(runWatch).toHaveBeenCalledWith({
|
|
160
|
+
targets: ["solid", "solid-demo"],
|
|
161
|
+
options: [],
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("runs watch command without targets", async () => {
|
|
166
|
+
await createCliParser(["watch"]).parse();
|
|
167
|
+
|
|
168
|
+
expect(runWatch).toHaveBeenCalledWith({
|
|
169
|
+
targets: [],
|
|
170
|
+
options: [],
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("passes --options option correctly", async () => {
|
|
175
|
+
await createCliParser(["watch", "-o", "dev"]).parse();
|
|
176
|
+
|
|
177
|
+
expect(runWatch).toHaveBeenCalledWith({
|
|
178
|
+
targets: [],
|
|
179
|
+
options: ["dev"],
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe("build command", () => {
|
|
185
|
+
it("calls runBuild with correct options", async () => {
|
|
186
|
+
await createCliParser(["build", "solid", "core-common"]).parse();
|
|
187
|
+
|
|
188
|
+
expect(runBuild).toHaveBeenCalledWith({
|
|
189
|
+
targets: ["solid", "core-common"],
|
|
190
|
+
options: [],
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("runs build command without targets", async () => {
|
|
195
|
+
await createCliParser(["build"]).parse();
|
|
196
|
+
|
|
197
|
+
expect(runBuild).toHaveBeenCalledWith({
|
|
198
|
+
targets: [],
|
|
199
|
+
options: [],
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("passes --options option correctly", async () => {
|
|
204
|
+
await createCliParser(["build", "-o", "prod"]).parse();
|
|
205
|
+
|
|
206
|
+
expect(runBuild).toHaveBeenCalledWith({
|
|
207
|
+
targets: [],
|
|
208
|
+
options: ["prod"],
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("publish command", () => {
|
|
214
|
+
it("calls runPublish with correct options", async () => {
|
|
215
|
+
await createCliParser(["publish", "solid", "core-common"]).parse();
|
|
216
|
+
|
|
217
|
+
expect(runPublish).toHaveBeenCalledWith({
|
|
218
|
+
targets: ["solid", "core-common"],
|
|
219
|
+
noBuild: false,
|
|
220
|
+
dryRun: false,
|
|
221
|
+
options: [],
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("runs publish command without targets", async () => {
|
|
226
|
+
await createCliParser(["publish"]).parse();
|
|
227
|
+
|
|
228
|
+
expect(runPublish).toHaveBeenCalledWith({
|
|
229
|
+
targets: [],
|
|
230
|
+
noBuild: false,
|
|
231
|
+
dryRun: false,
|
|
232
|
+
options: [],
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("passes --no-build option correctly", async () => {
|
|
237
|
+
await createCliParser(["publish", "--no-build"]).parse();
|
|
238
|
+
|
|
239
|
+
expect(runPublish).toHaveBeenCalledWith({
|
|
240
|
+
targets: [],
|
|
241
|
+
noBuild: true,
|
|
242
|
+
dryRun: false,
|
|
243
|
+
options: [],
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("passes --options option correctly", async () => {
|
|
248
|
+
await createCliParser(["publish", "-o", "prod", "-o", "test"]).parse();
|
|
249
|
+
|
|
250
|
+
expect(runPublish).toHaveBeenCalledWith({
|
|
251
|
+
targets: [],
|
|
252
|
+
noBuild: false,
|
|
253
|
+
dryRun: false,
|
|
254
|
+
options: ["prod", "test"],
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("passes all options together", async () => {
|
|
259
|
+
await createCliParser(["publish", "solid", "--no-build", "-o", "prod"]).parse();
|
|
260
|
+
|
|
261
|
+
expect(runPublish).toHaveBeenCalledWith({
|
|
262
|
+
targets: ["solid"],
|
|
263
|
+
noBuild: true,
|
|
264
|
+
dryRun: false,
|
|
265
|
+
options: ["prod"],
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("passes --dry-run option correctly", async () => {
|
|
270
|
+
await createCliParser(["publish", "--dry-run"]).parse();
|
|
271
|
+
|
|
272
|
+
expect(runPublish).toHaveBeenCalledWith({
|
|
273
|
+
targets: [],
|
|
274
|
+
noBuild: false,
|
|
275
|
+
dryRun: true,
|
|
276
|
+
options: [],
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("uses --dry-run with other options together", async () => {
|
|
281
|
+
await createCliParser(["publish", "solid", "--dry-run", "-o", "prod"]).parse();
|
|
282
|
+
|
|
283
|
+
expect(runPublish).toHaveBeenCalledWith({
|
|
284
|
+
targets: ["solid"],
|
|
285
|
+
noBuild: false,
|
|
286
|
+
dryRun: true,
|
|
287
|
+
options: ["prod"],
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("global --debug option", () => {
|
|
293
|
+
it("sets consola.level to debug with --debug option", async () => {
|
|
294
|
+
await createCliParser(["--debug", "lint"]).parse();
|
|
295
|
+
|
|
296
|
+
expect(consola.level).toBe(LogLevels.debug);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("does not change consola.level without --debug", async () => {
|
|
300
|
+
const levelBefore = consola.level;
|
|
301
|
+
await createCliParser(["lint"]).parse();
|
|
302
|
+
|
|
303
|
+
expect(consola.level).toBe(levelBefore);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe("error handling", () => {
|
|
308
|
+
it("throws error on unknown command", async () => {
|
|
309
|
+
let errorMessage: string | undefined;
|
|
310
|
+
const parser = createCliParser(["unknown"]).fail((msg) => {
|
|
311
|
+
errorMessage = msg;
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
await parser.parse();
|
|
315
|
+
|
|
316
|
+
expect(errorMessage).toMatch(/Unknown argument/);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("throws error when no command specified", async () => {
|
|
320
|
+
let errorMessage: string | undefined;
|
|
321
|
+
const parser = createCliParser([]).fail((msg) => {
|
|
322
|
+
errorMessage = msg;
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
await parser.parse();
|
|
326
|
+
|
|
327
|
+
expect(errorMessage).toMatch(/Please specify a command/);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getTailwindConfigDeps } from "../src/utils/tailwind-config-deps";
|
|
4
|
+
|
|
5
|
+
const packagesDir = path.resolve(import.meta.dirname, "../..");
|
|
6
|
+
|
|
7
|
+
describe("getTailwindConfigDeps", () => {
|
|
8
|
+
it("includes solid tailwind config in dependencies of solid-demo tailwind config", () => {
|
|
9
|
+
const configPath = path.join(packagesDir, "solid-demo/tailwind.config.ts");
|
|
10
|
+
const deps = getTailwindConfigDeps(configPath, ["@simplysm"]);
|
|
11
|
+
|
|
12
|
+
const solidConfig = path.join(packagesDir, "solid/tailwind.config.ts");
|
|
13
|
+
expect(deps).toContain(solidConfig);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("includes the config itself in dependencies", () => {
|
|
17
|
+
const configPath = path.join(packagesDir, "solid-demo/tailwind.config.ts");
|
|
18
|
+
const deps = getTailwindConfigDeps(configPath, ["@simplysm"]);
|
|
19
|
+
|
|
20
|
+
expect(deps).toContain(path.resolve(configPath));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("excludes packages outside @simplysm/ (like tailwindcss/colors)", () => {
|
|
24
|
+
const configPath = path.join(packagesDir, "solid/tailwind.config.ts");
|
|
25
|
+
const deps = getTailwindConfigDeps(configPath, ["@simplysm"]);
|
|
26
|
+
|
|
27
|
+
// solid/tailwind.config.ts imports tailwindcss/colors but does not track it
|
|
28
|
+
expect(deps).toHaveLength(1); // only itself
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { renderTemplateDir } from "../src/utils/template";
|
|
6
|
+
|
|
7
|
+
describe("renderTemplateDir", () => {
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
let srcDir: string;
|
|
10
|
+
let destDir: string;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sd-template-test-"));
|
|
14
|
+
srcDir = path.join(tmpDir, "src");
|
|
15
|
+
destDir = path.join(tmpDir, "dest");
|
|
16
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("renders .hbs files with context and removes .hbs extension", async () => {
|
|
24
|
+
fs.writeFileSync(path.join(srcDir, "hello.txt.hbs"), "Hello, {{name}}!");
|
|
25
|
+
await renderTemplateDir(srcDir, destDir, { name: "World" });
|
|
26
|
+
expect(fs.readFileSync(path.join(destDir, "hello.txt"), "utf-8")).toBe("Hello, World!");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("copies non-.hbs files as-is (binary safe)", async () => {
|
|
30
|
+
const binaryData = Uint8Array.from([0x00, 0x01, 0xff, 0xfe]);
|
|
31
|
+
fs.writeFileSync(path.join(srcDir, "icon.bin"), binaryData);
|
|
32
|
+
await renderTemplateDir(srcDir, destDir, {});
|
|
33
|
+
const copied = fs.readFileSync(path.join(destDir, "icon.bin"));
|
|
34
|
+
expect(new Uint8Array(copied)).toEqual(binaryData);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("replaces directory name placeholders", async () => {
|
|
38
|
+
const subDir = path.join(srcDir, "__CLIENT__");
|
|
39
|
+
fs.mkdirSync(subDir, { recursive: true });
|
|
40
|
+
fs.writeFileSync(path.join(subDir, "file.txt.hbs"), "pkg: {{clientName}}");
|
|
41
|
+
await renderTemplateDir(
|
|
42
|
+
srcDir,
|
|
43
|
+
destDir,
|
|
44
|
+
{ clientName: "client-admin" },
|
|
45
|
+
{ __CLIENT__: "client-admin" },
|
|
46
|
+
);
|
|
47
|
+
expect(fs.readFileSync(path.join(destDir, "client-admin", "file.txt"), "utf-8")).toBe(
|
|
48
|
+
"pkg: client-admin",
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("skips file when .hbs renders to empty/whitespace", async () => {
|
|
53
|
+
fs.writeFileSync(path.join(srcDir, "optional.ts.hbs"), "{{#if enabled}}content{{/if}}");
|
|
54
|
+
await renderTemplateDir(srcDir, destDir, { enabled: false });
|
|
55
|
+
expect(fs.existsSync(path.join(destDir, "optional.ts"))).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("handles nested directories", async () => {
|
|
59
|
+
fs.mkdirSync(path.join(srcDir, "a", "b"), { recursive: true });
|
|
60
|
+
fs.writeFileSync(path.join(srcDir, "a", "b", "deep.txt.hbs"), "{{value}}");
|
|
61
|
+
await renderTemplateDir(srcDir, destDir, { value: "nested" });
|
|
62
|
+
expect(fs.readFileSync(path.join(destDir, "a", "b", "deep.txt"), "utf-8")).toBe("nested");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("preserves Handlebars {{#if}} conditional blocks", async () => {
|
|
66
|
+
fs.writeFileSync(path.join(srcDir, "test.txt.hbs"), "start\n{{#if flag}}included\n{{/if}}end");
|
|
67
|
+
await renderTemplateDir(srcDir, destDir, { flag: true });
|
|
68
|
+
expect(fs.readFileSync(path.join(destDir, "test.txt"), "utf-8")).toBe("start\nincluded\nend");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { consola } from "consola";
|
|
3
|
+
import { RebuildManager } from "../../src/utils/rebuild-manager";
|
|
4
|
+
|
|
5
|
+
describe("RebuildManager", () => {
|
|
6
|
+
it("logs start/success messages on rebuild batch", async () => {
|
|
7
|
+
const logger = consola.withTag("test");
|
|
8
|
+
const startSpy = vi.spyOn(logger, "start");
|
|
9
|
+
const successSpy = vi.spyOn(logger, "success");
|
|
10
|
+
|
|
11
|
+
const manager = new RebuildManager(logger);
|
|
12
|
+
|
|
13
|
+
const resolver = manager.registerBuild("pkg1:build", "pkg1 (node)");
|
|
14
|
+
|
|
15
|
+
// resolve to complete the build
|
|
16
|
+
resolver();
|
|
17
|
+
|
|
18
|
+
// wait for _runBatch microtask + Promise.allSettled
|
|
19
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
20
|
+
|
|
21
|
+
expect(startSpy).toHaveBeenCalledWith("Rebuilding... (pkg1 (node))");
|
|
22
|
+
expect(successSpy).toHaveBeenCalledWith("Rebuild completed (pkg1 (node))");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("batches multiple builds and logs them together", async () => {
|
|
26
|
+
const logger = consola.withTag("test");
|
|
27
|
+
const startSpy = vi.spyOn(logger, "start");
|
|
28
|
+
const successSpy = vi.spyOn(logger, "success");
|
|
29
|
+
|
|
30
|
+
const manager = new RebuildManager(logger);
|
|
31
|
+
|
|
32
|
+
const resolver1 = manager.registerBuild("pkg1:build", "pkg1 (node)");
|
|
33
|
+
const resolver2 = manager.registerBuild("pkg2:build", "pkg2 (browser)");
|
|
34
|
+
|
|
35
|
+
resolver1();
|
|
36
|
+
resolver2();
|
|
37
|
+
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
39
|
+
|
|
40
|
+
expect(startSpy).toHaveBeenCalledWith("Rebuilding... (pkg1 (node), pkg2 (browser))");
|
|
41
|
+
expect(successSpy).toHaveBeenCalledWith("Rebuild completed (pkg1 (node), pkg2 (browser))");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
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 type esbuild from "esbuild";
|
|
6
|
+
import { writeChangedOutputFiles } from "../src/utils/esbuild-config";
|
|
7
|
+
|
|
8
|
+
function makeOutputFile(filePath: string, text: string): esbuild.OutputFile {
|
|
9
|
+
const contents = new TextEncoder().encode(text);
|
|
10
|
+
return { path: filePath, contents, text, hash: "" };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("writeChangedOutputFiles", () => {
|
|
14
|
+
let tmpDir: string;
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "esbuild-write-"));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("writes new files that don't exist on disk", async () => {
|
|
25
|
+
const outFile = makeOutputFile(path.join(tmpDir, "index.js"), "const x = 1;\n");
|
|
26
|
+
await writeChangedOutputFiles([outFile]);
|
|
27
|
+
|
|
28
|
+
const result = await fs.readFile(path.join(tmpDir, "index.js"), "utf-8");
|
|
29
|
+
expect(result).toBe("const x = 1;\n");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("skips writing when content is identical", async () => {
|
|
33
|
+
const filePath = path.join(tmpDir, "index.js");
|
|
34
|
+
await fs.writeFile(filePath, "const x = 1;\n");
|
|
35
|
+
const statBefore = await fs.stat(filePath);
|
|
36
|
+
|
|
37
|
+
// Small delay to ensure mtime would differ if file were rewritten
|
|
38
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
39
|
+
|
|
40
|
+
const outFile = makeOutputFile(filePath, "const x = 1;\n");
|
|
41
|
+
await writeChangedOutputFiles([outFile]);
|
|
42
|
+
|
|
43
|
+
const statAfter = await fs.stat(filePath);
|
|
44
|
+
expect(statAfter.mtimeMs).toBe(statBefore.mtimeMs);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("writes when content differs", async () => {
|
|
48
|
+
const filePath = path.join(tmpDir, "index.js");
|
|
49
|
+
await fs.writeFile(filePath, "const x = 1;\n");
|
|
50
|
+
|
|
51
|
+
const outFile = makeOutputFile(filePath, "const x = 2;\n");
|
|
52
|
+
await writeChangedOutputFiles([outFile]);
|
|
53
|
+
|
|
54
|
+
const result = await fs.readFile(filePath, "utf-8");
|
|
55
|
+
expect(result).toBe("const x = 2;\n");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("adds .js extension to relative imports in .js files", async () => {
|
|
59
|
+
const filePath = path.join(tmpDir, "index.js");
|
|
60
|
+
const outFile = makeOutputFile(filePath, 'import { foo } from "./utils";\nexport { foo };\n');
|
|
61
|
+
await writeChangedOutputFiles([outFile]);
|
|
62
|
+
|
|
63
|
+
const result = await fs.readFile(filePath, "utf-8");
|
|
64
|
+
expect(result).toBe('import { foo } from "./utils.js";\nexport { foo };\n');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("does not add .js extension to imports that already have known extensions", async () => {
|
|
68
|
+
const filePath = path.join(tmpDir, "index.js");
|
|
69
|
+
const outFile = makeOutputFile(
|
|
70
|
+
filePath,
|
|
71
|
+
'import "./styles.css";\nimport data from "./data.json";\n',
|
|
72
|
+
);
|
|
73
|
+
await writeChangedOutputFiles([outFile]);
|
|
74
|
+
|
|
75
|
+
const result = await fs.readFile(filePath, "utf-8");
|
|
76
|
+
expect(result).toBe('import "./styles.css";\nimport data from "./data.json";\n');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("does not rewrite imports in .js.map files", async () => {
|
|
80
|
+
const filePath = path.join(tmpDir, "index.js.map");
|
|
81
|
+
const content = '{"sources":["./utils"]}';
|
|
82
|
+
const outFile = makeOutputFile(filePath, content);
|
|
83
|
+
await writeChangedOutputFiles([outFile]);
|
|
84
|
+
|
|
85
|
+
const result = await fs.readFile(filePath, "utf-8");
|
|
86
|
+
expect(result).toBe(content);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("creates parent directories if needed", async () => {
|
|
90
|
+
const filePath = path.join(tmpDir, "sub", "deep", "index.js");
|
|
91
|
+
const outFile = makeOutputFile(filePath, "export {};\n");
|
|
92
|
+
await writeChangedOutputFiles([outFile]);
|
|
93
|
+
|
|
94
|
+
const result = await fs.readFile(filePath, "utf-8");
|
|
95
|
+
expect(result).toBe("export {};\n");
|
|
96
|
+
});
|
|
97
|
+
});
|