@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
package/src/utils/sd-config.ts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { createJiti } from "jiti";
|
|
3
|
-
import { SdError } from "@simplysm/core-common";
|
|
4
|
-
import { fsExists } from "@simplysm/core-node";
|
|
5
|
-
import type { SdConfig, SdConfigParams } from "../sd-config.types";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* sd.config.ts
|
|
9
|
-
* @returns SdConfig
|
|
10
|
-
* @throws sd.config.ts
|
|
11
|
-
*/
|
|
12
|
-
export async function loadSdConfig(params: SdConfigParams): Promise<SdConfig> {
|
|
13
|
-
const sdConfigPath = path.resolve(params.cwd, "sd.config.ts");
|
|
14
|
-
|
|
15
|
-
if (!(await fsExists(sdConfigPath))) {
|
|
16
|
-
throw new SdError(`sd.config.ts
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const jiti = createJiti(import.meta.url);
|
|
20
|
-
const sdConfigModule = await jiti.import(sdConfigPath);
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
sdConfigModule == null ||
|
|
24
|
-
typeof sdConfigModule !== "object" ||
|
|
25
|
-
!("default" in sdConfigModule) ||
|
|
26
|
-
typeof sdConfigModule.default !== "function"
|
|
27
|
-
) {
|
|
28
|
-
throw new SdError(`sd.config.ts
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const config = await sdConfigModule.default(params);
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
config == null ||
|
|
35
|
-
typeof config !== "object" ||
|
|
36
|
-
!("packages" in config) ||
|
|
37
|
-
config.packages == null ||
|
|
38
|
-
typeof config.packages !== "object" ||
|
|
39
|
-
Array.isArray(config.packages)
|
|
40
|
-
) {
|
|
41
|
-
throw new SdError(`sd.config.ts
|
|
42
|
-
}
|
|
43
|
-
return config as SdConfig;
|
|
44
|
-
}
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { createJiti } from "jiti";
|
|
3
|
+
import { SdError } from "@simplysm/core-common";
|
|
4
|
+
import { fsExists } from "@simplysm/core-node";
|
|
5
|
+
import type { SdConfig, SdConfigParams } from "../sd-config.types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Load sd.config.ts
|
|
9
|
+
* @returns SdConfig object
|
|
10
|
+
* @throws if sd.config.ts is missing or format is incorrect
|
|
11
|
+
*/
|
|
12
|
+
export async function loadSdConfig(params: SdConfigParams): Promise<SdConfig> {
|
|
13
|
+
const sdConfigPath = path.resolve(params.cwd, "sd.config.ts");
|
|
14
|
+
|
|
15
|
+
if (!(await fsExists(sdConfigPath))) {
|
|
16
|
+
throw new SdError(`sd.config.ts file not found: ${sdConfigPath}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const jiti = createJiti(import.meta.url);
|
|
20
|
+
const sdConfigModule = await jiti.import(sdConfigPath);
|
|
21
|
+
|
|
22
|
+
if (
|
|
23
|
+
sdConfigModule == null ||
|
|
24
|
+
typeof sdConfigModule !== "object" ||
|
|
25
|
+
!("default" in sdConfigModule) ||
|
|
26
|
+
typeof sdConfigModule.default !== "function"
|
|
27
|
+
) {
|
|
28
|
+
throw new SdError(`sd.config.ts must export a function as default: ${sdConfigPath}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const config = await sdConfigModule.default(params);
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
config == null ||
|
|
35
|
+
typeof config !== "object" ||
|
|
36
|
+
!("packages" in config) ||
|
|
37
|
+
config.packages == null ||
|
|
38
|
+
typeof config.packages !== "object" ||
|
|
39
|
+
Array.isArray(config.packages)
|
|
40
|
+
) {
|
|
41
|
+
throw new SdError(`sd.config.ts return value is not in the correct format: ${sdConfigPath}`);
|
|
42
|
+
}
|
|
43
|
+
return config as SdConfig;
|
|
44
|
+
}
|
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
const jsExtensions = [".js", ".cjs", ".mjs"];
|
|
5
|
-
|
|
6
|
-
const jsResolutionOrder = ["", ".js", ".cjs", ".mjs", ".ts", ".cts", ".mts", ".jsx", ".tsx"];
|
|
7
|
-
const tsResolutionOrder = ["", ".ts", ".cts", ".mts", ".tsx", ".js", ".cjs", ".mjs", ".jsx"];
|
|
8
|
-
|
|
9
|
-
function resolveWithExtension(file: string, extensions: string[]): string | null {
|
|
10
|
-
for (const ext of extensions) {
|
|
11
|
-
const full = `${file}${ext}`;
|
|
12
|
-
if (fs.existsSync(full) && fs.statSync(full).isFile()) {
|
|
13
|
-
return full;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
for (const ext of extensions) {
|
|
17
|
-
const full = `${file}/index${ext}`;
|
|
18
|
-
if (fs.existsSync(full) && fs.statSync(full).isFile()) {
|
|
19
|
-
return full;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolvePackageFile(specifier: string, fromDir: string): string | null {
|
|
26
|
-
const parts = specifier.split("/");
|
|
27
|
-
const pkgName = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
28
|
-
const subPath = specifier.startsWith("@") ? parts.slice(2).join("/") : parts.slice(1).join("/");
|
|
29
|
-
|
|
30
|
-
let searchDir = fromDir;
|
|
31
|
-
while (true) {
|
|
32
|
-
const candidate = path.join(searchDir, "node_modules", pkgName);
|
|
33
|
-
if (fs.existsSync(candidate)) {
|
|
34
|
-
const realDir = fs.realpathSync(candidate);
|
|
35
|
-
if (subPath) {
|
|
36
|
-
return resolveWithExtension(path.join(realDir, subPath), tsResolutionOrder);
|
|
37
|
-
}
|
|
38
|
-
return resolveWithExtension(path.join(realDir, "index"), tsResolutionOrder);
|
|
39
|
-
}
|
|
40
|
-
const parent = path.dirname(searchDir);
|
|
41
|
-
if (parent === searchDir) break;
|
|
42
|
-
searchDir = parent;
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* Tailwind
|
|
51
|
-
*
|
|
52
|
-
*/
|
|
53
|
-
export function getTailwindConfigDeps(configPath: string, replaceDeps: string[]): string[] {
|
|
54
|
-
const seen = new Set<string>();
|
|
55
|
-
|
|
56
|
-
function isReplaceDepImport(specifier: string): boolean {
|
|
57
|
-
return replaceDeps.some((dep) => specifier === dep || specifier.startsWith(dep + "/"));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function walk(absoluteFile: string): void {
|
|
61
|
-
if (seen.has(absoluteFile)) return;
|
|
62
|
-
if (!fs.existsSync(absoluteFile)) return;
|
|
63
|
-
seen.add(absoluteFile);
|
|
64
|
-
|
|
65
|
-
const base = path.dirname(absoluteFile);
|
|
66
|
-
const ext = path.extname(absoluteFile);
|
|
67
|
-
const extensions = jsExtensions.includes(ext) ? jsResolutionOrder : tsResolutionOrder;
|
|
68
|
-
|
|
69
|
-
let contents: string;
|
|
70
|
-
try {
|
|
71
|
-
contents = fs.readFileSync(absoluteFile, "utf-8");
|
|
72
|
-
} catch {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
for (const match of [
|
|
77
|
-
...contents.matchAll(/import[\s\S]*?['"](.{3,}?)['"]/gi),
|
|
78
|
-
...contents.matchAll(/import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi),
|
|
79
|
-
...contents.matchAll(/require\(['"`](.+)['"`]\)/gi),
|
|
80
|
-
]) {
|
|
81
|
-
const specifier = match[1];
|
|
82
|
-
let resolved: string | null = null;
|
|
83
|
-
|
|
84
|
-
if (specifier.startsWith(".")) {
|
|
85
|
-
resolved = resolveWithExtension(path.resolve(base, specifier), extensions);
|
|
86
|
-
} else if (isReplaceDepImport(specifier)) {
|
|
87
|
-
resolved = resolvePackageFile(specifier, base);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (resolved != null) {
|
|
91
|
-
walk(resolved);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
walk(path.resolve(configPath));
|
|
97
|
-
return [...seen];
|
|
98
|
-
}
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
const jsExtensions = [".js", ".cjs", ".mjs"];
|
|
5
|
+
|
|
6
|
+
const jsResolutionOrder = ["", ".js", ".cjs", ".mjs", ".ts", ".cts", ".mts", ".jsx", ".tsx"];
|
|
7
|
+
const tsResolutionOrder = ["", ".ts", ".cts", ".mts", ".tsx", ".js", ".cjs", ".mjs", ".jsx"];
|
|
8
|
+
|
|
9
|
+
function resolveWithExtension(file: string, extensions: string[]): string | null {
|
|
10
|
+
for (const ext of extensions) {
|
|
11
|
+
const full = `${file}${ext}`;
|
|
12
|
+
if (fs.existsSync(full) && fs.statSync(full).isFile()) {
|
|
13
|
+
return full;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
for (const ext of extensions) {
|
|
17
|
+
const full = `${file}/index${ext}`;
|
|
18
|
+
if (fs.existsSync(full) && fs.statSync(full).isFile()) {
|
|
19
|
+
return full;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolvePackageFile(specifier: string, fromDir: string): string | null {
|
|
26
|
+
const parts = specifier.split("/");
|
|
27
|
+
const pkgName = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
28
|
+
const subPath = specifier.startsWith("@") ? parts.slice(2).join("/") : parts.slice(1).join("/");
|
|
29
|
+
|
|
30
|
+
let searchDir = fromDir;
|
|
31
|
+
while (true) {
|
|
32
|
+
const candidate = path.join(searchDir, "node_modules", pkgName);
|
|
33
|
+
if (fs.existsSync(candidate)) {
|
|
34
|
+
const realDir = fs.realpathSync(candidate);
|
|
35
|
+
if (subPath) {
|
|
36
|
+
return resolveWithExtension(path.join(realDir, subPath), tsResolutionOrder);
|
|
37
|
+
}
|
|
38
|
+
return resolveWithExtension(path.join(realDir, "index"), tsResolutionOrder);
|
|
39
|
+
}
|
|
40
|
+
const parent = path.dirname(searchDir);
|
|
41
|
+
if (parent === searchDir) break;
|
|
42
|
+
searchDir = parent;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Recursively collect dependencies of Tailwind config file
|
|
49
|
+
*
|
|
50
|
+
* Tailwind built-in `getModuleDependencies` only tracks relative path imports,
|
|
51
|
+
* but this function also resolves `node_modules` symlinks to track actual files for packages in specified scope.
|
|
52
|
+
*/
|
|
53
|
+
export function getTailwindConfigDeps(configPath: string, replaceDeps: string[]): string[] {
|
|
54
|
+
const seen = new Set<string>();
|
|
55
|
+
|
|
56
|
+
function isReplaceDepImport(specifier: string): boolean {
|
|
57
|
+
return replaceDeps.some((dep) => specifier === dep || specifier.startsWith(dep + "/"));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function walk(absoluteFile: string): void {
|
|
61
|
+
if (seen.has(absoluteFile)) return;
|
|
62
|
+
if (!fs.existsSync(absoluteFile)) return;
|
|
63
|
+
seen.add(absoluteFile);
|
|
64
|
+
|
|
65
|
+
const base = path.dirname(absoluteFile);
|
|
66
|
+
const ext = path.extname(absoluteFile);
|
|
67
|
+
const extensions = jsExtensions.includes(ext) ? jsResolutionOrder : tsResolutionOrder;
|
|
68
|
+
|
|
69
|
+
let contents: string;
|
|
70
|
+
try {
|
|
71
|
+
contents = fs.readFileSync(absoluteFile, "utf-8");
|
|
72
|
+
} catch {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const match of [
|
|
77
|
+
...contents.matchAll(/import[\s\S]*?['"](.{3,}?)['"]/gi),
|
|
78
|
+
...contents.matchAll(/import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi),
|
|
79
|
+
...contents.matchAll(/require\(['"`](.+)['"`]\)/gi),
|
|
80
|
+
]) {
|
|
81
|
+
const specifier = match[1];
|
|
82
|
+
let resolved: string | null = null;
|
|
83
|
+
|
|
84
|
+
if (specifier.startsWith(".")) {
|
|
85
|
+
resolved = resolveWithExtension(path.resolve(base, specifier), extensions);
|
|
86
|
+
} else if (isReplaceDepImport(specifier)) {
|
|
87
|
+
resolved = resolvePackageFile(specifier, base);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (resolved != null) {
|
|
91
|
+
walk(resolved);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
walk(path.resolve(configPath));
|
|
97
|
+
return [...seen];
|
|
98
|
+
}
|
package/src/utils/template.ts
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import Handlebars from "handlebars";
|
|
3
|
-
import { fsCopy, fsMkdir, fsRead, fsReaddir, fsStat, fsWrite } from "@simplysm/core-node";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - `.hbs`
|
|
9
|
-
* - `.hbs`
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
* @param srcDir -
|
|
13
|
-
* @param destDir -
|
|
14
|
-
* @param context - Handlebars
|
|
15
|
-
* @param dirReplacements -
|
|
16
|
-
*/
|
|
17
|
-
export async function renderTemplateDir(
|
|
18
|
-
srcDir: string,
|
|
19
|
-
destDir: string,
|
|
20
|
-
context: Record<string, unknown>,
|
|
21
|
-
dirReplacements?: Record<string, string>,
|
|
22
|
-
): Promise<void> {
|
|
23
|
-
await fsMkdir(destDir);
|
|
24
|
-
|
|
25
|
-
const entries = await fsReaddir(srcDir);
|
|
26
|
-
|
|
27
|
-
for (const entry of entries) {
|
|
28
|
-
const srcPath = path.join(srcDir, entry);
|
|
29
|
-
const stat = await fsStat(srcPath);
|
|
30
|
-
|
|
31
|
-
if (stat.isDirectory()) {
|
|
32
|
-
//
|
|
33
|
-
const destName = dirReplacements?.[entry] ?? entry;
|
|
34
|
-
await renderTemplateDir(
|
|
35
|
-
path.join(srcDir, entry),
|
|
36
|
-
path.join(destDir, destName),
|
|
37
|
-
context,
|
|
38
|
-
dirReplacements,
|
|
39
|
-
);
|
|
40
|
-
} else if (entry.endsWith(".hbs")) {
|
|
41
|
-
// Handlebars
|
|
42
|
-
const source = await fsRead(srcPath);
|
|
43
|
-
const template = Handlebars.compile(source, { noEscape: true });
|
|
44
|
-
const result = template(context);
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
if (result.trim().length === 0) continue;
|
|
48
|
-
|
|
49
|
-
const destFileName = entry.slice(0, -4); // .hbs
|
|
50
|
-
await fsWrite(path.join(destDir, destFileName), result);
|
|
51
|
-
} else {
|
|
52
|
-
//
|
|
53
|
-
await fsCopy(srcPath, path.join(destDir, entry));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
1
|
+
import path from "path";
|
|
2
|
+
import Handlebars from "handlebars";
|
|
3
|
+
import { fsCopy, fsMkdir, fsRead, fsReaddir, fsStat, fsWrite } from "@simplysm/core-node";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Recursively traverse template directory, render with Handlebars, and generate files
|
|
7
|
+
*
|
|
8
|
+
* - `.hbs` extension files: Compile with Handlebars → save with `.hbs` removed from name
|
|
9
|
+
* - If `.hbs` result is empty string/whitespace only: skip file creation
|
|
10
|
+
* - Other files: copy as-is (binary)
|
|
11
|
+
*
|
|
12
|
+
* @param srcDir - Template source directory
|
|
13
|
+
* @param destDir - Output destination directory
|
|
14
|
+
* @param context - Handlebars template variables
|
|
15
|
+
* @param dirReplacements - Directory name substitution map (e.g., `{ __CLIENT__: "client-admin" }`)
|
|
16
|
+
*/
|
|
17
|
+
export async function renderTemplateDir(
|
|
18
|
+
srcDir: string,
|
|
19
|
+
destDir: string,
|
|
20
|
+
context: Record<string, unknown>,
|
|
21
|
+
dirReplacements?: Record<string, string>,
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
await fsMkdir(destDir);
|
|
24
|
+
|
|
25
|
+
const entries = await fsReaddir(srcDir);
|
|
26
|
+
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
const srcPath = path.join(srcDir, entry);
|
|
29
|
+
const stat = await fsStat(srcPath);
|
|
30
|
+
|
|
31
|
+
if (stat.isDirectory()) {
|
|
32
|
+
// Apply directory name substitution
|
|
33
|
+
const destName = dirReplacements?.[entry] ?? entry;
|
|
34
|
+
await renderTemplateDir(
|
|
35
|
+
path.join(srcDir, entry),
|
|
36
|
+
path.join(destDir, destName),
|
|
37
|
+
context,
|
|
38
|
+
dirReplacements,
|
|
39
|
+
);
|
|
40
|
+
} else if (entry.endsWith(".hbs")) {
|
|
41
|
+
// Render Handlebars template
|
|
42
|
+
const source = await fsRead(srcPath);
|
|
43
|
+
const template = Handlebars.compile(source, { noEscape: true });
|
|
44
|
+
const result = template(context);
|
|
45
|
+
|
|
46
|
+
// Skip file creation if result is empty or whitespace-only
|
|
47
|
+
if (result.trim().length === 0) continue;
|
|
48
|
+
|
|
49
|
+
const destFileName = entry.slice(0, -4); // Remove .hbs
|
|
50
|
+
await fsWrite(path.join(destDir, destFileName), result);
|
|
51
|
+
} else {
|
|
52
|
+
// Copy binary files as-is
|
|
53
|
+
await fsCopy(srcPath, path.join(destDir, entry));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/utils/tsconfig.ts
CHANGED
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { fsExists, fsReadJson, pathIsChildPath } from "@simplysm/core-node";
|
|
4
|
-
import { SdError } from "@simplysm/core-common";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* DOM
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
const DOM_LIB_PATTERNS = ["dom", "webworker"] as const;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
export async function getTypesFromPackageJson(packageDir: string): Promise<string[]> {
|
|
16
|
-
const packageJsonPath = path.join(packageDir, "package.json");
|
|
17
|
-
if (!(await fsExists(packageJsonPath))) {
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const packageJson = await fsReadJson<{ devDependencies?: Record<string, string> }>(
|
|
22
|
-
packageJsonPath,
|
|
23
|
-
);
|
|
24
|
-
const devDeps = packageJson.devDependencies ?? {};
|
|
25
|
-
|
|
26
|
-
return Object.keys(devDeps)
|
|
27
|
-
.filter((dep) => dep.startsWith("@types/"))
|
|
28
|
-
.map((dep) => dep.replace("@types/", ""));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
* - node: DOM lib
|
|
34
|
-
* - browser: node
|
|
35
|
-
* - neutral: DOM lib
|
|
36
|
-
*/
|
|
37
|
-
export type TypecheckEnv = "node" | "browser" | "neutral";
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* @param baseOptions
|
|
43
|
-
* @param env
|
|
44
|
-
* @param packageDir
|
|
45
|
-
*
|
|
46
|
-
* @remarks
|
|
47
|
-
* types
|
|
48
|
-
*
|
|
49
|
-
* (
|
|
50
|
-
*/
|
|
51
|
-
export async function getCompilerOptionsForPackage(
|
|
52
|
-
baseOptions: ts.CompilerOptions,
|
|
53
|
-
env: TypecheckEnv,
|
|
54
|
-
packageDir: string,
|
|
55
|
-
): Promise<ts.CompilerOptions> {
|
|
56
|
-
const options = { ...baseOptions };
|
|
57
|
-
const packageTypes = await getTypesFromPackageJson(packageDir);
|
|
58
|
-
|
|
59
|
-
// pnpm
|
|
60
|
-
options.typeRoots = [
|
|
61
|
-
path.join(packageDir, "node_modules", "@types"),
|
|
62
|
-
path.join(process.cwd(), "node_modules", "@types"),
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
switch (env) {
|
|
66
|
-
case "node":
|
|
67
|
-
options.lib = options.lib?.filter(
|
|
68
|
-
(lib) => !DOM_LIB_PATTERNS.some((pattern) => lib.toLowerCase().includes(pattern)),
|
|
69
|
-
);
|
|
70
|
-
options.types = [...new Set([...packageTypes, "node"])];
|
|
71
|
-
break;
|
|
72
|
-
case "browser":
|
|
73
|
-
options.types = packageTypes.filter((t) => t !== "node");
|
|
74
|
-
break;
|
|
75
|
-
case "neutral":
|
|
76
|
-
options.types = [...new Set([...packageTypes, "node"])];
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return options;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
*
|
|
85
|
-
* @throws
|
|
86
|
-
*/
|
|
87
|
-
export function parseRootTsconfig(cwd: string): ts.ParsedCommandLine {
|
|
88
|
-
const tsconfigPath = path.join(cwd, "tsconfig.json");
|
|
89
|
-
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
90
|
-
|
|
91
|
-
if (configFile.error) {
|
|
92
|
-
const message = ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
|
|
93
|
-
throw new SdError(`tsconfig.json
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, cwd);
|
|
97
|
-
|
|
98
|
-
if (parsed.errors.length > 0) {
|
|
99
|
-
const messages = parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n"));
|
|
100
|
-
throw new SdError(`tsconfig.json
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return parsed;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
108
|
-
*/
|
|
109
|
-
export function getPackageSourceFiles(
|
|
110
|
-
pkgDir: string,
|
|
111
|
-
parsedConfig: ts.ParsedCommandLine,
|
|
112
|
-
): string[] {
|
|
113
|
-
const pkgSrcDir = path.join(pkgDir, "src");
|
|
114
|
-
return parsedConfig.fileNames.filter((f) => pathIsChildPath(f, pkgSrcDir));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
*
|
|
119
|
-
*/
|
|
120
|
-
export function getPackageFiles(pkgDir: string, parsedConfig: ts.ParsedCommandLine): string[] {
|
|
121
|
-
return parsedConfig.fileNames.filter((f) => {
|
|
122
|
-
if (!pathIsChildPath(f, pkgDir)) return false;
|
|
123
|
-
//
|
|
124
|
-
const relative = path.relative(pkgDir, f);
|
|
125
|
-
return path.dirname(relative) !== ".";
|
|
126
|
-
});
|
|
127
|
-
}
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fsExists, fsReadJson, pathIsChildPath } from "@simplysm/core-node";
|
|
4
|
+
import { SdError } from "@simplysm/core-common";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* DOM-related lib patterns - libs that include browser APIs
|
|
8
|
+
* Used when filtering libs that should be excluded from node environment (lib.dom.d.ts, lib.webworker.d.ts, etc)
|
|
9
|
+
*/
|
|
10
|
+
const DOM_LIB_PATTERNS = ["dom", "webworker"] as const;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Read @types/* devDependencies from package.json and return types list
|
|
14
|
+
*/
|
|
15
|
+
export async function getTypesFromPackageJson(packageDir: string): Promise<string[]> {
|
|
16
|
+
const packageJsonPath = path.join(packageDir, "package.json");
|
|
17
|
+
if (!(await fsExists(packageJsonPath))) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const packageJson = await fsReadJson<{ devDependencies?: Record<string, string> }>(
|
|
22
|
+
packageJsonPath,
|
|
23
|
+
);
|
|
24
|
+
const devDeps = packageJson.devDependencies ?? {};
|
|
25
|
+
|
|
26
|
+
return Object.keys(devDeps)
|
|
27
|
+
.filter((dep) => dep.startsWith("@types/"))
|
|
28
|
+
.map((dep) => dep.replace("@types/", ""));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Type check environment
|
|
33
|
+
* - node: remove DOM lib + add node types
|
|
34
|
+
* - browser: remove node types
|
|
35
|
+
* - neutral: keep DOM lib + add node types (for Node/browser shared packages)
|
|
36
|
+
*/
|
|
37
|
+
export type TypecheckEnv = "node" | "browser" | "neutral";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create compiler options for package
|
|
41
|
+
*
|
|
42
|
+
* @param baseOptions - Compiler options from root tsconfig
|
|
43
|
+
* @param env - Type check environment (node: remove DOM lib + add node types, browser: remove node types)
|
|
44
|
+
* @param packageDir - Package directory path
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* The types option ignores baseOptions.types and is newly constructed per package.
|
|
48
|
+
* This is because the global types in root tsconfig may not fit the package environment.
|
|
49
|
+
* (e.g., prevent node types from being included in browser packages)
|
|
50
|
+
*/
|
|
51
|
+
export async function getCompilerOptionsForPackage(
|
|
52
|
+
baseOptions: ts.CompilerOptions,
|
|
53
|
+
env: TypecheckEnv,
|
|
54
|
+
packageDir: string,
|
|
55
|
+
): Promise<ts.CompilerOptions> {
|
|
56
|
+
const options = { ...baseOptions };
|
|
57
|
+
const packageTypes = await getTypesFromPackageJson(packageDir);
|
|
58
|
+
|
|
59
|
+
// pnpm environment: search both package-specific node_modules/@types and root node_modules/@types
|
|
60
|
+
options.typeRoots = [
|
|
61
|
+
path.join(packageDir, "node_modules", "@types"),
|
|
62
|
+
path.join(process.cwd(), "node_modules", "@types"),
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
switch (env) {
|
|
66
|
+
case "node":
|
|
67
|
+
options.lib = options.lib?.filter(
|
|
68
|
+
(lib) => !DOM_LIB_PATTERNS.some((pattern) => lib.toLowerCase().includes(pattern)),
|
|
69
|
+
);
|
|
70
|
+
options.types = [...new Set([...packageTypes, "node"])];
|
|
71
|
+
break;
|
|
72
|
+
case "browser":
|
|
73
|
+
options.types = packageTypes.filter((t) => t !== "node");
|
|
74
|
+
break;
|
|
75
|
+
case "neutral":
|
|
76
|
+
options.types = [...new Set([...packageTypes, "node"])];
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Parse root tsconfig
|
|
85
|
+
* @throws If unable to read or parse tsconfig.json
|
|
86
|
+
*/
|
|
87
|
+
export function parseRootTsconfig(cwd: string): ts.ParsedCommandLine {
|
|
88
|
+
const tsconfigPath = path.join(cwd, "tsconfig.json");
|
|
89
|
+
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
90
|
+
|
|
91
|
+
if (configFile.error) {
|
|
92
|
+
const message = ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
|
|
93
|
+
throw new SdError(`Failed to read tsconfig.json: ${message}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, cwd);
|
|
97
|
+
|
|
98
|
+
if (parsed.errors.length > 0) {
|
|
99
|
+
const messages = parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n"));
|
|
100
|
+
throw new SdError(`Failed to parse tsconfig.json: ${messages.join("; ")}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get list of package source files (based on tsconfig)
|
|
108
|
+
*/
|
|
109
|
+
export function getPackageSourceFiles(
|
|
110
|
+
pkgDir: string,
|
|
111
|
+
parsedConfig: ts.ParsedCommandLine,
|
|
112
|
+
): string[] {
|
|
113
|
+
const pkgSrcDir = path.join(pkgDir, "src");
|
|
114
|
+
return parsedConfig.fileNames.filter((f) => pathIsChildPath(f, pkgSrcDir));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get full list of package files (including src + tests)
|
|
119
|
+
*/
|
|
120
|
+
export function getPackageFiles(pkgDir: string, parsedConfig: ts.ParsedCommandLine): string[] {
|
|
121
|
+
return parsedConfig.fileNames.filter((f) => {
|
|
122
|
+
if (!pathIsChildPath(f, pkgDir)) return false;
|
|
123
|
+
// Exclude files directly in package root (config files) — treated same as project root files in other tasks
|
|
124
|
+
const relative = path.relative(pkgDir, f);
|
|
125
|
+
return path.dirname(relative) !== ".";
|
|
126
|
+
});
|
|
127
|
+
}
|