@simplysm/sd-cli 13.0.93 → 13.0.96
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 +67 -44
- package/dist/capacitor/capacitor.d.ts +15 -1
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +52 -31
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/electron/electron.d.ts +6 -2
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +12 -6
- package/dist/electron/electron.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +22 -4
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +4 -1
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/utils/esbuild-config.d.ts +2 -0
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +86 -6
- package/dist/utils/esbuild-config.js.map +1 -1
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +7 -0
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/replace-deps.d.ts.map +1 -1
- package/dist/utils/replace-deps.js +17 -0
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/worker-utils.d.ts +9 -1
- package/dist/utils/worker-utils.d.ts.map +1 -1
- package/dist/utils/worker-utils.js +7 -0
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +2 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/dts.worker.d.ts.map +1 -1
- package/dist/workers/dts.worker.js +2 -1
- package/dist/workers/dts.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts.map +1 -1
- package/dist/workers/library.worker.js +2 -1
- package/dist/workers/library.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.d.ts +1 -0
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +36 -2
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +144 -4
- package/dist/workers/server.worker.js.map +1 -1
- package/docs/architecture.md +14 -14
- package/docs/config-types.md +12 -2
- package/package.json +4 -4
- package/src/capacitor/capacitor.ts +59 -31
- package/src/electron/electron.ts +13 -6
- package/src/orchestrators/DevOrchestrator.ts +20 -1
- package/src/sd-cli-entry.ts +4 -1
- package/src/utils/esbuild-config.ts +86 -6
- package/src/utils/package-utils.ts +8 -0
- package/src/utils/replace-deps.ts +20 -0
- package/src/utils/worker-utils.ts +14 -1
- package/src/workers/client.worker.ts +3 -1
- package/src/workers/dts.worker.ts +3 -1
- package/src/workers/library.worker.ts +3 -1
- package/src/workers/server-runtime.worker.ts +42 -2
- package/src/workers/server.worker.ts +165 -3
- package/templates/init/package.json.hbs +3 -3
- package/templates/init/packages/client-admin/package.json.hbs +7 -7
- package/templates/init/packages/db-main/package.json.hbs +2 -2
- package/templates/init/packages/server/package.json.hbs +5 -5
- package/templates/init/tests-e2e/package.json.hbs +1 -1
- package/tests/capacitor.spec.ts +49 -0
|
@@ -39,6 +39,8 @@ export class Capacitor {
|
|
|
39
39
|
private static readonly _ANDROID_KEYSTORE_FILE_NAME = "android.keystore";
|
|
40
40
|
private static readonly _LOCK_FILE_NAME = ".capacitor.lock";
|
|
41
41
|
private static readonly _logger = consola.withTag("sd:cli:capacitor");
|
|
42
|
+
private static readonly _utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
43
|
+
private static readonly _fallbackDecoder = new TextDecoder("euc-kr");
|
|
42
44
|
|
|
43
45
|
private readonly _capPath: string;
|
|
44
46
|
private readonly _platforms: string[];
|
|
@@ -78,7 +80,7 @@ export class Capacitor {
|
|
|
78
80
|
if (typeof config.appName !== "string" || config.appName.trim() === "") {
|
|
79
81
|
throw new CapacitorConfigError("capacitor.appName is required.");
|
|
80
82
|
}
|
|
81
|
-
if (!/^[
|
|
83
|
+
if (!/^[\p{L}\p{N} \-]+$/u.test(config.appName)) {
|
|
82
84
|
throw new CapacitorConfigError(`capacitor.appName contains invalid characters: ${config.appName}`);
|
|
83
85
|
}
|
|
84
86
|
if (config.platform != null) {
|
|
@@ -91,14 +93,47 @@ export class Capacitor {
|
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Execute a Capacitor CLI command via npx
|
|
98
|
+
*/
|
|
99
|
+
private async _execCap(args: string[]): Promise<string> {
|
|
100
|
+
return this._exec("npx", ["cap", ...args], this._capPath);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Execute capacitor-assets CLI command via npx
|
|
105
|
+
*/
|
|
106
|
+
private async _execCapAssets(args: string[]): Promise<string> {
|
|
107
|
+
return this._exec("npx", ["capacitor-assets", ...args], this._capPath);
|
|
108
|
+
}
|
|
109
|
+
|
|
94
110
|
/**
|
|
95
111
|
* Execute command (with logging)
|
|
96
112
|
*/
|
|
97
113
|
private async _exec(cmd: string, args: string[], cwd: string): Promise<string> {
|
|
98
114
|
Capacitor._logger.debug(`executed command: ${cmd} ${args.join(" ")}`);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
|
|
116
|
+
const result = await execa(cmd, args, { cwd, reject: false, encoding: "buffer" });
|
|
117
|
+
const stdout = Capacitor._decodeOutput(result.stdout);
|
|
118
|
+
|
|
119
|
+
if (result.exitCode !== 0) {
|
|
120
|
+
const stderr = Capacitor._decodeOutput(result.stderr);
|
|
121
|
+
throw new Error(`${cmd} ${args.join(" ")} failed (exit ${result.exitCode}): ${stderr || stdout}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Capacitor._logger.debug(`execution result: ${stdout}`);
|
|
125
|
+
return stdout;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Decode command output (UTF-8 first, fallback to EUC-KR for Windows CP949)
|
|
130
|
+
*/
|
|
131
|
+
private static _decodeOutput(bytes: Uint8Array): string {
|
|
132
|
+
try {
|
|
133
|
+
return Capacitor._utf8Decoder.decode(bytes);
|
|
134
|
+
} catch {
|
|
135
|
+
return Capacitor._fallbackDecoder.decode(bytes);
|
|
136
|
+
}
|
|
102
137
|
}
|
|
103
138
|
|
|
104
139
|
/**
|
|
@@ -186,9 +221,9 @@ export class Capacitor {
|
|
|
186
221
|
|
|
187
222
|
// 6. Synchronize web assets
|
|
188
223
|
if (changed) {
|
|
189
|
-
await this.
|
|
224
|
+
await this._execCap(["sync"]);
|
|
190
225
|
} else {
|
|
191
|
-
await this.
|
|
226
|
+
await this._execCap(["copy"]);
|
|
192
227
|
}
|
|
193
228
|
} finally {
|
|
194
229
|
await this._releaseLock();
|
|
@@ -205,7 +240,7 @@ export class Capacitor {
|
|
|
205
240
|
const buildType = this._config.debug ? "debug" : "release";
|
|
206
241
|
|
|
207
242
|
for (const platform of this._platforms) {
|
|
208
|
-
await this.
|
|
243
|
+
await this._execCap(["copy", platform]);
|
|
209
244
|
|
|
210
245
|
if (platform === "android") {
|
|
211
246
|
await this._buildAndroid(outPath, buildType);
|
|
@@ -229,10 +264,10 @@ export class Capacitor {
|
|
|
229
264
|
}
|
|
230
265
|
|
|
231
266
|
for (const platform of this._platforms) {
|
|
232
|
-
await this.
|
|
267
|
+
await this._execCap(["copy", platform]);
|
|
233
268
|
|
|
234
269
|
try {
|
|
235
|
-
await this.
|
|
270
|
+
await this._execCap(["run", platform]);
|
|
236
271
|
} catch (err) {
|
|
237
272
|
if (platform === "android") {
|
|
238
273
|
try {
|
|
@@ -266,24 +301,22 @@ export class Capacitor {
|
|
|
266
301
|
//#region Private - Initialization
|
|
267
302
|
|
|
268
303
|
/**
|
|
269
|
-
* Basic Capacitor project initialization (package.json,
|
|
304
|
+
* Basic Capacitor project initialization (package.json, pnpm install, cap init)
|
|
270
305
|
*/
|
|
271
306
|
private async _initCap(): Promise<boolean> {
|
|
272
307
|
const depChanged = await this._setupNpmConf();
|
|
273
|
-
|
|
308
|
+
const nodeModulesExists = await fsx.exists(path.resolve(this._capPath, "node_modules"));
|
|
309
|
+
|
|
310
|
+
if (!depChanged && nodeModulesExists) return false;
|
|
274
311
|
|
|
275
312
|
// pnpm install
|
|
276
|
-
const installResult = await this._exec("
|
|
277
|
-
Capacitor._logger.debug(`
|
|
313
|
+
const installResult = await this._exec("npm", ["install"], this._capPath);
|
|
314
|
+
Capacitor._logger.debug(`npm install completed: ${installResult}`);
|
|
278
315
|
|
|
279
316
|
// F12: cap init idempotency - execute only when capacitor.config.ts does not exist
|
|
280
317
|
const configPath = path.resolve(this._capPath, "capacitor.config.ts");
|
|
281
318
|
if (!(await fsx.exists(configPath))) {
|
|
282
|
-
await this.
|
|
283
|
-
"npx",
|
|
284
|
-
["cap", "init", this._config.appName, this._config.appId],
|
|
285
|
-
this._capPath,
|
|
286
|
-
);
|
|
319
|
+
await this._execCap(["init", this._config.appId, this._config.appId]);
|
|
287
320
|
}
|
|
288
321
|
|
|
289
322
|
// Create default www/index.html
|
|
@@ -430,7 +463,7 @@ export default config;
|
|
|
430
463
|
continue;
|
|
431
464
|
}
|
|
432
465
|
|
|
433
|
-
await this.
|
|
466
|
+
await this._execCap(["add", platform]);
|
|
434
467
|
}
|
|
435
468
|
}
|
|
436
469
|
|
|
@@ -475,18 +508,13 @@ export default config;
|
|
|
475
508
|
})
|
|
476
509
|
.toFile(logoPath);
|
|
477
510
|
|
|
478
|
-
await this.
|
|
479
|
-
"
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
"--splashBackgroundColor",
|
|
486
|
-
"#ffffff",
|
|
487
|
-
],
|
|
488
|
-
this._capPath,
|
|
489
|
-
);
|
|
511
|
+
await this._execCapAssets([
|
|
512
|
+
"generate",
|
|
513
|
+
"--iconBackgroundColor",
|
|
514
|
+
"#ffffff",
|
|
515
|
+
"--splashBackgroundColor",
|
|
516
|
+
"#ffffff",
|
|
517
|
+
]);
|
|
490
518
|
} catch (err) {
|
|
491
519
|
Capacitor._logger.warn(
|
|
492
520
|
`icon generation failed: ${err instanceof Error ? err.message : err}. Using default icon.`,
|
package/src/electron/electron.ts
CHANGED
|
@@ -58,6 +58,13 @@ export class Electron {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Resolve binary path from {pkgPath}/node_modules/.bin/
|
|
63
|
+
*/
|
|
64
|
+
private _localBin(name: string): string {
|
|
65
|
+
return path.resolve(this._pkgPath, "node_modules/.bin", name);
|
|
66
|
+
}
|
|
67
|
+
|
|
61
68
|
/**
|
|
62
69
|
* Execute command (with logging)
|
|
63
70
|
*/
|
|
@@ -79,7 +86,7 @@ export class Electron {
|
|
|
79
86
|
* Initialize Electron project
|
|
80
87
|
*
|
|
81
88
|
* 1. Create .electron/src/package.json
|
|
82
|
-
* 2. Run
|
|
89
|
+
* 2. Run pnpm install
|
|
83
90
|
* 3. Run electron-rebuild (rebuild native modules)
|
|
84
91
|
*/
|
|
85
92
|
async initialize(): Promise<void> {
|
|
@@ -94,7 +101,7 @@ export class Electron {
|
|
|
94
101
|
// 3. Rebuild native modules
|
|
95
102
|
const reinstallDeps = this._config.reinstallDependencies ?? [];
|
|
96
103
|
if (reinstallDeps.length > 0) {
|
|
97
|
-
await this._exec("
|
|
104
|
+
await this._exec(this._localBin("electron-rebuild"), [], srcPath);
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
107
|
|
|
@@ -128,7 +135,7 @@ export class Electron {
|
|
|
128
135
|
*
|
|
129
136
|
* 1. Bundle electron-main.ts with esbuild
|
|
130
137
|
* 2. Create dist/electron/package.json
|
|
131
|
-
* 3. Run
|
|
138
|
+
* 3. Run electron .
|
|
132
139
|
*/
|
|
133
140
|
async run(url?: string): Promise<void> {
|
|
134
141
|
const electronRunPath = path.resolve(this._pkgPath, "dist/electron");
|
|
@@ -154,7 +161,7 @@ export class Electron {
|
|
|
154
161
|
runEnv["ELECTRON_DEV_URL"] = url;
|
|
155
162
|
}
|
|
156
163
|
|
|
157
|
-
await this._exec("
|
|
164
|
+
await this._exec(this._localBin("electron"), ["."], electronRunPath, runEnv);
|
|
158
165
|
}
|
|
159
166
|
|
|
160
167
|
//#endregion
|
|
@@ -300,8 +307,8 @@ export class Electron {
|
|
|
300
307
|
await fsx.writeJson(configFilePath, builderConfig, { space: 2 });
|
|
301
308
|
|
|
302
309
|
await this._exec(
|
|
303
|
-
"
|
|
304
|
-
["
|
|
310
|
+
this._localBin("electron-builder"),
|
|
311
|
+
["--win", "--config", configFilePath],
|
|
305
312
|
this._pkgPath,
|
|
306
313
|
);
|
|
307
314
|
}
|
|
@@ -498,7 +498,7 @@ export class DevOrchestrator {
|
|
|
498
498
|
};
|
|
499
499
|
|
|
500
500
|
// Register Server Build Worker event handlers
|
|
501
|
-
for (const { name } of this._serverPackages) {
|
|
501
|
+
for (const { name, config } of this._serverPackages) {
|
|
502
502
|
const serverBuild = this._serverBuildWorkers.get(name)!;
|
|
503
503
|
|
|
504
504
|
serverBuild.worker.on("buildStart", () => {
|
|
@@ -540,6 +540,7 @@ export class DevOrchestrator {
|
|
|
540
540
|
serverRuntimePromises,
|
|
541
541
|
resolveServerStep,
|
|
542
542
|
viteClientReadyPromises,
|
|
543
|
+
{ ...this._baseEnv, ...config.env },
|
|
543
544
|
).catch((err: unknown) => {
|
|
544
545
|
const message = errNs.message(err);
|
|
545
546
|
this._logger.error(`[${name}] Error starting Server Runtime:`, message);
|
|
@@ -610,7 +611,9 @@ export class DevOrchestrator {
|
|
|
610
611
|
serverRuntimePromises: Map<string, { promise: Promise<void>; resolver: () => void }>,
|
|
611
612
|
resolveServerStep: (serverName: string, resultKey: string, result: BuildResult) => void,
|
|
612
613
|
viteClientReadyPromises: Map<string, { promise: Promise<void>; resolver: () => void }>,
|
|
614
|
+
env?: Record<string, string>,
|
|
613
615
|
): Promise<void> {
|
|
616
|
+
const runtimeStartTime = performance.now();
|
|
614
617
|
this._logger.debug(`[${serverName}] _startServerRuntime: ${mainJsPath}`);
|
|
615
618
|
const updatedBuild = this._serverBuildWorkers.get(serverName)!;
|
|
616
619
|
updatedBuild.mainJsPath = mainJsPath;
|
|
@@ -619,10 +622,15 @@ export class DevOrchestrator {
|
|
|
619
622
|
const existingRuntime = this._serverRuntimeWorkers.get(serverName);
|
|
620
623
|
if (existingRuntime != null) {
|
|
621
624
|
this._logger.info(`[${serverName}] Restarting server...`);
|
|
625
|
+
const terminateStart = performance.now();
|
|
622
626
|
await existingRuntime.terminate();
|
|
627
|
+
this._logger.debug(
|
|
628
|
+
`[${serverName}] Previous runtime terminated (${Math.round(performance.now() - terminateStart)}ms)`,
|
|
629
|
+
);
|
|
623
630
|
}
|
|
624
631
|
|
|
625
632
|
// Create and start new Server Runtime Worker
|
|
633
|
+
this._logger.debug(`[${serverName}] Creating runtime worker...`);
|
|
626
634
|
const runtimeWorker = Worker.create<typeof ServerRuntimeWorkerModule>(serverRuntimeWorkerPath);
|
|
627
635
|
this._serverRuntimeWorkers.set(serverName, runtimeWorker);
|
|
628
636
|
|
|
@@ -635,7 +643,11 @@ export class DevOrchestrator {
|
|
|
635
643
|
`[${serverName}] Waiting for clients: ${String(clientReadyPromises.length)} total`,
|
|
636
644
|
);
|
|
637
645
|
if (clientReadyPromises.length > 0) {
|
|
646
|
+
const waitStart = performance.now();
|
|
638
647
|
await Promise.all(clientReadyPromises);
|
|
648
|
+
this._logger.debug(
|
|
649
|
+
`[${serverName}] Clients ready (${Math.round(performance.now() - waitStart)}ms)`,
|
|
650
|
+
);
|
|
639
651
|
}
|
|
640
652
|
|
|
641
653
|
// Collect client ports for this server
|
|
@@ -645,6 +657,9 @@ export class DevOrchestrator {
|
|
|
645
657
|
serverClientPorts[clientName] = this._clientPorts[clientName];
|
|
646
658
|
}
|
|
647
659
|
}
|
|
660
|
+
this._logger.debug(
|
|
661
|
+
`[${serverName}] Client ports: ${JSON.stringify(serverClientPorts)}`,
|
|
662
|
+
);
|
|
648
663
|
|
|
649
664
|
// Server Runtime event handlers
|
|
650
665
|
runtimeWorker.on("serverReady", (readyData) => {
|
|
@@ -672,10 +687,14 @@ export class DevOrchestrator {
|
|
|
672
687
|
// Start Server Runtime
|
|
673
688
|
// If worker crashes, it terminates without emitting "serverReady"/"error" events,
|
|
674
689
|
// so catch promise rejection to prevent hanging
|
|
690
|
+
this._logger.debug(
|
|
691
|
+
`[${serverName}] Starting runtime worker... (setup took ${Math.round(performance.now() - runtimeStartTime)}ms)`,
|
|
692
|
+
);
|
|
675
693
|
runtimeWorker
|
|
676
694
|
.start({
|
|
677
695
|
mainJsPath,
|
|
678
696
|
clientPorts: serverClientPorts,
|
|
697
|
+
env,
|
|
679
698
|
})
|
|
680
699
|
.catch((err: unknown) => {
|
|
681
700
|
const message = errNs.message(err);
|
package/src/sd-cli-entry.ts
CHANGED
|
@@ -77,7 +77,10 @@ export function createCliParser(argv: string[]): Argv {
|
|
|
77
77
|
global: true,
|
|
78
78
|
})
|
|
79
79
|
.middleware((args) => {
|
|
80
|
-
if (args.debug)
|
|
80
|
+
if (args.debug) {
|
|
81
|
+
consola.level = LogLevels.debug;
|
|
82
|
+
process.env["SD_DEBUG"] = "true";
|
|
83
|
+
}
|
|
81
84
|
})
|
|
82
85
|
.command(
|
|
83
86
|
"lint [targets..]",
|
|
@@ -5,6 +5,9 @@ import { createRequire } from "module";
|
|
|
5
5
|
import type esbuild from "esbuild";
|
|
6
6
|
import { solidPlugin } from "esbuild-plugin-solid";
|
|
7
7
|
import type { TypecheckEnv } from "./tsconfig";
|
|
8
|
+
import { consola } from "consola";
|
|
9
|
+
|
|
10
|
+
const logger = consola.withTag("sd:cli:esbuild-config");
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Write only changed files from esbuild outputFiles to disk
|
|
@@ -65,6 +68,8 @@ export interface ServerEsbuildOptions {
|
|
|
65
68
|
env?: Record<string, string>;
|
|
66
69
|
/** External modules to exclude from bundle */
|
|
67
70
|
external?: string[];
|
|
71
|
+
/** Dev mode: skip minification for faster builds */
|
|
72
|
+
dev?: boolean;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
/**
|
|
@@ -102,7 +107,7 @@ export function createLibraryEsbuildOptions(options: LibraryEsbuildOptions): esb
|
|
|
102
107
|
bundle: false,
|
|
103
108
|
write: false,
|
|
104
109
|
tsconfigRaw: {
|
|
105
|
-
compilerOptions: options.compilerOptions
|
|
110
|
+
compilerOptions: toEsbuildTsconfigRaw(options.compilerOptions),
|
|
106
111
|
},
|
|
107
112
|
logLevel: "silent",
|
|
108
113
|
plugins,
|
|
@@ -130,7 +135,7 @@ export function createServerEsbuildOptions(options: ServerEsbuildOptions): esbui
|
|
|
130
135
|
entryPoints: options.entryPoints,
|
|
131
136
|
outdir: path.join(options.pkgDir, "dist"),
|
|
132
137
|
format: "esm",
|
|
133
|
-
minify: true,
|
|
138
|
+
minify: options.dev !== true,
|
|
134
139
|
platform: "node",
|
|
135
140
|
target: "node20",
|
|
136
141
|
bundle: true,
|
|
@@ -140,12 +145,64 @@ export function createServerEsbuildOptions(options: ServerEsbuildOptions): esbui
|
|
|
140
145
|
external: options.external,
|
|
141
146
|
define,
|
|
142
147
|
tsconfigRaw: {
|
|
143
|
-
compilerOptions: options.compilerOptions
|
|
148
|
+
compilerOptions: toEsbuildTsconfigRaw(options.compilerOptions),
|
|
144
149
|
},
|
|
145
150
|
logLevel: "silent",
|
|
146
151
|
};
|
|
147
152
|
}
|
|
148
153
|
|
|
154
|
+
// TypeScript ScriptTarget enum → esbuild target string
|
|
155
|
+
const TARGET_MAP: Record<number, string> = {
|
|
156
|
+
0: "es3", 1: "es5", 2: "es2015", 3: "es2016", 4: "es2017",
|
|
157
|
+
5: "es2018", 6: "es2019", 7: "es2020", 8: "es2021", 9: "es2022",
|
|
158
|
+
10: "es2023", 11: "es2024", 99: "esnext",
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// TypeScript JsxEmit enum → esbuild jsx string
|
|
162
|
+
const JSX_MAP: Record<number, string> = {
|
|
163
|
+
1: "preserve", 2: "react", 3: "react-native", 4: "react-jsx", 5: "react-jsxdev",
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// TypeScript ModuleKind enum → string
|
|
167
|
+
const MODULE_MAP: Record<number, string> = {
|
|
168
|
+
0: "none", 1: "commonjs", 2: "amd", 3: "umd", 4: "system",
|
|
169
|
+
5: "es2015", 6: "es2020", 7: "es2022", 99: "esnext",
|
|
170
|
+
100: "node16", 199: "nodenext",
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// TypeScript ModuleResolutionKind enum → string
|
|
174
|
+
const MODULE_RESOLUTION_MAP: Record<number, string> = {
|
|
175
|
+
1: "node10", 2: "node16", 3: "nodenext", 100: "bundler",
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Convert TypeScript's ts.CompilerOptions to esbuild-compatible tsconfigRaw compilerOptions.
|
|
180
|
+
*
|
|
181
|
+
* TypeScript uses numeric enum values (e.g., target: 99) while esbuild expects
|
|
182
|
+
* string values (e.g., "esnext"). This converts known enum fields and passes
|
|
183
|
+
* everything else through as-is.
|
|
184
|
+
*/
|
|
185
|
+
function toEsbuildTsconfigRaw(
|
|
186
|
+
compilerOptions: Record<string, unknown>,
|
|
187
|
+
): esbuild.TsconfigRaw["compilerOptions"] {
|
|
188
|
+
const result = { ...compilerOptions };
|
|
189
|
+
|
|
190
|
+
if (typeof result["target"] === "number") {
|
|
191
|
+
result["target"] = TARGET_MAP[result["target"]] ?? "esnext";
|
|
192
|
+
}
|
|
193
|
+
if (typeof result["jsx"] === "number") {
|
|
194
|
+
result["jsx"] = JSX_MAP[result["jsx"]];
|
|
195
|
+
}
|
|
196
|
+
if (typeof result["module"] === "number") {
|
|
197
|
+
result["module"] = MODULE_MAP[result["module"]];
|
|
198
|
+
}
|
|
199
|
+
if (typeof result["moduleResolution"] === "number") {
|
|
200
|
+
result["moduleResolution"] = MODULE_RESOLUTION_MAP[result["moduleResolution"]];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return result as esbuild.TsconfigRaw["compilerOptions"];
|
|
204
|
+
}
|
|
205
|
+
|
|
149
206
|
/**
|
|
150
207
|
* Extract TypecheckEnv from build target
|
|
151
208
|
*
|
|
@@ -183,6 +240,7 @@ function scanDependencyTree(
|
|
|
183
240
|
try {
|
|
184
241
|
pkgJsonPath = req.resolve(`${pkgName}/package.json`);
|
|
185
242
|
} catch {
|
|
243
|
+
logger.debug(`[scanDependencyTree] Could not resolve: ${pkgName}`);
|
|
186
244
|
return;
|
|
187
245
|
}
|
|
188
246
|
|
|
@@ -196,7 +254,13 @@ function scanDependencyTree(
|
|
|
196
254
|
}
|
|
197
255
|
|
|
198
256
|
// Recursively traverse sub-dependencies
|
|
199
|
-
|
|
257
|
+
const subDeps = Object.keys(pkgJson.dependencies ?? {});
|
|
258
|
+
if (subDeps.length > 0) {
|
|
259
|
+
logger.debug(
|
|
260
|
+
`[scanDependencyTree] ${pkgName}: traversing ${String(subDeps.length)} sub-dependencies`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
for (const dep of subDeps) {
|
|
200
264
|
scanDependencyTree(dep, depDir, external, visited, collector);
|
|
201
265
|
}
|
|
202
266
|
}
|
|
@@ -212,7 +276,12 @@ export function collectUninstalledOptionalPeerDeps(pkgDir: string): string[] {
|
|
|
212
276
|
const visited = new Set<string>();
|
|
213
277
|
|
|
214
278
|
const pkgJson = JSON.parse(readFileSync(path.join(pkgDir, "package.json"), "utf-8")) as PkgJson;
|
|
215
|
-
|
|
279
|
+
const deps = Object.keys(pkgJson.dependencies ?? {});
|
|
280
|
+
logger.debug(
|
|
281
|
+
`[optionalPeerDeps] Scanning ${String(deps.length)} top-level dependencies...`,
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
for (const dep of deps) {
|
|
216
285
|
scanDependencyTree(dep, pkgDir, external, visited, (_pkgName, depDir, depPkgJson) => {
|
|
217
286
|
const found: string[] = [];
|
|
218
287
|
if (depPkgJson.peerDependenciesMeta != null) {
|
|
@@ -232,6 +301,9 @@ export function collectUninstalledOptionalPeerDeps(pkgDir: string): string[] {
|
|
|
232
301
|
});
|
|
233
302
|
}
|
|
234
303
|
|
|
304
|
+
logger.debug(
|
|
305
|
+
`[optionalPeerDeps] Done: visited ${String(visited.size)} packages, found ${String(external.size)} externals`,
|
|
306
|
+
);
|
|
235
307
|
return [...external];
|
|
236
308
|
}
|
|
237
309
|
|
|
@@ -250,7 +322,12 @@ export function collectNativeModuleExternals(pkgDir: string): string[] {
|
|
|
250
322
|
const visited = new Set<string>();
|
|
251
323
|
|
|
252
324
|
const pkgJson = JSON.parse(readFileSync(path.join(pkgDir, "package.json"), "utf-8")) as PkgJson;
|
|
253
|
-
|
|
325
|
+
const deps = Object.keys(pkgJson.dependencies ?? {});
|
|
326
|
+
logger.debug(
|
|
327
|
+
`[nativeModules] Scanning ${String(deps.length)} top-level dependencies...`,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
for (const dep of deps) {
|
|
254
331
|
scanDependencyTree(dep, pkgDir, external, visited, (pkgName, depDir, _pkgJson) => {
|
|
255
332
|
const found: string[] = [];
|
|
256
333
|
// Detect native modules by checking for binding.gyp
|
|
@@ -261,6 +338,9 @@ export function collectNativeModuleExternals(pkgDir: string): string[] {
|
|
|
261
338
|
});
|
|
262
339
|
}
|
|
263
340
|
|
|
341
|
+
logger.debug(
|
|
342
|
+
`[nativeModules] Done: visited ${String(visited.size)} packages, found ${String(external.size)} externals`,
|
|
343
|
+
);
|
|
264
344
|
return [...external];
|
|
265
345
|
}
|
|
266
346
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs";
|
|
3
|
+
import { consola } from "consola";
|
|
3
4
|
import type { SdPackageConfig } from "../sd-config.types";
|
|
4
5
|
|
|
6
|
+
const logger = consola.withTag("sd:cli:package-utils");
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* Walk up from import.meta.dirname to find package.json and return package root
|
|
7
10
|
*/
|
|
@@ -25,6 +28,8 @@ export function collectDeps(
|
|
|
25
28
|
cwd: string,
|
|
26
29
|
replaceDepsConfig?: Record<string, string>,
|
|
27
30
|
): DepsResult {
|
|
31
|
+
const startTime = performance.now();
|
|
32
|
+
logger.debug("[collectDeps] Starting dependency collection...");
|
|
28
33
|
const rootPkgJsonPath = path.join(cwd, "package.json");
|
|
29
34
|
const rootPkgJson = JSON.parse(fs.readFileSync(rootPkgJsonPath, "utf-8")) as { name: string };
|
|
30
35
|
const scopeMatch = rootPkgJson.name.match(/^(@[^/]+)\//);
|
|
@@ -80,6 +85,9 @@ export function collectDeps(
|
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
traverse(pkgDir);
|
|
88
|
+
logger.debug(
|
|
89
|
+
`[collectDeps] Done: workspace=${String(workspaceDeps.length)}, replace=${String(replaceDeps.length)} (${Math.round(performance.now() - startTime)}ms)`,
|
|
90
|
+
);
|
|
83
91
|
return { workspaceDeps, replaceDeps };
|
|
84
92
|
}
|
|
85
93
|
|
|
@@ -3,6 +3,8 @@ import path from "path";
|
|
|
3
3
|
import { glob } from "glob";
|
|
4
4
|
import { consola } from "consola";
|
|
5
5
|
import { fsx, pathx, FsWatcher } from "@simplysm/core-node";
|
|
6
|
+
import { exec } from "child_process";
|
|
7
|
+
import { promisify } from "util";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Match glob patterns from replaceDeps config with target package list
|
|
@@ -258,6 +260,24 @@ export async function setupReplaceDeps(
|
|
|
258
260
|
}
|
|
259
261
|
|
|
260
262
|
logger.success(`Replaced ${setupCount} dependencies`);
|
|
263
|
+
|
|
264
|
+
// Run postinstall scripts from replaced packages
|
|
265
|
+
for (const { targetName, resolvedSourcePath, actualTargetPath } of entries) {
|
|
266
|
+
const sourcePkgJsonPath = path.join(resolvedSourcePath, "package.json");
|
|
267
|
+
try {
|
|
268
|
+
const pkgJson = JSON.parse(await fs.promises.readFile(sourcePkgJsonPath, "utf-8"));
|
|
269
|
+
const postinstall = pkgJson.scripts?.postinstall as string | undefined;
|
|
270
|
+
if (postinstall == null) continue;
|
|
271
|
+
|
|
272
|
+
logger.start(`Running postinstall for ${targetName}`);
|
|
273
|
+
await promisify(exec)(postinstall, { cwd: actualTargetPath });
|
|
274
|
+
logger.success(`postinstall done: ${targetName}`);
|
|
275
|
+
} catch (err) {
|
|
276
|
+
logger.error(
|
|
277
|
+
`postinstall failed (${targetName}): ${err instanceof Error ? err.message : err}`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
261
281
|
}
|
|
262
282
|
|
|
263
283
|
/**
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import consola, { type ConsolaInstance, LogLevels } from "consola";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Apply debug log level in worker threads
|
|
5
|
+
*
|
|
6
|
+
* Checks the SD_DEBUG environment variable (set by --debug flag in main process)
|
|
7
|
+
* and applies debug log level to consola in the current worker thread.
|
|
8
|
+
* Must be called at worker module top level.
|
|
9
|
+
*/
|
|
10
|
+
export function applyDebugLevel(): void {
|
|
11
|
+
if (process.env["SD_DEBUG"] === "true") {
|
|
12
|
+
consola.level = LogLevels.debug;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
2
15
|
|
|
3
16
|
/**
|
|
4
17
|
* Register cleanup handlers for worker process shutdown signals
|
|
@@ -8,7 +8,9 @@ import type { SdClientPackageConfig } from "../sd-config.types";
|
|
|
8
8
|
import { parseRootTsconfig, getCompilerOptionsForPackage } from "../utils/tsconfig";
|
|
9
9
|
import { createViteConfig } from "../utils/vite-config";
|
|
10
10
|
import { collectDeps } from "../utils/package-utils";
|
|
11
|
-
import { registerCleanupHandlers, createOnceGuard } from "../utils/worker-utils";
|
|
11
|
+
import { registerCleanupHandlers, createOnceGuard, applyDebugLevel } from "../utils/worker-utils";
|
|
12
|
+
|
|
13
|
+
applyDebugLevel();
|
|
12
14
|
|
|
13
15
|
//#region Types
|
|
14
16
|
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
type TypecheckEnv,
|
|
12
12
|
} from "../utils/tsconfig";
|
|
13
13
|
import { serializeDiagnostic, type SerializedDiagnostic } from "../utils/typecheck-serialization";
|
|
14
|
-
import { createOnceGuard, registerCleanupHandlers } from "../utils/worker-utils";
|
|
14
|
+
import { createOnceGuard, registerCleanupHandlers, applyDebugLevel } from "../utils/worker-utils";
|
|
15
|
+
|
|
16
|
+
applyDebugLevel();
|
|
15
17
|
|
|
16
18
|
//#region Types
|
|
17
19
|
|
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
getTypecheckEnvFromTarget,
|
|
15
15
|
writeChangedOutputFiles,
|
|
16
16
|
} from "../utils/esbuild-config";
|
|
17
|
-
import { registerCleanupHandlers, createOnceGuard } from "../utils/worker-utils";
|
|
17
|
+
import { registerCleanupHandlers, createOnceGuard, applyDebugLevel } from "../utils/worker-utils";
|
|
18
|
+
|
|
19
|
+
applyDebugLevel();
|
|
18
20
|
|
|
19
21
|
//#region Types
|
|
20
22
|
|