@ubean/cli 0.6.1-beta.2 → 0.6.1
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/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as readViteManifest, D as compareBundleBaseline, E as createFsOps, M as writeBundleBaseline, O as findClientManifest, S as renderTemplate, a as scaffold, c as scaffoldStarterTemplate, i as recoverScaffold, j as summarizeBundle, k as formatKilobytes, l as scaffoldUnifyTemplate, n as listScaffoldableFiles, o as scaffoldBlogTemplate, r as pageCommand, s as scaffoldMinimalTemplate, t as deleteScaffold } from "./page-
|
|
1
|
+
import { A as readViteManifest, D as compareBundleBaseline, E as createFsOps, M as writeBundleBaseline, O as findClientManifest, S as renderTemplate, a as scaffold, c as scaffoldStarterTemplate, i as recoverScaffold, j as summarizeBundle, k as formatKilobytes, l as scaffoldUnifyTemplate, n as listScaffoldableFiles, o as scaffoldBlogTemplate, r as pageCommand, s as scaffoldMinimalTemplate, t as deleteScaffold } from "./page-Fg1W_Mmr.js";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { basename, join, relative, resolve } from "pathe";
|
|
4
4
|
import { getLogger, setDebugLogging, setMinLevel } from "@ubean/shared/logger";
|
|
@@ -17,13 +17,68 @@ import { ubeanIslandsPlugin } from "@ubean/islands/vite";
|
|
|
17
17
|
import { findAvailablePort, findUserViteConfig, getLanAddresses, isLoopbackHost, isWildcardHost } from "@ubean/shared/node";
|
|
18
18
|
import { execSync } from "node:child_process";
|
|
19
19
|
import { createServer as createServer$1 } from "node:http";
|
|
20
|
+
//#region src/shared/exit.ts
|
|
21
|
+
/**
|
|
22
|
+
* CLI 的退出入口。
|
|
23
|
+
*
|
|
24
|
+
* ## 为什么不能直接 `process.exit(code)`
|
|
25
|
+
*
|
|
26
|
+
* `process.exit()` 在 Windows 上会在事件循环仍有活跃异步句柄时立刻开始拆进程,这会踩到 Node 24 的
|
|
27
|
+
* 一个竞态:ESM/TS loader 与未清理的 undici 连接在 close 阶段被并发释放,触发 libuv 断言,或者让
|
|
28
|
+
* 进程停在那里既不退出也不崩溃。
|
|
29
|
+
*
|
|
30
|
+
* 本仓库 CI 的真实症状(run 37954075109,windows-latest / Node 24):`ubean build` 已经
|
|
31
|
+
* 打印了致命错误、随后**既不退出也不崩溃**,spawn 侧只能等满看门狗(120s)才失败。日志:
|
|
32
|
+
*
|
|
33
|
+
* ```text
|
|
34
|
+
* Error: 构建未在 120000ms 内退出(挂住即为回归)
|
|
35
|
+
* fixture: D:\a\ubean\ubean\packages\cli\test\fixtures\build-errors\config-not-constructible
|
|
36
|
+
* 输出尾巴:
|
|
37
|
+
* Building ubean application...
|
|
38
|
+
* Class constructor Foo cannot be invoked without 'new'
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* 上游同类报告与各自的绕过方式:
|
|
42
|
+
* - nodejs/node#56645 —— `Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)`,仅在
|
|
43
|
+
* Windows + `process.exit()` 紧跟异步工作后出现。#61999 修的是断言面(已随 Node 24.20.0 进入
|
|
44
|
+
* LTS),并没有修「不退出」这一面。
|
|
45
|
+
* - pnpm/pnpm#12121 —— 退出前先销毁自己的 undici dispatcher。
|
|
46
|
+
* - silverwind/updates#137 —— **不再调用 `process.exit()`**,改用 `process.exitCode` 让事件循环
|
|
47
|
+
* 自然排空(其作者在 Node 26 上发现 50ms 延时仍不彻底)。
|
|
48
|
+
*
|
|
49
|
+
* ## 采用的策略
|
|
50
|
+
*
|
|
51
|
+
* 先设 `process.exitCode`(立刻生效,即使调用方不 await,退出码也是对的),然后用一个**不保活**的
|
|
52
|
+
* 定时器给出 `EXIT_GRACE_MS` 宽限期等进程自然退出;只有宽限期用尽、进程仍活着时才回退到
|
|
53
|
+
* `process.exit()`:
|
|
54
|
+
* - 间歇故障路径(Windows):进程自然排空,绕开竞态;
|
|
55
|
+
* - 真挂住(tinypool worker 池、prerender 动态导入的 SSR entry):最多多等 `EXIT_GRACE_MS`,
|
|
56
|
+
* 之后仍由强制退出兜底 —— 与旧实现行为一致,只是晚 1.5s。
|
|
57
|
+
*
|
|
58
|
+
* 定时器必须 `unref()`:否则宽限期本身会把进程钉活满 1.5s,成功路径白白变慢。
|
|
59
|
+
*/
|
|
60
|
+
/** 自然退出的宽限期(ms)。超时才回退到强制退出。 */
|
|
61
|
+
const EXIT_GRACE_MS = 1500;
|
|
62
|
+
/**
|
|
63
|
+
* 退出 CLI。**不返回**:要么进程在宽限期内自然退出,要么宽限期后被强制退出。
|
|
64
|
+
*
|
|
65
|
+
* @param code 退出码
|
|
66
|
+
*/
|
|
67
|
+
async function exitCli(code) {
|
|
68
|
+
process.exitCode = code;
|
|
69
|
+
await new Promise((resolve) => {
|
|
70
|
+
setTimeout(resolve, EXIT_GRACE_MS).unref?.();
|
|
71
|
+
});
|
|
72
|
+
process.exit(code);
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
20
75
|
//#region src/config.ts
|
|
21
76
|
const logger$11 = getLogger("cli");
|
|
22
77
|
const CONFIG_TEMPLATE = `import { defineConfig } from 'ubean';
|
|
23
78
|
|
|
24
79
|
export default defineConfig({
|
|
25
80
|
srcDir: 'src',
|
|
26
|
-
preset: '{{preset}}'
|
|
81
|
+
build: { preset: '{{preset}}' }
|
|
27
82
|
});
|
|
28
83
|
`;
|
|
29
84
|
const CONFIG_EXAMPLE_FULL = `import { defineConfig } from 'ubean';
|
|
@@ -32,27 +87,20 @@ export default defineConfig({
|
|
|
32
87
|
// Source directory
|
|
33
88
|
srcDir: 'src',
|
|
34
89
|
|
|
35
|
-
//
|
|
36
|
-
|
|
90
|
+
// Build options (platform preset lives here — a top-level \`preset\` field
|
|
91
|
+
// is silently ignored, so always nest it under \`build\`).
|
|
92
|
+
build: {
|
|
93
|
+
preset: 'standard', // 'standard' | 'node' | 'cloudflare' | 'vercel' | 'netlify' | 'bun' | 'deno' | 'aws' | 'azure'
|
|
94
|
+
outputDir: 'dist'
|
|
95
|
+
},
|
|
37
96
|
|
|
38
97
|
// Dev server options
|
|
39
98
|
dev: {
|
|
40
99
|
port: 9527,
|
|
41
|
-
host: 'localhost'
|
|
42
|
-
open: false
|
|
100
|
+
host: 'localhost'
|
|
43
101
|
},
|
|
44
102
|
|
|
45
|
-
//
|
|
46
|
-
build: {
|
|
47
|
-
outDir: 'dist'
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
// View transitions
|
|
51
|
-
viewTransitions: {
|
|
52
|
-
enabled: true
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
// DevTools
|
|
103
|
+
// DevTools (disabled by default)
|
|
56
104
|
devtools: {
|
|
57
105
|
enabled: true
|
|
58
106
|
}
|
|
@@ -81,7 +129,7 @@ const configCommand = {
|
|
|
81
129
|
args: {
|
|
82
130
|
preset: {
|
|
83
131
|
type: "string",
|
|
84
|
-
description: "Preset to use (standard, node, cloudflare)",
|
|
132
|
+
description: "Preset to use (standard, node, cloudflare, vercel, netlify, bun, deno, aws, azure, ...)",
|
|
85
133
|
default: "standard"
|
|
86
134
|
},
|
|
87
135
|
force: {
|
|
@@ -152,10 +200,7 @@ const configCommand = {
|
|
|
152
200
|
const cwd = process.cwd();
|
|
153
201
|
const fs = createFsOps(cwd);
|
|
154
202
|
const configFile = await findConfigFile(fs);
|
|
155
|
-
if (!configFile)
|
|
156
|
-
logger$11.error("No config file found");
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
203
|
+
if (!configFile) return exitCli(1);
|
|
159
204
|
logger$11.info(fs.resolve(configFile));
|
|
160
205
|
}
|
|
161
206
|
}
|
|
@@ -437,9 +482,9 @@ const buildCommand = {
|
|
|
437
482
|
logger$9.info(` Client assets: ${manifest.assets.length} files`);
|
|
438
483
|
} catch (err) {
|
|
439
484
|
logger$9.error(err instanceof Error ? err.message : String(err));
|
|
440
|
-
|
|
485
|
+
await exitCli(1);
|
|
441
486
|
}
|
|
442
|
-
|
|
487
|
+
await exitCli(0);
|
|
443
488
|
}
|
|
444
489
|
};
|
|
445
490
|
//#endregion
|
|
@@ -790,12 +835,12 @@ const devCommand = {
|
|
|
790
835
|
});
|
|
791
836
|
} catch (err) {
|
|
792
837
|
logger$6.error(err?.stack || err?.message || String(err));
|
|
793
|
-
|
|
838
|
+
await exitCli(1);
|
|
794
839
|
}
|
|
795
840
|
const cleanup = async () => {
|
|
796
841
|
if (logging.lifecycle) logger$6.info("\nShutting down...");
|
|
797
842
|
await devServer?.stop();
|
|
798
|
-
|
|
843
|
+
await exitCli(0);
|
|
799
844
|
};
|
|
800
845
|
process.on("SIGINT", cleanup);
|
|
801
846
|
process.on("SIGTERM", cleanup);
|
|
@@ -1297,7 +1342,7 @@ const initCommand = {
|
|
|
1297
1342
|
},
|
|
1298
1343
|
preset: {
|
|
1299
1344
|
type: "string",
|
|
1300
|
-
description: "Target preset (standard/node/cloudflare)"
|
|
1345
|
+
description: "Target preset (any registered preset; the interactive menu offers standard/node/cloudflare)"
|
|
1301
1346
|
},
|
|
1302
1347
|
packageManager: {
|
|
1303
1348
|
type: "string",
|
|
@@ -1630,7 +1675,7 @@ const previewCommand = {
|
|
|
1630
1675
|
} catch (err) {
|
|
1631
1676
|
if (err?.code === "EADDRINUSE") logger$1.error(`Port ${requestedPort} is already in use${host ? ` on ${host}` : ""}. Try a different port or remove the --strictPort flag.`);
|
|
1632
1677
|
else logger$1.error(`Failed to resolve a port: ${err?.message || String(err)}`);
|
|
1633
|
-
|
|
1678
|
+
return exitCli(1);
|
|
1634
1679
|
}
|
|
1635
1680
|
if (actualPort !== requestedPort) logger$1.warn(`Port ${requestedPort} is in use, trying ${actualPort} instead.`);
|
|
1636
1681
|
const label = (text) => dim(text);
|
|
@@ -1645,13 +1690,13 @@ const previewCommand = {
|
|
|
1645
1690
|
if (!existsSync(requiredArtifact)) {
|
|
1646
1691
|
logger$1.error(`Build output not found: ${requiredArtifact}`);
|
|
1647
1692
|
logger$1.info("Run `ubean build` first to create a production build.");
|
|
1648
|
-
|
|
1693
|
+
return exitCli(1);
|
|
1649
1694
|
}
|
|
1650
1695
|
if (isCloudflare) {
|
|
1651
1696
|
const probe = await createCloudflarePreviewRunner({ workerPath: requiredArtifact });
|
|
1652
1697
|
if (!probe.ok) {
|
|
1653
1698
|
logger$1.error(probe.message);
|
|
1654
|
-
|
|
1699
|
+
return exitCli(1);
|
|
1655
1700
|
}
|
|
1656
1701
|
await probe.runner.dispose();
|
|
1657
1702
|
}
|
|
@@ -1676,7 +1721,7 @@ const previewCommand = {
|
|
|
1676
1721
|
} catch (err) {
|
|
1677
1722
|
if (!staticMode) {
|
|
1678
1723
|
logger$1.error(`Failed to start the preview server: ${err instanceof Error ? err.message : String(err)}`);
|
|
1679
|
-
|
|
1724
|
+
return exitCli(1);
|
|
1680
1725
|
}
|
|
1681
1726
|
logger$1.warn(`Vite preview unavailable (${err instanceof Error ? err.message : String(err)}); falling back to the built-in static server.`);
|
|
1682
1727
|
const fallback = startStaticServer({
|
|
@@ -1687,8 +1732,8 @@ const previewCommand = {
|
|
|
1687
1732
|
});
|
|
1688
1733
|
printBanner(actualPort, `static (${mode})`);
|
|
1689
1734
|
const cleanupFallback = () => {
|
|
1690
|
-
fallback.close(() =>
|
|
1691
|
-
setTimeout(() =>
|
|
1735
|
+
fallback.close(() => void exitCli(0));
|
|
1736
|
+
setTimeout(() => void exitCli(0), 1e3).unref();
|
|
1692
1737
|
};
|
|
1693
1738
|
process.on("SIGINT", cleanupFallback);
|
|
1694
1739
|
process.on("SIGTERM", cleanupFallback);
|
|
@@ -1696,8 +1741,8 @@ const previewCommand = {
|
|
|
1696
1741
|
}
|
|
1697
1742
|
printBanner(actualPort, staticMode ? `static (${mode})` : "production");
|
|
1698
1743
|
const cleanup = () => {
|
|
1699
|
-
server?.close().catch(() => void 0).finally(() =>
|
|
1700
|
-
setTimeout(() =>
|
|
1744
|
+
server?.close().catch(() => void 0).finally(() => void exitCli(0));
|
|
1745
|
+
setTimeout(() => void exitCli(0), 1e3).unref();
|
|
1701
1746
|
};
|
|
1702
1747
|
process.on("SIGINT", cleanup);
|
|
1703
1748
|
process.on("SIGTERM", cleanup);
|
|
@@ -1919,7 +1964,7 @@ const main = defineCommand({
|
|
|
1919
1964
|
description: "Print the scaffold JSON manifest (RM-S01)"
|
|
1920
1965
|
},
|
|
1921
1966
|
async run() {
|
|
1922
|
-
const { getScaffoldManifest } = await import("./scaffold-manifest-
|
|
1967
|
+
const { getScaffoldManifest } = await import("./scaffold-manifest-DzxRu2yz.js").then((n) => n.r);
|
|
1923
1968
|
process.stdout.write(`${JSON.stringify(getScaffoldManifest(), null, 2)}\n`);
|
|
1924
1969
|
}
|
|
1925
1970
|
} }
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as getScaffoldManifest, t as SCAFFOLD_CONTRACT_VERSION } from "./scaffold-manifest-
|
|
2
|
-
import { A as readViteManifest, C as toCamelCase, D as compareBundleBaseline, E as createFsOps, M as writeBundleBaseline, O as findClientManifest, S as renderTemplate, T as toPascalCase, _ as renderCronTemplate, a as scaffold, b as renderPageTemplate, d as CRON_TEMPLATE, f as LAYOUT_TEMPLATE, g as renderApiTemplate, h as PLUGIN_TEMPLATE, i as recoverScaffold, j as summarizeBundle, m as PAGE_TEMPLATE, n as listScaffoldableFiles, p as MIDDLEWARE_TEMPLATE, t as deleteScaffold, u as API_TEMPLATE, v as renderLayoutTemplate, w as toKebabCase, x as renderPluginTemplate, y as renderMiddlewareTemplate } from "./page-
|
|
1
|
+
import { n as getScaffoldManifest, t as SCAFFOLD_CONTRACT_VERSION } from "./scaffold-manifest-DzxRu2yz.js";
|
|
2
|
+
import { A as readViteManifest, C as toCamelCase, D as compareBundleBaseline, E as createFsOps, M as writeBundleBaseline, O as findClientManifest, S as renderTemplate, T as toPascalCase, _ as renderCronTemplate, a as scaffold, b as renderPageTemplate, d as CRON_TEMPLATE, f as LAYOUT_TEMPLATE, g as renderApiTemplate, h as PLUGIN_TEMPLATE, i as recoverScaffold, j as summarizeBundle, m as PAGE_TEMPLATE, n as listScaffoldableFiles, p as MIDDLEWARE_TEMPLATE, t as deleteScaffold, u as API_TEMPLATE, v as renderLayoutTemplate, w as toKebabCase, x as renderPluginTemplate, y as renderMiddlewareTemplate } from "./page-Fg1W_Mmr.js";
|
|
3
3
|
export { API_TEMPLATE, CRON_TEMPLATE, LAYOUT_TEMPLATE, MIDDLEWARE_TEMPLATE, PAGE_TEMPLATE, PLUGIN_TEMPLATE, SCAFFOLD_CONTRACT_VERSION, compareBundleBaseline, createFsOps, deleteScaffold, findClientManifest, getScaffoldManifest, listScaffoldableFiles, readViteManifest, recoverScaffold, renderApiTemplate, renderCronTemplate, renderLayoutTemplate, renderMiddlewareTemplate, renderPageTemplate, renderPluginTemplate, renderTemplate, scaffold, summarizeBundle, toCamelCase, toKebabCase, toPascalCase, writeBundleBaseline };
|
|
@@ -464,7 +464,7 @@ const UBEAN_CONFIG = `import { defineConfig } from 'ubean';
|
|
|
464
464
|
|
|
465
465
|
export default defineConfig({
|
|
466
466
|
srcDir: 'src',
|
|
467
|
-
preset: '{{preset}}',
|
|
467
|
+
build: { preset: '{{preset}}' },
|
|
468
468
|
i18n: {
|
|
469
469
|
defaultLocale: 'zh',
|
|
470
470
|
locales: ['en', 'zh'],
|
|
@@ -1159,7 +1159,7 @@ const UBEAN_CONFIG_BASE = `import { defineConfig } from 'ubean';
|
|
|
1159
1159
|
|
|
1160
1160
|
export default defineConfig({
|
|
1161
1161
|
srcDir: 'src',
|
|
1162
|
-
preset: '{{preset}}'
|
|
1162
|
+
build: { preset: '{{preset}}' }
|
|
1163
1163
|
});
|
|
1164
1164
|
`;
|
|
1165
1165
|
/** 基础 README — minimal/starter/blog 共用 */
|
|
@@ -1391,7 +1391,7 @@ function getDirForType(cwd, type) {
|
|
|
1391
1391
|
case "api": return join(srcDir, "routes");
|
|
1392
1392
|
case "layout": return join(srcDir, "layouts");
|
|
1393
1393
|
case "middleware": return join(srcDir, "middleware");
|
|
1394
|
-
case "cron": return join(srcDir, "
|
|
1394
|
+
case "cron": return join(srcDir, "crons");
|
|
1395
1395
|
case "plugin": return join(srcDir, "plugins");
|
|
1396
1396
|
default: return srcDir;
|
|
1397
1397
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubean/cli",
|
|
3
|
-
"version": "0.6.1
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "CLI for ubean (ubean-next binary: dev, build, prepare, init, preview, page, env, config, devtools, api/layout/middleware/cron/plugin scaffolds) plus the Vite dev server runner",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ubean-next": "./bin/ubean-next.mjs"
|
|
@@ -24,27 +24,27 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@ubean/build": "0.6.1
|
|
28
|
-
"@ubean/config": "0.6.1
|
|
29
|
-
"@ubean/i18n": "0.6.1
|
|
30
|
-
"@ubean/islands": "0.6.1
|
|
31
|
-
"@ubean/preset": "0.6.1
|
|
32
|
-
"@ubean/scan": "0.6.1
|
|
33
|
-
"@ubean/shared": "0.6.1
|
|
27
|
+
"@ubean/build": "0.6.1",
|
|
28
|
+
"@ubean/config": "0.6.1",
|
|
29
|
+
"@ubean/i18n": "0.6.1",
|
|
30
|
+
"@ubean/islands": "0.6.1",
|
|
31
|
+
"@ubean/preset": "0.6.1",
|
|
32
|
+
"@ubean/scan": "0.6.1",
|
|
33
|
+
"@ubean/shared": "0.6.1",
|
|
34
34
|
"citty": "^0.2.2",
|
|
35
35
|
"kolorist": "^1.8.0",
|
|
36
36
|
"pathe": "^2.0.3"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@types/node": "^26.6.
|
|
39
|
+
"@types/node": "^26.6.5",
|
|
40
40
|
"env-runner": "0.3.3",
|
|
41
41
|
"typescript": "npm:typescript-native-bridge@latest",
|
|
42
42
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
43
43
|
"vite-plus": "1.1.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@ubean/content": "0.6.1
|
|
47
|
-
"@ubean/devtools": "0.6.1
|
|
46
|
+
"@ubean/content": "0.6.1",
|
|
47
|
+
"@ubean/devtools": "0.6.1",
|
|
48
48
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
49
49
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0"
|
|
50
50
|
},
|