@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
|
@@ -4,7 +4,7 @@ import { err as errNs } from "@simplysm/core-common";
|
|
|
4
4
|
import { consola } from "consola";
|
|
5
5
|
import net from "net";
|
|
6
6
|
import { pathToFileURL } from "url";
|
|
7
|
-
import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
7
|
+
import { registerCleanupHandlers, applyDebugLevel } from "../utils/worker-utils";
|
|
8
8
|
|
|
9
9
|
//#region Types
|
|
10
10
|
|
|
@@ -14,6 +14,7 @@ import { registerCleanupHandlers } from "../utils/worker-utils";
|
|
|
14
14
|
export interface ServerRuntimeStartInfo {
|
|
15
15
|
mainJsPath: string;
|
|
16
16
|
clientPorts: Record<string, number>;
|
|
17
|
+
env?: Record<string, string>;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -40,6 +41,8 @@ export interface ServerRuntimeWorkerEvents extends Record<string, unknown> {
|
|
|
40
41
|
|
|
41
42
|
//#endregion
|
|
42
43
|
|
|
44
|
+
applyDebugLevel();
|
|
45
|
+
|
|
43
46
|
const logger = consola.withTag("sd:cli:server-runtime:worker");
|
|
44
47
|
|
|
45
48
|
/** Server instance (to be cleaned up) */
|
|
@@ -109,8 +112,20 @@ async function findAvailablePort(startPort: number, maxRetries = 20): Promise<nu
|
|
|
109
112
|
*/
|
|
110
113
|
async function start(info: ServerRuntimeStartInfo): Promise<void> {
|
|
111
114
|
try {
|
|
115
|
+
const startTime = performance.now();
|
|
116
|
+
|
|
117
|
+
// Inject environment variables into process.env before importing main.js
|
|
118
|
+
if (info.env != null) {
|
|
119
|
+
for (const [key, value] of Object.entries(info.env)) {
|
|
120
|
+
process.env[key] = value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
112
124
|
// Import main.js (must export a server instance)
|
|
125
|
+
logger.debug("[start] Importing main.js...");
|
|
126
|
+
let stepStart = performance.now();
|
|
113
127
|
const module = await import(pathToFileURL(info.mainJsPath).href);
|
|
128
|
+
logger.debug(`[start] main.js imported (${Math.round(performance.now() - stepStart)}ms)`);
|
|
114
129
|
const server = module.server;
|
|
115
130
|
|
|
116
131
|
if (server == null) {
|
|
@@ -121,15 +136,28 @@ async function start(info: ServerRuntimeStartInfo): Promise<void> {
|
|
|
121
136
|
serverInstance = server;
|
|
122
137
|
|
|
123
138
|
// Find available port (auto-increment on port conflict)
|
|
139
|
+
logger.debug("[start] Finding available port...");
|
|
140
|
+
stepStart = performance.now();
|
|
124
141
|
const originalPort = server.options.port;
|
|
125
142
|
const availablePort = await findAvailablePort(originalPort);
|
|
126
143
|
if (availablePort !== originalPort) {
|
|
127
144
|
logger.info(`Port ${originalPort} in use, changing to ${availablePort}`);
|
|
128
145
|
server.options.port = availablePort;
|
|
129
146
|
}
|
|
147
|
+
logger.debug(
|
|
148
|
+
`[start] Port ${String(availablePort)} available (${Math.round(performance.now() - stepStart)}ms)`,
|
|
149
|
+
);
|
|
130
150
|
|
|
131
151
|
// Configure Vite proxy (only if clientPorts exists)
|
|
132
|
-
|
|
152
|
+
const clientEntries = Object.entries(info.clientPorts);
|
|
153
|
+
if (clientEntries.length > 0) {
|
|
154
|
+
logger.debug(
|
|
155
|
+
`[start] Configuring ${String(clientEntries.length)} Vite proxy(s)...`,
|
|
156
|
+
);
|
|
157
|
+
stepStart = performance.now();
|
|
158
|
+
}
|
|
159
|
+
for (const [name, port] of clientEntries) {
|
|
160
|
+
logger.debug(`[start] Registering proxy: /${name} -> http://127.0.0.1:${String(port)}`);
|
|
133
161
|
await server.fastify.register(proxy, {
|
|
134
162
|
prefix: `/${name}`,
|
|
135
163
|
upstream: `http://127.0.0.1:${port}`,
|
|
@@ -137,9 +165,21 @@ async function start(info: ServerRuntimeStartInfo): Promise<void> {
|
|
|
137
165
|
websocket: true,
|
|
138
166
|
});
|
|
139
167
|
}
|
|
168
|
+
if (clientEntries.length > 0) {
|
|
169
|
+
logger.debug(
|
|
170
|
+
`[start] Proxies configured (${Math.round(performance.now() - stepStart)}ms)`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
140
173
|
|
|
141
174
|
// Start server
|
|
175
|
+
logger.debug("[start] Starting server listen...");
|
|
176
|
+
stepStart = performance.now();
|
|
142
177
|
await server.listen();
|
|
178
|
+
logger.debug(`[start] Server listening (${Math.round(performance.now() - stepStart)}ms)`);
|
|
179
|
+
|
|
180
|
+
logger.debug(
|
|
181
|
+
`[start] Total runtime startup: ${Math.round(performance.now() - startTime)}ms`,
|
|
182
|
+
);
|
|
143
183
|
|
|
144
184
|
sender.send("serverReady", { port: server.options.port });
|
|
145
185
|
} catch (err) {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
collectNativeModuleExternals,
|
|
17
17
|
writeChangedOutputFiles,
|
|
18
18
|
} from "../utils/esbuild-config";
|
|
19
|
-
import { registerCleanupHandlers, createOnceGuard } from "../utils/worker-utils";
|
|
19
|
+
import { registerCleanupHandlers, createOnceGuard, applyDebugLevel } from "../utils/worker-utils";
|
|
20
20
|
import { collectDeps } from "../utils/package-utils";
|
|
21
21
|
import { copyPublicFiles, watchPublicFiles } from "../utils/copy-public";
|
|
22
22
|
|
|
@@ -101,6 +101,8 @@ export interface ServerWorkerEvents extends Record<string, unknown> {
|
|
|
101
101
|
|
|
102
102
|
//#region Resource Management
|
|
103
103
|
|
|
104
|
+
applyDebugLevel();
|
|
105
|
+
|
|
104
106
|
const logger = consola.withTag("sd:cli:server:worker");
|
|
105
107
|
|
|
106
108
|
/** esbuild build context (to be cleaned up) */
|
|
@@ -151,8 +153,20 @@ async function cleanup(): Promise<void> {
|
|
|
151
153
|
* 3. Manually specified in sd.config.ts
|
|
152
154
|
*/
|
|
153
155
|
function collectAllExternals(pkgDir: string, manualExternals?: string[]): string[] {
|
|
156
|
+
logger.debug("[externals] Scanning optional peer deps...");
|
|
157
|
+
let stepStart = performance.now();
|
|
154
158
|
const optionalPeerDeps = collectUninstalledOptionalPeerDeps(pkgDir);
|
|
159
|
+
logger.debug(
|
|
160
|
+
`[externals] Optional peer deps done: ${String(optionalPeerDeps.length)} found (${Math.round(performance.now() - stepStart)}ms)`,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
logger.debug("[externals] Scanning native modules...");
|
|
164
|
+
stepStart = performance.now();
|
|
155
165
|
const nativeModules = collectNativeModuleExternals(pkgDir);
|
|
166
|
+
logger.debug(
|
|
167
|
+
`[externals] Native modules done: ${String(nativeModules.length)} found (${Math.round(performance.now() - stepStart)}ms)`,
|
|
168
|
+
);
|
|
169
|
+
|
|
156
170
|
const manual = manualExternals ?? [];
|
|
157
171
|
|
|
158
172
|
const merged = [...new Set([...optionalPeerDeps, ...nativeModules, ...manual])];
|
|
@@ -307,20 +321,39 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
|
|
|
307
321
|
|
|
308
322
|
try {
|
|
309
323
|
// Parse tsconfig
|
|
324
|
+
logger.debug("[build] Parsing tsconfig...");
|
|
325
|
+
let stepStart = performance.now();
|
|
310
326
|
const parsedConfig = parseRootTsconfig(info.cwd);
|
|
327
|
+
logger.debug(`[build] tsconfig parsed (${Math.round(performance.now() - stepStart)}ms)`);
|
|
328
|
+
|
|
329
|
+
stepStart = performance.now();
|
|
311
330
|
const entryPoints = getPackageSourceFiles(info.pkgDir, parsedConfig);
|
|
331
|
+
logger.debug(
|
|
332
|
+
`[build] Found ${String(entryPoints.length)} source files (${Math.round(performance.now() - stepStart)}ms)`,
|
|
333
|
+
);
|
|
312
334
|
|
|
313
335
|
// Server target is node environment
|
|
336
|
+
logger.debug("[build] Getting compiler options...");
|
|
337
|
+
stepStart = performance.now();
|
|
314
338
|
const compilerOptions = await getCompilerOptionsForPackage(
|
|
315
339
|
parsedConfig.options,
|
|
316
340
|
"node",
|
|
317
341
|
info.pkgDir,
|
|
318
342
|
);
|
|
343
|
+
logger.debug(
|
|
344
|
+
`[build] Compiler options ready (${Math.round(performance.now() - stepStart)}ms)`,
|
|
345
|
+
);
|
|
319
346
|
|
|
320
347
|
// Collect all externals (optional peer deps + native modules + manual)
|
|
348
|
+
logger.debug("[build] Collecting externals...");
|
|
349
|
+
stepStart = performance.now();
|
|
321
350
|
const external = collectAllExternals(info.pkgDir, info.externals);
|
|
351
|
+
logger.debug(
|
|
352
|
+
`[build] Collected ${String(external.length)} externals (${Math.round(performance.now() - stepStart)}ms)`,
|
|
353
|
+
);
|
|
322
354
|
|
|
323
355
|
// One-time esbuild
|
|
356
|
+
logger.debug("[build] Creating esbuild options...");
|
|
324
357
|
const esbuildOptions = createServerEsbuildOptions({
|
|
325
358
|
pkgDir: info.pkgDir,
|
|
326
359
|
entryPoints,
|
|
@@ -329,17 +362,30 @@ async function build(info: ServerBuildInfo): Promise<ServerBuildResult> {
|
|
|
329
362
|
external,
|
|
330
363
|
});
|
|
331
364
|
|
|
365
|
+
logger.debug("[build] Running esbuild...");
|
|
366
|
+
stepStart = performance.now();
|
|
332
367
|
const result = await esbuild.build(esbuildOptions);
|
|
368
|
+
logger.debug(`[build] esbuild done (${Math.round(performance.now() - stepStart)}ms)`);
|
|
333
369
|
|
|
334
370
|
// Generate .config.json
|
|
335
371
|
const confDistPath = path.join(info.pkgDir, "dist", ".config.json");
|
|
336
372
|
fs.writeFileSync(confDistPath, JSON.stringify(info.configs ?? {}, undefined, 2));
|
|
337
373
|
|
|
338
374
|
// Copy public/ to dist/ (production build: no public-dev)
|
|
375
|
+
logger.debug("[build] Copying public files...");
|
|
376
|
+
stepStart = performance.now();
|
|
339
377
|
await copyPublicFiles(info.pkgDir, false);
|
|
378
|
+
logger.debug(
|
|
379
|
+
`[build] Public files copied (${Math.round(performance.now() - stepStart)}ms)`,
|
|
380
|
+
);
|
|
340
381
|
|
|
341
382
|
// Generate production files (package.json, mise.toml, openssl.cnf, pm2.config.cjs)
|
|
383
|
+
logger.debug("[build] Generating production files...");
|
|
384
|
+
stepStart = performance.now();
|
|
342
385
|
generateProductionFiles(info, external);
|
|
386
|
+
logger.debug(
|
|
387
|
+
`[build] Production files generated (${Math.round(performance.now() - stepStart)}ms)`,
|
|
388
|
+
);
|
|
343
389
|
|
|
344
390
|
const errors = result.errors.map((e) => e.text);
|
|
345
391
|
const warnings = result.warnings.map((w) => w.text);
|
|
@@ -368,26 +414,53 @@ async function createAndBuildContext(
|
|
|
368
414
|
isFirstBuild: boolean,
|
|
369
415
|
resolveFirstBuild?: () => void,
|
|
370
416
|
): Promise<esbuild.BuildContext> {
|
|
417
|
+
const contextStart = performance.now();
|
|
418
|
+
|
|
419
|
+
logger.debug("[context] Parsing tsconfig...");
|
|
420
|
+
let stepStart = performance.now();
|
|
371
421
|
const parsedConfig = parseRootTsconfig(info.cwd);
|
|
422
|
+
logger.debug(`[context] tsconfig parsed (${Math.round(performance.now() - stepStart)}ms)`);
|
|
423
|
+
|
|
424
|
+
stepStart = performance.now();
|
|
372
425
|
const entryPoints = getPackageSourceFiles(info.pkgDir, parsedConfig);
|
|
426
|
+
logger.debug(
|
|
427
|
+
`[context] Found ${String(entryPoints.length)} source files (${Math.round(performance.now() - stepStart)}ms)`,
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
logger.debug("[context] Getting compiler options...");
|
|
431
|
+
stepStart = performance.now();
|
|
373
432
|
const compilerOptions = await getCompilerOptionsForPackage(
|
|
374
433
|
parsedConfig.options,
|
|
375
434
|
"node",
|
|
376
435
|
info.pkgDir,
|
|
377
436
|
);
|
|
437
|
+
logger.debug(
|
|
438
|
+
`[context] Compiler options ready (${Math.round(performance.now() - stepStart)}ms)`,
|
|
439
|
+
);
|
|
378
440
|
|
|
379
441
|
const mainJsPath = path.join(info.pkgDir, "dist", "main.js");
|
|
442
|
+
|
|
443
|
+
logger.debug("[context] Collecting externals...");
|
|
444
|
+
stepStart = performance.now();
|
|
380
445
|
const external = collectAllExternals(info.pkgDir, info.externals);
|
|
446
|
+
logger.debug(
|
|
447
|
+
`[context] Collected ${String(external.length)} externals (${Math.round(performance.now() - stepStart)}ms)`,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
logger.debug("[context] Creating esbuild options (dev mode, minify disabled)...");
|
|
381
451
|
const baseOptions = createServerEsbuildOptions({
|
|
382
452
|
pkgDir: info.pkgDir,
|
|
383
453
|
entryPoints,
|
|
384
454
|
compilerOptions,
|
|
385
455
|
env: info.env,
|
|
386
456
|
external,
|
|
457
|
+
dev: true,
|
|
387
458
|
});
|
|
388
459
|
|
|
389
460
|
let isBuildFirstTime = isFirstBuild;
|
|
390
461
|
|
|
462
|
+
logger.debug("[context] Creating esbuild context...");
|
|
463
|
+
stepStart = performance.now();
|
|
391
464
|
const context = await esbuild.context({
|
|
392
465
|
...baseOptions,
|
|
393
466
|
metafile: true,
|
|
@@ -396,14 +469,43 @@ async function createAndBuildContext(
|
|
|
396
469
|
{
|
|
397
470
|
name: "watch-notify",
|
|
398
471
|
setup(pluginBuild) {
|
|
472
|
+
let consecutiveStarts = 0;
|
|
473
|
+
|
|
399
474
|
pluginBuild.onStart(() => {
|
|
400
|
-
|
|
475
|
+
consecutiveStarts++;
|
|
476
|
+
logger.debug(`[esbuild] onStart (#${String(consecutiveStarts)})`);
|
|
477
|
+
|
|
478
|
+
if (consecutiveStarts > 3) {
|
|
479
|
+
// esbuild context.rebuild() silently retries on build errors (esbuild bug).
|
|
480
|
+
// Stop the retry loop and diagnose via esbuild.build().
|
|
481
|
+
void context.dispose().catch(() => {});
|
|
482
|
+
|
|
483
|
+
void esbuild
|
|
484
|
+
.build(baseOptions)
|
|
485
|
+
.catch((err: unknown) => {
|
|
486
|
+
sender.send("build", {
|
|
487
|
+
success: false,
|
|
488
|
+
mainJsPath,
|
|
489
|
+
errors: [errNs.message(err)],
|
|
490
|
+
});
|
|
491
|
+
})
|
|
492
|
+
.finally(() => {
|
|
493
|
+
resolveFirstBuild?.();
|
|
494
|
+
});
|
|
495
|
+
} else {
|
|
496
|
+
sender.send("buildStart", {});
|
|
497
|
+
}
|
|
401
498
|
});
|
|
402
499
|
|
|
403
500
|
pluginBuild.onEnd(async (result) => {
|
|
501
|
+
consecutiveStarts = 0;
|
|
502
|
+
|
|
404
503
|
// Save metafile
|
|
405
504
|
if (result.metafile != null) {
|
|
406
505
|
lastMetafile = result.metafile;
|
|
506
|
+
logger.debug(
|
|
507
|
+
`[esbuild] Metafile: ${String(Object.keys(result.metafile.inputs).length)} inputs, ${String(Object.keys(result.metafile.outputs).length)} outputs`,
|
|
508
|
+
);
|
|
407
509
|
}
|
|
408
510
|
|
|
409
511
|
const errors = result.errors.map((e) => e.text);
|
|
@@ -413,7 +515,11 @@ async function createAndBuildContext(
|
|
|
413
515
|
// Write output files and check for changes
|
|
414
516
|
let hasOutputChange = false;
|
|
415
517
|
if (success && result.outputFiles != null) {
|
|
518
|
+
const writeStart = performance.now();
|
|
416
519
|
hasOutputChange = await writeChangedOutputFiles(result.outputFiles);
|
|
520
|
+
logger.debug(
|
|
521
|
+
`[esbuild] Output files written: changed=${String(hasOutputChange)}, count=${String(result.outputFiles.length)} (${Math.round(performance.now() - writeStart)}ms)`,
|
|
522
|
+
);
|
|
417
523
|
}
|
|
418
524
|
|
|
419
525
|
if (isBuildFirstTime && success) {
|
|
@@ -442,8 +548,33 @@ async function createAndBuildContext(
|
|
|
442
548
|
},
|
|
443
549
|
],
|
|
444
550
|
});
|
|
551
|
+
logger.debug(
|
|
552
|
+
`[context] esbuild context created (${Math.round(performance.now() - stepStart)}ms)`,
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
logger.debug("[context] Running initial rebuild...");
|
|
556
|
+
stepStart = performance.now();
|
|
557
|
+
const progressTimer = setInterval(() => {
|
|
558
|
+
logger.debug(
|
|
559
|
+
`[esbuild] Still building... (${Math.round((performance.now() - stepStart) / 1000)}s elapsed)`,
|
|
560
|
+
);
|
|
561
|
+
}, 5000);
|
|
562
|
+
try {
|
|
563
|
+
await context.rebuild();
|
|
564
|
+
} catch {
|
|
565
|
+
// context.rebuild() may reject with "Cannot rebuild" when disposed
|
|
566
|
+
// from onStart guard. The real error is reported via esbuild.build()
|
|
567
|
+
// fallback in onStart, so we suppress this rejection.
|
|
568
|
+
} finally {
|
|
569
|
+
clearInterval(progressTimer);
|
|
570
|
+
}
|
|
571
|
+
logger.debug(
|
|
572
|
+
`[context] Initial rebuild done (${Math.round(performance.now() - stepStart)}ms)`,
|
|
573
|
+
);
|
|
445
574
|
|
|
446
|
-
|
|
575
|
+
logger.debug(
|
|
576
|
+
`[context] Total context setup: ${Math.round(performance.now() - contextStart)}ms`,
|
|
577
|
+
);
|
|
447
578
|
|
|
448
579
|
return context;
|
|
449
580
|
}
|
|
@@ -457,6 +588,9 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
|
|
|
457
588
|
guardStartWatch();
|
|
458
589
|
|
|
459
590
|
try {
|
|
591
|
+
const watchStart = performance.now();
|
|
592
|
+
logger.debug("[startWatch] Starting watch setup...");
|
|
593
|
+
|
|
460
594
|
// Promise to wait for first build completion
|
|
461
595
|
let resolveFirstBuild!: () => void;
|
|
462
596
|
const firstBuildPromise = new Promise<void>((resolve) => {
|
|
@@ -464,16 +598,36 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
|
|
|
464
598
|
});
|
|
465
599
|
|
|
466
600
|
// Create initial esbuild context and build
|
|
601
|
+
logger.debug("[startWatch] Creating initial esbuild context...");
|
|
602
|
+
let stepStart = performance.now();
|
|
467
603
|
esbuildContext = await createAndBuildContext(info, true, resolveFirstBuild);
|
|
604
|
+
logger.debug(
|
|
605
|
+
`[startWatch] Initial context created (${Math.round(performance.now() - stepStart)}ms)`,
|
|
606
|
+
);
|
|
468
607
|
|
|
469
608
|
// Wait for first build completion
|
|
609
|
+
logger.debug("[startWatch] Waiting for first build completion...");
|
|
610
|
+
stepStart = performance.now();
|
|
470
611
|
await firstBuildPromise;
|
|
612
|
+
logger.debug(
|
|
613
|
+
`[startWatch] First build completed (${Math.round(performance.now() - stepStart)}ms)`,
|
|
614
|
+
);
|
|
471
615
|
|
|
472
616
|
// Watch public/ and public-dev/ (dev mode includes public-dev)
|
|
617
|
+
logger.debug("[startWatch] Setting up public file watcher...");
|
|
618
|
+
stepStart = performance.now();
|
|
473
619
|
publicWatcher = await watchPublicFiles(info.pkgDir, true);
|
|
620
|
+
logger.debug(
|
|
621
|
+
`[startWatch] Public watcher ready (${Math.round(performance.now() - stepStart)}ms)`,
|
|
622
|
+
);
|
|
474
623
|
|
|
475
624
|
// Collect watch paths based on dependencies
|
|
625
|
+
logger.debug("[startWatch] Collecting dependencies for watch paths...");
|
|
626
|
+
stepStart = performance.now();
|
|
476
627
|
const { workspaceDeps, replaceDeps } = collectDeps(info.pkgDir, info.cwd, info.replaceDeps);
|
|
628
|
+
logger.debug(
|
|
629
|
+
`[startWatch] Deps collected: workspace=${String(workspaceDeps.length)}, replace=${String(replaceDeps.length)} (${Math.round(performance.now() - stepStart)}ms)`,
|
|
630
|
+
);
|
|
477
631
|
|
|
478
632
|
const watchPaths: string[] = [];
|
|
479
633
|
|
|
@@ -496,7 +650,15 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
|
|
|
496
650
|
}
|
|
497
651
|
|
|
498
652
|
// Start FsWatcher
|
|
653
|
+
logger.debug(`[startWatch] Starting FsWatcher with ${String(watchPaths.length)} paths...`);
|
|
654
|
+
stepStart = performance.now();
|
|
499
655
|
srcWatcher = await FsWatcher.watch(watchPaths);
|
|
656
|
+
logger.debug(
|
|
657
|
+
`[startWatch] FsWatcher ready (${Math.round(performance.now() - stepStart)}ms)`,
|
|
658
|
+
);
|
|
659
|
+
logger.debug(
|
|
660
|
+
`[startWatch] Total watch setup: ${Math.round(performance.now() - watchStart)}ms`,
|
|
661
|
+
);
|
|
500
662
|
|
|
501
663
|
// Handle file changes
|
|
502
664
|
srcWatcher.onChange({ delay: 300 }, async (changes) => {
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"postinstall": "playwright install && playwright-cli install --skills"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@simplysm/lint": "~13.0.
|
|
23
|
-
"@simplysm/sd-cli": "~13.0.
|
|
24
|
-
"@simplysm/sd-claude": "~13.0.
|
|
22
|
+
"@simplysm/lint": "~13.0.96",
|
|
23
|
+
"@simplysm/sd-cli": "~13.0.96",
|
|
24
|
+
"@simplysm/sd-claude": "~13.0.96",
|
|
25
25
|
"@types/node": "^20.19.37",
|
|
26
26
|
"eslint": "^9.39.4",
|
|
27
27
|
"prettier": "^3.8.1",
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
"private": true,
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@{{projectName}}/db-main": "workspace:*",
|
|
9
|
-
"@simplysm/core-browser": "~13.0.
|
|
10
|
-
"@simplysm/core-common": "~13.0.
|
|
11
|
-
"@simplysm/excel": "~13.0.
|
|
12
|
-
"@simplysm/orm-common": "~13.0.
|
|
13
|
-
"@simplysm/service-client": "~13.0.
|
|
14
|
-
"@simplysm/service-common": "~13.0.
|
|
15
|
-
"@simplysm/solid": "~13.0.
|
|
9
|
+
"@simplysm/core-browser": "~13.0.96",
|
|
10
|
+
"@simplysm/core-common": "~13.0.96",
|
|
11
|
+
"@simplysm/excel": "~13.0.96",
|
|
12
|
+
"@simplysm/orm-common": "~13.0.96",
|
|
13
|
+
"@simplysm/service-client": "~13.0.96",
|
|
14
|
+
"@simplysm/service-common": "~13.0.96",
|
|
15
|
+
"@simplysm/solid": "~13.0.96",
|
|
16
16
|
"@solid-primitives/event-listener": "^2.4.5",
|
|
17
17
|
"@solidjs/router": "^0.15.4",
|
|
18
18
|
"@tabler/icons-solidjs": "^3.40.0",
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
"private": true,
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@{{projectName}}/db-main": "workspace:*",
|
|
8
|
-
"@simplysm/core-common": "~13.0.
|
|
9
|
-
"@simplysm/excel": "~13.0.
|
|
10
|
-
"@simplysm/orm-common": "~13.0.
|
|
11
|
-
"@simplysm/orm-node": "~13.0.
|
|
12
|
-
"@simplysm/service-server": "~13.0.
|
|
8
|
+
"@simplysm/core-common": "~13.0.96",
|
|
9
|
+
"@simplysm/excel": "~13.0.96",
|
|
10
|
+
"@simplysm/orm-common": "~13.0.96",
|
|
11
|
+
"@simplysm/orm-node": "~13.0.96",
|
|
12
|
+
"@simplysm/service-server": "~13.0.96",
|
|
13
13
|
"bcrypt": "^6.0.0",
|
|
14
14
|
"pg": "^8.20.0",
|
|
15
15
|
"pg-copy-streams": "^7.0.0"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
vi.mock("@simplysm/core-node", () => ({
|
|
4
|
+
fsx: {
|
|
5
|
+
readJson: vi.fn().mockResolvedValue({ name: "test", version: "1.0.0" }),
|
|
6
|
+
},
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock("consola", () => {
|
|
10
|
+
const withTag = () => ({ debug: vi.fn(), warn: vi.fn() });
|
|
11
|
+
const consola = { withTag };
|
|
12
|
+
return { default: consola, consola };
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
vi.mock("sharp", () => ({ default: vi.fn() }));
|
|
16
|
+
vi.mock("execa", () => ({ execa: vi.fn() }));
|
|
17
|
+
|
|
18
|
+
import { Capacitor } from "../src/capacitor/capacitor";
|
|
19
|
+
|
|
20
|
+
describe("Capacitor appName validation", () => {
|
|
21
|
+
const createConfig = (appName: string) => ({
|
|
22
|
+
appId: "com.test.app",
|
|
23
|
+
appName,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("한글 포함 앱 이름을 허용해야 한다", async () => {
|
|
27
|
+
await expect(
|
|
28
|
+
Capacitor.create("/tmp", createConfig("AD-TEK 기업솔루션")),
|
|
29
|
+
).resolves.toBeDefined();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("ASCII 앱 이름을 허용해야 한다", async () => {
|
|
33
|
+
await expect(
|
|
34
|
+
Capacitor.create("/tmp", createConfig("My App-1")),
|
|
35
|
+
).resolves.toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("파일명 위험 특수문자를 거부해야 한다", async () => {
|
|
39
|
+
await expect(
|
|
40
|
+
Capacitor.create("/tmp", createConfig("App<script>")),
|
|
41
|
+
).rejects.toThrow("invalid characters");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("빈 문자열을 거부해야 한다", async () => {
|
|
45
|
+
await expect(
|
|
46
|
+
Capacitor.create("/tmp", createConfig("")),
|
|
47
|
+
).rejects.toThrow("appName is required");
|
|
48
|
+
});
|
|
49
|
+
});
|