create-szyy-app 1.0.3 → 1.0.5
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 +20 -13
- package/dist/index.js +337 -173
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,12 +17,13 @@ npx create-szyy-app my-order-sys --yes --minimal
|
|
|
17
17
|
|
|
18
18
|
## 选项
|
|
19
19
|
|
|
20
|
-
| 选项
|
|
21
|
-
|
|
|
22
|
-
| `[project-name]`
|
|
23
|
-
| `-p, --port <port>`
|
|
24
|
-
| `--
|
|
25
|
-
|
|
|
20
|
+
| 选项 | 说明 |
|
|
21
|
+
| ---------------------- | ---------------------------------------------------- |
|
|
22
|
+
| `[project-name]` | 项目名称(kebab-case,小写字母开头) |
|
|
23
|
+
| `-p, --port <port>` | 联调端口,写入 `.env` / `.env.micro` 等(默认 5690) |
|
|
24
|
+
| `--template-tag <tag>` | 覆盖模板 Git tag |
|
|
25
|
+
| `--minimal` | 删除 `templateSys` Demo,仅保留首页菜单 |
|
|
26
|
+
| `-y, --yes` | 跳过交互,使用默认值 |
|
|
26
27
|
|
|
27
28
|
> **注意:** `pnpm dev` 独立模式端口固定 **3000**(`.env.development`),不受 `--port` 影响。`pnpm dev:micro` 使用 `--port` 配置的联调端口。
|
|
28
29
|
|
|
@@ -42,10 +43,15 @@ pnpm build:prod # 生产构建
|
|
|
42
43
|
| -------------------- | --------------------------------------------------------------------------------- |
|
|
43
44
|
| `SZYY_TEMPLATE_REPO` | 覆盖模板 Git 仓库 URL(默认 `http://172.1.1.65/RDKit/sunyard-szyy-app-template`) |
|
|
44
45
|
| `SZYY_TEMPLATE_PATH` | 使用本地目录作为模板(开发/联调 CLI 时使用) |
|
|
46
|
+
| `SZYY_TEMPLATE_TAG` | 覆盖模板 Git tag |
|
|
47
|
+
| `SZYY_VERSIONS_URL` | 覆盖远程版本配置 URL(默认从模板仓 `create-app` 分支拉取 `versions.json`) |
|
|
48
|
+
| `SZYY_VERSIONS_PATH` | 使用本地 `versions.json`(开发/联调 CLI 时使用) |
|
|
45
49
|
|
|
46
50
|
## 版本锁定
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
版本配置默认从模板仓 **`create-app` 分支** 拉取 `versions.json`(内网 Git 走 `git clone`,公网 GitLab 等先尝试 Raw URL);拉取失败时使用 npm 包内置配置兜底。更新模板 tag 或依赖版本时,只需修改模板仓 `create-app` 分支上的 `versions.json`,**无需重新发布 CLI**。
|
|
53
|
+
|
|
54
|
+
生成项目的 `package.json` 会写死精确版本号(无 `^`)。
|
|
49
55
|
|
|
50
56
|
## 开发 CLI
|
|
51
57
|
|
|
@@ -54,7 +60,7 @@ pnpm install
|
|
|
54
60
|
pnpm build
|
|
55
61
|
|
|
56
62
|
# 使用本地模板联调
|
|
57
|
-
SZYY_TEMPLATE_PATH=../sunyard-szyy-app-template node dist/index.js test-app --yes
|
|
63
|
+
SZYY_TEMPLATE_PATH=../sunyard-szyy-app-template SZYY_VERSIONS_PATH=../sunyard-szyy-app-template/versions.json node dist/index.js test-app --yes
|
|
58
64
|
```
|
|
59
65
|
|
|
60
66
|
## 发布
|
|
@@ -77,11 +83,12 @@ pnpm publish:pkg --tag=beta
|
|
|
77
83
|
|
|
78
84
|
## 故障排查
|
|
79
85
|
|
|
80
|
-
| 问题
|
|
81
|
-
|
|
|
82
|
-
| degit 拉取失败
|
|
83
|
-
| 占位符残留报错
|
|
84
|
-
|
|
|
86
|
+
| 问题 | 处理 |
|
|
87
|
+
| ---------------- | ------------------------------------------------------------------------------------------ |
|
|
88
|
+
| degit 拉取失败 | 检查内网 Git 连通性,或设置 `SZYY_TEMPLATE_PATH` 使用本地模板 |
|
|
89
|
+
| 占位符残留报错 | 确认模板 tag 与远程/本地 `versions.json` 中 `template` 版本一致 |
|
|
90
|
+
| 版本配置拉取失败 | 确认模板仓 `create-app` 分支存在 `versions.json` 且本机 Git 已登录;CLI 会自动回退内置配置 |
|
|
91
|
+
| 联调端口冲突 | 创建时指定 `--port`,或修改生成项目 `.env.micro` |
|
|
85
92
|
|
|
86
93
|
## 延伸阅读
|
|
87
94
|
|
package/dist/index.js
CHANGED
|
@@ -1,32 +1,133 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import fs4 from "fs";
|
|
5
|
+
import path5 from "path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import * as p2 from "@clack/prompts";
|
|
8
8
|
import pc2 from "picocolors";
|
|
9
9
|
|
|
10
10
|
// src/prompts.ts
|
|
11
|
-
import
|
|
11
|
+
import path from "path";
|
|
12
12
|
import * as p from "@clack/prompts";
|
|
13
13
|
import pc from "picocolors";
|
|
14
14
|
|
|
15
|
+
// src/validate.ts
|
|
16
|
+
function validateName(name) {
|
|
17
|
+
if (!name) return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
|
|
18
|
+
if (!/^[a-z][a-z0-9-_]*$/.test(name)) {
|
|
19
|
+
return "\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26(-)\u6216\u4E0B\u5212\u7EBF(_)\uFF0C\u4E14\u4EE5\u5B57\u6BCD\u5F00\u5934";
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function validatePort(port) {
|
|
24
|
+
const n = Number(port);
|
|
25
|
+
if (!port || isNaN(n) || !Number.isInteger(n) || n < 1024 || n > 65535) {
|
|
26
|
+
return "\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1024 ~ 65535 \u4E4B\u95F4\u7684\u6574\u6570";
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function toAppTitle(name) {
|
|
31
|
+
return name.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
32
|
+
}
|
|
33
|
+
function toAppNameUpper(name) {
|
|
34
|
+
return name.replace(/[-_]/g, "").toUpperCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/prompts.ts
|
|
38
|
+
async function collectOptions(projectNameArg, flags, versions) {
|
|
39
|
+
const cwd = process.cwd();
|
|
40
|
+
p.intro(`${pc.bgCyan(pc.black(" create-szyy-app "))} \u5FAE\u524D\u7AEF\u5B50\u5E94\u7528\u811A\u624B\u67B6`);
|
|
41
|
+
let appName = projectNameArg?.trim() || "";
|
|
42
|
+
if (!flags.yes || !appName) {
|
|
43
|
+
const nameResult = await p.text({
|
|
44
|
+
message: "\u9879\u76EE\u540D\u79F0",
|
|
45
|
+
placeholder: "my-order-sys",
|
|
46
|
+
initialValue: appName || void 0,
|
|
47
|
+
validate: (value) => validateName(value?.trim() || "") ?? void 0
|
|
48
|
+
});
|
|
49
|
+
if (p.isCancel(nameResult)) {
|
|
50
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
appName = nameResult.trim();
|
|
54
|
+
} else {
|
|
55
|
+
const nameError = validateName(appName);
|
|
56
|
+
if (nameError) {
|
|
57
|
+
p.log.error(nameError);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
let appPort = flags.port ?? versions.defaultPort;
|
|
62
|
+
if (!flags.yes || flags.port === void 0) {
|
|
63
|
+
const portResult = await p.text({
|
|
64
|
+
message: "\u672C\u5730\u8054\u8C03\u7AEF\u53E3",
|
|
65
|
+
placeholder: String(versions.defaultPort),
|
|
66
|
+
initialValue: String(appPort),
|
|
67
|
+
validate: (value) => {
|
|
68
|
+
const port = value?.trim() || String(versions.defaultPort);
|
|
69
|
+
return validatePort(port) ?? void 0;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
if (p.isCancel(portResult)) {
|
|
73
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
appPort = Number(portResult.trim() || versions.defaultPort);
|
|
77
|
+
} else {
|
|
78
|
+
const portError = validatePort(appPort);
|
|
79
|
+
if (portError) {
|
|
80
|
+
p.log.error(portError);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let keepDemo = !flags.minimal;
|
|
85
|
+
if (!flags.yes && flags.minimal === void 0) {
|
|
86
|
+
const demoResult = await p.confirm({
|
|
87
|
+
message: "\u662F\u5426\u4FDD\u7559 Demo \u9875",
|
|
88
|
+
initialValue: true
|
|
89
|
+
});
|
|
90
|
+
if (p.isCancel(demoResult)) {
|
|
91
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
keepDemo = demoResult;
|
|
95
|
+
}
|
|
96
|
+
const defaultTarget = path.resolve(cwd, appName);
|
|
97
|
+
let targetDir = defaultTarget;
|
|
98
|
+
if (!flags.yes) {
|
|
99
|
+
const dirResult = await p.text({
|
|
100
|
+
message: "\u76EE\u6807\u76EE\u5F55",
|
|
101
|
+
initialValue: defaultTarget,
|
|
102
|
+
validate: (value) => {
|
|
103
|
+
if (!value?.trim()) return "\u76EE\u6807\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A";
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
if (p.isCancel(dirResult)) {
|
|
108
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
targetDir = path.resolve(dirResult.trim());
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
appName,
|
|
115
|
+
appNameUpper: toAppNameUpper(appName),
|
|
116
|
+
appTitle: toAppTitle(appName),
|
|
117
|
+
appPort,
|
|
118
|
+
targetDir,
|
|
119
|
+
keepDemo
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
15
123
|
// src/replace.ts
|
|
16
124
|
import fs from "fs";
|
|
17
|
-
import
|
|
18
|
-
import { fileURLToPath } from "url";
|
|
19
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
20
|
-
var __dirname = path.dirname(__filename);
|
|
21
|
-
function loadVersions() {
|
|
22
|
-
const versionsPath = path.resolve(__dirname, "../templates/versions.json");
|
|
23
|
-
return JSON.parse(fs.readFileSync(versionsPath, "utf-8"));
|
|
24
|
-
}
|
|
125
|
+
import path2 from "path";
|
|
25
126
|
function stripRange(version) {
|
|
26
127
|
return version.replace(/^[\^~>=<]+/, "");
|
|
27
128
|
}
|
|
28
129
|
function pinDependencies(targetDir, versions) {
|
|
29
|
-
const pkgPath =
|
|
130
|
+
const pkgPath = path2.join(targetDir, "package.json");
|
|
30
131
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
31
132
|
for (const section of ["dependencies", "devDependencies"]) {
|
|
32
133
|
const deps = pkg[section];
|
|
@@ -65,13 +166,13 @@ var INCLUDE_EXTENSIONS = [
|
|
|
65
166
|
var INCLUDE_BASENAMES = [".env", ".env.development", ".env.production", ".env.micro", ".env.sit", ".gitignore"];
|
|
66
167
|
var PORT_SKIP_FILES = /* @__PURE__ */ new Set([".env.development"]);
|
|
67
168
|
function shouldSkip(filePath, rootDir) {
|
|
68
|
-
const rel =
|
|
69
|
-
return SKIP_PATTERNS.some((p3) => rel === p3 || rel.startsWith(`${p3}${
|
|
169
|
+
const rel = path2.relative(rootDir, filePath);
|
|
170
|
+
return SKIP_PATTERNS.some((p3) => rel === p3 || rel.startsWith(`${p3}${path2.sep}`) || rel.startsWith(`${p3}/`));
|
|
70
171
|
}
|
|
71
172
|
function shouldProcess(filePath) {
|
|
72
|
-
const base =
|
|
173
|
+
const base = path2.basename(filePath);
|
|
73
174
|
if (INCLUDE_BASENAMES.includes(base)) return true;
|
|
74
|
-
const ext =
|
|
175
|
+
const ext = path2.extname(filePath);
|
|
75
176
|
return !!ext && INCLUDE_EXTENSIONS.includes(ext);
|
|
76
177
|
}
|
|
77
178
|
function replaceInFile(filePath, replacements) {
|
|
@@ -97,14 +198,14 @@ function getReplacements(options) {
|
|
|
97
198
|
}
|
|
98
199
|
function walkReplace(dir, rootDir, options, count) {
|
|
99
200
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
100
|
-
const fullPath =
|
|
201
|
+
const fullPath = path2.join(dir, entry.name);
|
|
101
202
|
if (shouldSkip(fullPath, rootDir)) continue;
|
|
102
203
|
if (entry.isDirectory()) {
|
|
103
204
|
walkReplace(fullPath, rootDir, options, count);
|
|
104
205
|
continue;
|
|
105
206
|
}
|
|
106
207
|
if (!entry.isFile() || !shouldProcess(fullPath)) continue;
|
|
107
|
-
const fileBase =
|
|
208
|
+
const fileBase = path2.basename(fullPath);
|
|
108
209
|
const fileReplacements = getReplacements(options).filter(([from]) => {
|
|
109
210
|
if (from === "__APP_PORT__" && PORT_SKIP_FILES.has(fileBase)) return false;
|
|
110
211
|
return true;
|
|
@@ -118,14 +219,14 @@ function validateNoPlaceholders(targetDir) {
|
|
|
118
219
|
const leftovers = [];
|
|
119
220
|
function scan(dir) {
|
|
120
221
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
121
|
-
const fullPath =
|
|
222
|
+
const fullPath = path2.join(dir, entry.name);
|
|
122
223
|
if (shouldSkip(fullPath, targetDir)) continue;
|
|
123
224
|
if (entry.isDirectory()) {
|
|
124
225
|
scan(fullPath);
|
|
125
226
|
} else if (entry.isFile() && shouldProcess(fullPath)) {
|
|
126
227
|
const content = fs.readFileSync(fullPath, "utf-8");
|
|
127
228
|
if (content.includes("__APP_NAME__") || content.includes("__APP_NAME_UPPER__") || content.includes("__APP_PORT__") || content.includes("__APP_TITLE__")) {
|
|
128
|
-
leftovers.push(
|
|
229
|
+
leftovers.push(path2.relative(targetDir, fullPath));
|
|
129
230
|
}
|
|
130
231
|
}
|
|
131
232
|
}
|
|
@@ -137,7 +238,7 @@ ${leftovers.map((f) => ` - ${f}`).join("\n")}`);
|
|
|
137
238
|
}
|
|
138
239
|
}
|
|
139
240
|
function validateDevelopmentPort(targetDir) {
|
|
140
|
-
const devEnv =
|
|
241
|
+
const devEnv = path2.join(targetDir, ".env.development");
|
|
141
242
|
const content = fs.readFileSync(devEnv, "utf-8");
|
|
142
243
|
const match = content.match(/^\s*VITE_PORT\s*=\s*(\S+)/m);
|
|
143
244
|
if (!match || match[1] !== "3000") {
|
|
@@ -152,11 +253,11 @@ function applyReplacements(targetDir, options) {
|
|
|
152
253
|
return count.files;
|
|
153
254
|
}
|
|
154
255
|
function applyMinimalMode(targetDir, appName) {
|
|
155
|
-
const templateSysDir =
|
|
256
|
+
const templateSysDir = path2.join(targetDir, "src/views/templateSys");
|
|
156
257
|
if (fs.existsSync(templateSysDir)) {
|
|
157
258
|
fs.rmSync(templateSysDir, { recursive: true, force: true });
|
|
158
259
|
}
|
|
159
|
-
const menuPath =
|
|
260
|
+
const menuPath = path2.join(targetDir, "mock/authMenuList.ts");
|
|
160
261
|
fs.writeFileSync(
|
|
161
262
|
menuPath,
|
|
162
263
|
`export const authMenuList = [
|
|
@@ -181,7 +282,7 @@ function applyMinimalMode(targetDir, appName) {
|
|
|
181
282
|
`,
|
|
182
283
|
"utf-8"
|
|
183
284
|
);
|
|
184
|
-
const homePath =
|
|
285
|
+
const homePath = path2.join(targetDir, "src/views/home/index.vue");
|
|
185
286
|
fs.writeFileSync(
|
|
186
287
|
homePath,
|
|
187
288
|
`<template>
|
|
@@ -207,21 +308,21 @@ var GENERATED_CHANGELOG = `# \u53D1\u5E03\u8BB0\u5F55
|
|
|
207
308
|
> \u672C\u9879\u76EE\u6309\u7167\u81EA\u5B9A\u4E49\u63D0\u4EA4\u89C4\u8303\uFF08\u5982 fix: +\u63CF\u8FF0\uFF09\u81EA\u52A8\u751F\u6210\u4EE5\u4E0B\u53D8\u66F4\u8BB0\u5F55\u3002
|
|
208
309
|
`;
|
|
209
310
|
function cleanupTemplateArtifacts(targetDir) {
|
|
210
|
-
const cursorDir =
|
|
311
|
+
const cursorDir = path2.join(targetDir, ".cursor");
|
|
211
312
|
if (fs.existsSync(cursorDir)) {
|
|
212
313
|
fs.rmSync(cursorDir, { recursive: true, force: true });
|
|
213
314
|
}
|
|
214
|
-
const syncScript =
|
|
315
|
+
const syncScript = path2.join(targetDir, "build/sync-to-template.ts");
|
|
215
316
|
if (fs.existsSync(syncScript)) {
|
|
216
317
|
fs.rmSync(syncScript, { force: true });
|
|
217
318
|
}
|
|
218
|
-
fs.writeFileSync(
|
|
319
|
+
fs.writeFileSync(path2.join(targetDir, "CHANGELOG.md"), GENERATED_CHANGELOG, "utf-8");
|
|
219
320
|
cleanupGeneratedPackageJson(targetDir);
|
|
220
321
|
cleanupGeneratedReadme(targetDir);
|
|
221
322
|
cleanupGeneratedGitignore(targetDir);
|
|
222
323
|
}
|
|
223
324
|
function cleanupGeneratedPackageJson(targetDir) {
|
|
224
|
-
const pkgPath =
|
|
325
|
+
const pkgPath = path2.join(targetDir, "package.json");
|
|
225
326
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
226
327
|
if (pkg.scripts) {
|
|
227
328
|
delete pkg.scripts["sync:to-template"];
|
|
@@ -231,7 +332,7 @@ function cleanupGeneratedPackageJson(targetDir) {
|
|
|
231
332
|
`, "utf-8");
|
|
232
333
|
}
|
|
233
334
|
function cleanupGeneratedReadme(targetDir) {
|
|
234
|
-
const readmePath =
|
|
335
|
+
const readmePath = path2.join(targetDir, "README.md");
|
|
235
336
|
if (!fs.existsSync(readmePath)) return;
|
|
236
337
|
let content = fs.readFileSync(readmePath, "utf-8");
|
|
237
338
|
content = content.replace(/\n> \*\*说明:\*\*[^\n]*\n/, "\n");
|
|
@@ -239,7 +340,7 @@ function cleanupGeneratedReadme(targetDir) {
|
|
|
239
340
|
fs.writeFileSync(readmePath, content, "utf-8");
|
|
240
341
|
}
|
|
241
342
|
function cleanupGeneratedGitignore(targetDir) {
|
|
242
|
-
const gitignorePath =
|
|
343
|
+
const gitignorePath = path2.join(targetDir, ".gitignore");
|
|
243
344
|
if (!fs.existsSync(gitignorePath)) return;
|
|
244
345
|
let content = fs.readFileSync(gitignorePath, "utf-8");
|
|
245
346
|
content = content.replace(/\n# Cursor project metadata[\s\S]*?(?=\n*$)/, "\n");
|
|
@@ -250,121 +351,41 @@ function cleanupGeneratedGitignore(targetDir) {
|
|
|
250
351
|
fs.writeFileSync(gitignorePath, content.replace(/\n{3,}/g, "\n\n").replace(/\n+$/, "\n"), "utf-8");
|
|
251
352
|
}
|
|
252
353
|
|
|
253
|
-
// src/
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
354
|
+
// src/template.ts
|
|
355
|
+
import fs2 from "fs";
|
|
356
|
+
import path3 from "path";
|
|
357
|
+
import os from "os";
|
|
358
|
+
import degit from "degit";
|
|
359
|
+
|
|
360
|
+
// src/git.ts
|
|
361
|
+
import { spawnSync } from "child_process";
|
|
362
|
+
var PUBLIC_GIT_HOSTS = /* @__PURE__ */ new Set(["github.com", "gitlab.com", "bitbucket.org", "git.sr.ht"]);
|
|
363
|
+
function shouldUseGitCloneDirectly(source) {
|
|
364
|
+
if (source.startsWith("git@") || source.startsWith("file://")) return true;
|
|
365
|
+
if (source.startsWith(".") || source.startsWith("/") || /^[A-Za-z]:[\\/]/.test(source)) return true;
|
|
366
|
+
try {
|
|
367
|
+
const url = new URL(source);
|
|
368
|
+
return !PUBLIC_GIT_HOSTS.has(url.hostname.toLowerCase());
|
|
369
|
+
} catch {
|
|
370
|
+
return true;
|
|
258
371
|
}
|
|
259
|
-
return null;
|
|
260
372
|
}
|
|
261
|
-
function
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
373
|
+
function shallowCloneBranch(repo, branch, targetDir) {
|
|
374
|
+
const result = spawnSync("git", ["clone", "--depth", "1", "--branch", branch, repo, targetDir], {
|
|
375
|
+
encoding: "utf8"
|
|
376
|
+
});
|
|
377
|
+
if (result.status === 0) {
|
|
378
|
+
return;
|
|
265
379
|
}
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
function toAppTitle(name) {
|
|
269
|
-
return name.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
380
|
+
const errorOutput = result.stderr?.trim() || result.stdout?.trim() || "git clone \u6267\u884C\u5931\u8D25";
|
|
381
|
+
throw new Error(`Git \u62C9\u53D6\u5931\u8D25: ${errorOutput}`);
|
|
270
382
|
}
|
|
271
|
-
function
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// src/prompts.ts
|
|
276
|
-
async function collectOptions(projectNameArg, flags) {
|
|
277
|
-
const versions = loadVersions();
|
|
278
|
-
const cwd = process.cwd();
|
|
279
|
-
p.intro(`${pc.bgCyan(pc.black(" create-szyy-app "))} \u5FAE\u524D\u7AEF\u5B50\u5E94\u7528\u811A\u624B\u67B6`);
|
|
280
|
-
let appName = projectNameArg?.trim() || "";
|
|
281
|
-
if (!flags.yes || !appName) {
|
|
282
|
-
const nameResult = await p.text({
|
|
283
|
-
message: "\u9879\u76EE\u540D\u79F0",
|
|
284
|
-
placeholder: "my-order-sys",
|
|
285
|
-
initialValue: appName || void 0,
|
|
286
|
-
validate: (value) => validateName(value?.trim() || "") ?? void 0
|
|
287
|
-
});
|
|
288
|
-
if (p.isCancel(nameResult)) {
|
|
289
|
-
p.cancel("\u5DF2\u53D6\u6D88");
|
|
290
|
-
return null;
|
|
291
|
-
}
|
|
292
|
-
appName = nameResult.trim();
|
|
293
|
-
} else {
|
|
294
|
-
const nameError = validateName(appName);
|
|
295
|
-
if (nameError) {
|
|
296
|
-
p.log.error(nameError);
|
|
297
|
-
return null;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
let appPort = flags.port ?? versions.defaultPort;
|
|
301
|
-
if (!flags.yes || flags.port === void 0) {
|
|
302
|
-
const portResult = await p.text({
|
|
303
|
-
message: "\u672C\u5730\u8054\u8C03\u7AEF\u53E3",
|
|
304
|
-
placeholder: String(versions.defaultPort),
|
|
305
|
-
initialValue: String(appPort),
|
|
306
|
-
validate: (value) => {
|
|
307
|
-
const port = value?.trim() || String(versions.defaultPort);
|
|
308
|
-
return validatePort(port) ?? void 0;
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
if (p.isCancel(portResult)) {
|
|
312
|
-
p.cancel("\u5DF2\u53D6\u6D88");
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
appPort = Number(portResult.trim() || versions.defaultPort);
|
|
316
|
-
} else {
|
|
317
|
-
const portError = validatePort(appPort);
|
|
318
|
-
if (portError) {
|
|
319
|
-
p.log.error(portError);
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
let keepDemo = !flags.minimal;
|
|
324
|
-
if (!flags.yes && flags.minimal === void 0) {
|
|
325
|
-
const demoResult = await p.confirm({
|
|
326
|
-
message: "\u662F\u5426\u4FDD\u7559 Demo \u9875",
|
|
327
|
-
initialValue: true
|
|
328
|
-
});
|
|
329
|
-
if (p.isCancel(demoResult)) {
|
|
330
|
-
p.cancel("\u5DF2\u53D6\u6D88");
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
keepDemo = demoResult;
|
|
334
|
-
}
|
|
335
|
-
const defaultTarget = path2.resolve(cwd, appName);
|
|
336
|
-
let targetDir = defaultTarget;
|
|
337
|
-
if (!flags.yes) {
|
|
338
|
-
const dirResult = await p.text({
|
|
339
|
-
message: "\u76EE\u6807\u76EE\u5F55",
|
|
340
|
-
initialValue: defaultTarget,
|
|
341
|
-
validate: (value) => {
|
|
342
|
-
if (!value?.trim()) return "\u76EE\u6807\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A";
|
|
343
|
-
return void 0;
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
if (p.isCancel(dirResult)) {
|
|
347
|
-
p.cancel("\u5DF2\u53D6\u6D88");
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
|
-
targetDir = path2.resolve(dirResult.trim());
|
|
351
|
-
}
|
|
352
|
-
return {
|
|
353
|
-
appName,
|
|
354
|
-
appNameUpper: toAppNameUpper(appName),
|
|
355
|
-
appTitle: toAppTitle(appName),
|
|
356
|
-
appPort,
|
|
357
|
-
targetDir,
|
|
358
|
-
keepDemo
|
|
359
|
-
};
|
|
383
|
+
function shouldFallbackToGitClone(error) {
|
|
384
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
385
|
+
return message.includes("degit supports GitHub, GitLab, Sourcehut and BitBucket");
|
|
360
386
|
}
|
|
361
387
|
|
|
362
388
|
// src/template.ts
|
|
363
|
-
import fs2 from "fs";
|
|
364
|
-
import path3 from "path";
|
|
365
|
-
import os from "os";
|
|
366
|
-
import { spawnSync } from "child_process";
|
|
367
|
-
import degit from "degit";
|
|
368
389
|
async function fetchTemplate(targetDir, versions) {
|
|
369
390
|
const localPath = process.env.SZYY_TEMPLATE_PATH;
|
|
370
391
|
if (localPath) {
|
|
@@ -393,34 +414,15 @@ async function fetchTemplate(targetDir, versions) {
|
|
|
393
414
|
cloneWithGit(gitBase, versions.template, targetDir);
|
|
394
415
|
}
|
|
395
416
|
}
|
|
396
|
-
var DEGIT_HOSTS = /* @__PURE__ */ new Set(["github.com", "gitlab.com", "bitbucket.org", "git.sr.ht"]);
|
|
397
|
-
function shouldUseGitCloneDirectly(source) {
|
|
398
|
-
if (source.startsWith("git@") || source.startsWith("file://")) return true;
|
|
399
|
-
if (source.startsWith(".") || source.startsWith("/") || /^[A-Za-z]:[\\/]/.test(source)) return true;
|
|
400
|
-
try {
|
|
401
|
-
const url = new URL(source);
|
|
402
|
-
return !DEGIT_HOSTS.has(url.hostname.toLowerCase());
|
|
403
|
-
} catch {
|
|
404
|
-
return true;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
function shouldFallbackToGitClone(error) {
|
|
408
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
409
|
-
return message.includes("degit supports GitHub, GitLab, Sourcehut and BitBucket");
|
|
410
|
-
}
|
|
411
417
|
function cloneWithGit(repo, ref, targetDir) {
|
|
412
418
|
const tempDir = fs2.mkdtempSync(path3.join(os.tmpdir(), "create-szyy-app-"));
|
|
413
419
|
try {
|
|
414
420
|
const cloneDir = path3.join(tempDir, "template");
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const errorOutput = result.stderr?.trim() || result.stdout?.trim() || "git clone \u6267\u884C\u5931\u8D25";
|
|
423
|
-
throw new Error(`\u6A21\u677F\u62C9\u53D6\u5931\u8D25: ${errorOutput}`);
|
|
421
|
+
shallowCloneBranch(repo, ref, cloneDir);
|
|
422
|
+
copyRecursive(cloneDir, targetDir);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
425
|
+
throw new Error(`\u6A21\u677F\u62C9\u53D6\u5931\u8D25: ${message}`);
|
|
424
426
|
} finally {
|
|
425
427
|
fs2.rmSync(tempDir, { recursive: true, force: true });
|
|
426
428
|
}
|
|
@@ -459,9 +461,156 @@ function removeExcludedTemplateEntries(targetDir) {
|
|
|
459
461
|
}
|
|
460
462
|
}
|
|
461
463
|
|
|
464
|
+
// src/versions.ts
|
|
465
|
+
import fs3 from "fs";
|
|
466
|
+
import os2 from "os";
|
|
467
|
+
import path4 from "path";
|
|
468
|
+
import { fileURLToPath } from "url";
|
|
469
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
470
|
+
var __dirname2 = path4.dirname(__filename2);
|
|
471
|
+
var CONFIG_BRANCH = "create-app";
|
|
472
|
+
function loadBundledVersions() {
|
|
473
|
+
const versionsPath = path4.resolve(__dirname2, "../templates/versions.json");
|
|
474
|
+
return validateVersionsConfig(JSON.parse(fs3.readFileSync(versionsPath, "utf-8")));
|
|
475
|
+
}
|
|
476
|
+
function validateVersionsConfig(config) {
|
|
477
|
+
if (!config || typeof config !== "object") {
|
|
478
|
+
throw new Error("\u7248\u672C\u914D\u7F6E\u683C\u5F0F\u65E0\u6548");
|
|
479
|
+
}
|
|
480
|
+
const value = config;
|
|
481
|
+
if (typeof value.template !== "string" || !value.template.trim()) {
|
|
482
|
+
throw new Error("\u7248\u672C\u914D\u7F6E\u7F3A\u5C11 template");
|
|
483
|
+
}
|
|
484
|
+
if (typeof value.defaultPort !== "number" || !Number.isFinite(value.defaultPort)) {
|
|
485
|
+
throw new Error("\u7248\u672C\u914D\u7F6E\u7F3A\u5C11\u6709\u6548\u7684 defaultPort");
|
|
486
|
+
}
|
|
487
|
+
if (!value.dependencies || typeof value.dependencies !== "object" || Array.isArray(value.dependencies)) {
|
|
488
|
+
throw new Error("\u7248\u672C\u914D\u7F6E\u7F3A\u5C11 dependencies");
|
|
489
|
+
}
|
|
490
|
+
return config;
|
|
491
|
+
}
|
|
492
|
+
function mergeVersions(bundled, remote) {
|
|
493
|
+
return {
|
|
494
|
+
...bundled,
|
|
495
|
+
...remote,
|
|
496
|
+
dependencies: { ...bundled.dependencies, ...remote.dependencies },
|
|
497
|
+
compat: { ...bundled.compat, ...remote.compat }
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function getRepoBase(bundled) {
|
|
501
|
+
const override = process.env.SZYY_TEMPLATE_REPO;
|
|
502
|
+
if (override) {
|
|
503
|
+
return override.replace(/\.git$/, "").replace(/\/$/, "");
|
|
504
|
+
}
|
|
505
|
+
return `${bundled.templateGitBase}/${bundled.templateRepo}`.replace(/\/$/, "");
|
|
506
|
+
}
|
|
507
|
+
function getDefaultRemoteVersionsUrl(bundled) {
|
|
508
|
+
return `${getRepoBase(bundled)}/-/raw/${CONFIG_BRANCH}/versions.json`;
|
|
509
|
+
}
|
|
510
|
+
function getGitVersionsLabel(repoBase) {
|
|
511
|
+
return `${repoBase}#${CONFIG_BRANCH}:versions.json`;
|
|
512
|
+
}
|
|
513
|
+
async function fetchVersionsFromUrl(url) {
|
|
514
|
+
const response = await fetch(url, {
|
|
515
|
+
headers: { Accept: "application/json" },
|
|
516
|
+
signal: AbortSignal.timeout(15e3)
|
|
517
|
+
});
|
|
518
|
+
if (!response.ok) {
|
|
519
|
+
throw new Error(`\u62C9\u53D6\u7248\u672C\u914D\u7F6E\u5931\u8D25 (${response.status}): ${url}`);
|
|
520
|
+
}
|
|
521
|
+
return validateVersionsConfig(await response.json());
|
|
522
|
+
}
|
|
523
|
+
function fetchVersionsFromGit(repo, branch) {
|
|
524
|
+
const tempDir = fs3.mkdtempSync(path4.join(os2.tmpdir(), "create-szyy-app-versions-"));
|
|
525
|
+
try {
|
|
526
|
+
const cloneDir = path4.join(tempDir, "repo");
|
|
527
|
+
shallowCloneBranch(repo, branch, cloneDir);
|
|
528
|
+
const versionsPath = path4.join(cloneDir, "versions.json");
|
|
529
|
+
if (!fs3.existsSync(versionsPath)) {
|
|
530
|
+
throw new Error(`\u6A21\u677F\u4ED3 ${branch} \u5206\u652F\u7F3A\u5C11 versions.json`);
|
|
531
|
+
}
|
|
532
|
+
return validateVersionsConfig(JSON.parse(fs3.readFileSync(versionsPath, "utf-8")));
|
|
533
|
+
} finally {
|
|
534
|
+
fs3.rmSync(tempDir, { recursive: true, force: true });
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async function fetchRemoteVersions(bundled) {
|
|
538
|
+
const repoBase = getRepoBase(bundled);
|
|
539
|
+
const remoteUrl = process.env.SZYY_VERSIONS_URL || getDefaultRemoteVersionsUrl(bundled);
|
|
540
|
+
if (process.env.SZYY_VERSIONS_URL) {
|
|
541
|
+
return {
|
|
542
|
+
config: await fetchVersionsFromUrl(remoteUrl),
|
|
543
|
+
remoteUrl
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
if (shouldUseGitCloneDirectly(repoBase)) {
|
|
547
|
+
return {
|
|
548
|
+
config: fetchVersionsFromGit(repoBase, CONFIG_BRANCH),
|
|
549
|
+
remoteUrl: getGitVersionsLabel(repoBase)
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
try {
|
|
553
|
+
return {
|
|
554
|
+
config: await fetchVersionsFromUrl(remoteUrl),
|
|
555
|
+
remoteUrl
|
|
556
|
+
};
|
|
557
|
+
} catch (httpError) {
|
|
558
|
+
try {
|
|
559
|
+
return {
|
|
560
|
+
config: fetchVersionsFromGit(repoBase, CONFIG_BRANCH),
|
|
561
|
+
remoteUrl: getGitVersionsLabel(repoBase)
|
|
562
|
+
};
|
|
563
|
+
} catch (gitError) {
|
|
564
|
+
const httpMsg = httpError instanceof Error ? httpError.message : String(httpError);
|
|
565
|
+
const gitMsg = gitError instanceof Error ? gitError.message : String(gitError);
|
|
566
|
+
throw new Error(`${httpMsg}; Git \u56DE\u9000\u4E5F\u5931\u8D25: ${gitMsg}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function readVersionsFromPath(filePath) {
|
|
571
|
+
const resolved = path4.resolve(filePath);
|
|
572
|
+
if (!fs3.existsSync(resolved)) {
|
|
573
|
+
throw new Error(`\u7248\u672C\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${resolved}`);
|
|
574
|
+
}
|
|
575
|
+
return validateVersionsConfig(JSON.parse(fs3.readFileSync(resolved, "utf-8")));
|
|
576
|
+
}
|
|
577
|
+
function applyOverrides(versions, options) {
|
|
578
|
+
const templateTag = options?.templateTag?.trim() || process.env.SZYY_TEMPLATE_TAG?.trim();
|
|
579
|
+
if (!templateTag) {
|
|
580
|
+
return versions;
|
|
581
|
+
}
|
|
582
|
+
return { ...versions, template: templateTag };
|
|
583
|
+
}
|
|
584
|
+
async function resolveVersions(options) {
|
|
585
|
+
const bundled = loadBundledVersions();
|
|
586
|
+
if (process.env.SZYY_VERSIONS_PATH) {
|
|
587
|
+
return {
|
|
588
|
+
config: applyOverrides(readVersionsFromPath(process.env.SZYY_VERSIONS_PATH), options),
|
|
589
|
+
source: "path"
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
const { config: remote, remoteUrl } = await fetchRemoteVersions(bundled);
|
|
594
|
+
return {
|
|
595
|
+
config: applyOverrides(mergeVersions(bundled, remote), options),
|
|
596
|
+
source: "remote",
|
|
597
|
+
remoteUrl
|
|
598
|
+
};
|
|
599
|
+
} catch (error) {
|
|
600
|
+
const remoteUrl = process.env.SZYY_VERSIONS_URL || getDefaultRemoteVersionsUrl(bundled);
|
|
601
|
+
const fallbackReason = error instanceof Error ? error.message : String(error);
|
|
602
|
+
return {
|
|
603
|
+
config: applyOverrides(bundled, options),
|
|
604
|
+
source: "bundled",
|
|
605
|
+
remoteUrl,
|
|
606
|
+
fallbackReason
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
462
611
|
// src/index.ts
|
|
463
612
|
var program = new Command();
|
|
464
|
-
program.name("create-szyy-app").description("\u521B\u5EFA\u5FAE\u524D\u7AEF\u5B50\u5E94\u7528").argument("[project-name]", "\u9879\u76EE\u540D\u79F0\uFF08kebab-case\uFF09").option("-p, --port <port>", "\u672C\u5730\u8054\u8C03\u7AEF\u53E3", (value) => Number(value)).option("--minimal", "\u4E0D\u4FDD\u7559 Demo \u9875").option("-y, --yes", "\u8DF3\u8FC7\u4EA4\u4E92\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C").action(async (projectName, options) => {
|
|
613
|
+
program.name("create-szyy-app").description("\u521B\u5EFA\u5FAE\u524D\u7AEF\u5B50\u5E94\u7528").argument("[project-name]", "\u9879\u76EE\u540D\u79F0\uFF08kebab-case\uFF09").option("-p, --port <port>", "\u672C\u5730\u8054\u8C03\u7AEF\u53E3", (value) => Number(value)).option("--template-tag <tag>", "\u8986\u76D6\u6A21\u677F Git tag").option("--minimal", "\u4E0D\u4FDD\u7559 Demo \u9875").option("-y, --yes", "\u8DF3\u8FC7\u4EA4\u4E92\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C").action(async (projectName, options) => {
|
|
465
614
|
try {
|
|
466
615
|
await run(projectName, options);
|
|
467
616
|
} catch (error) {
|
|
@@ -470,12 +619,16 @@ program.name("create-szyy-app").description("\u521B\u5EFA\u5FAE\u524D\u7AEF\u5B5
|
|
|
470
619
|
}
|
|
471
620
|
});
|
|
472
621
|
async function run(projectName, flags) {
|
|
622
|
+
const spinner2 = p2.spinner();
|
|
623
|
+
spinner2.start("\u52A0\u8F7D\u7248\u672C\u914D\u7F6E...");
|
|
624
|
+
const resolved = await resolveVersions({ templateTag: flags.templateTag });
|
|
625
|
+
const { config: versions } = resolved;
|
|
626
|
+
spinner2.stop(formatVersionsMessage(resolved));
|
|
473
627
|
let createOptions;
|
|
474
628
|
if (flags.yes && projectName) {
|
|
475
629
|
const nameError = validateName(projectName);
|
|
476
630
|
if (nameError) throw new Error(nameError);
|
|
477
|
-
const
|
|
478
|
-
const port = flags.port ?? versions2.defaultPort;
|
|
631
|
+
const port = flags.port ?? versions.defaultPort;
|
|
479
632
|
const portError = validatePort(port);
|
|
480
633
|
if (portError) throw new Error(portError);
|
|
481
634
|
createOptions = {
|
|
@@ -483,25 +636,23 @@ async function run(projectName, flags) {
|
|
|
483
636
|
appNameUpper: toAppNameUpper(projectName),
|
|
484
637
|
appTitle: toAppTitle(projectName),
|
|
485
638
|
appPort: port,
|
|
486
|
-
targetDir:
|
|
639
|
+
targetDir: path5.resolve(process.cwd(), projectName),
|
|
487
640
|
keepDemo: !flags.minimal
|
|
488
641
|
};
|
|
489
642
|
} else {
|
|
490
|
-
createOptions = await collectOptions(projectName, flags);
|
|
643
|
+
createOptions = await collectOptions(projectName, flags, versions);
|
|
491
644
|
if (!createOptions) {
|
|
492
645
|
process.exit(0);
|
|
493
646
|
}
|
|
494
647
|
}
|
|
495
|
-
if (
|
|
496
|
-
const entries =
|
|
648
|
+
if (fs4.existsSync(createOptions.targetDir)) {
|
|
649
|
+
const entries = fs4.readdirSync(createOptions.targetDir);
|
|
497
650
|
if (entries.length > 0) {
|
|
498
651
|
throw new Error(`\u76EE\u6807\u76EE\u5F55\u975E\u7A7A: ${createOptions.targetDir}`);
|
|
499
652
|
}
|
|
500
653
|
} else {
|
|
501
|
-
|
|
654
|
+
fs4.mkdirSync(createOptions.targetDir, { recursive: true });
|
|
502
655
|
}
|
|
503
|
-
const versions = loadVersions();
|
|
504
|
-
const spinner2 = p2.spinner();
|
|
505
656
|
spinner2.start(`\u62C9\u53D6\u6A21\u677F ${versions.template}...`);
|
|
506
657
|
await fetchTemplate(createOptions.targetDir, versions);
|
|
507
658
|
spinner2.stop("\u6A21\u677F\u62C9\u53D6\u5B8C\u6210");
|
|
@@ -524,11 +675,24 @@ async function run(projectName, flags) {
|
|
|
524
675
|
`${pc2.green("\u2713")} \u9879\u76EE ${pc2.cyan(createOptions.appName)} \u521B\u5EFA\u6210\u529F`,
|
|
525
676
|
"",
|
|
526
677
|
"\u4E0B\u4E00\u6B65:",
|
|
527
|
-
` cd ${
|
|
678
|
+
` cd ${path5.relative(process.cwd(), createOptions.targetDir) || "."}`,
|
|
528
679
|
" pnpm install",
|
|
529
680
|
" pnpm dev # \u72EC\u7ACB\u6A21\u5F0F\uFF0C\u7AEF\u53E3 3000",
|
|
530
681
|
` pnpm dev:micro # \u57FA\u5EA7\u8054\u8C03\uFF0C\u7AEF\u53E3 ${createOptions.appPort}`
|
|
531
682
|
].join("\n")
|
|
532
683
|
);
|
|
533
684
|
}
|
|
685
|
+
function formatVersionsMessage(resolved) {
|
|
686
|
+
const { config, source, fallbackReason } = resolved;
|
|
687
|
+
if (source === "remote") {
|
|
688
|
+
return `\u7248\u672C\u914D\u7F6E\u5DF2\u52A0\u8F7D\uFF08\u8FDC\u7A0B\uFF0C\u6A21\u677F ${config.template}\uFF09`;
|
|
689
|
+
}
|
|
690
|
+
if (source === "path") {
|
|
691
|
+
return `\u7248\u672C\u914D\u7F6E\u5DF2\u52A0\u8F7D\uFF08\u672C\u5730\u6587\u4EF6\uFF0C\u6A21\u677F ${config.template}\uFF09`;
|
|
692
|
+
}
|
|
693
|
+
if (fallbackReason) {
|
|
694
|
+
p2.log.warn(`\u8FDC\u7A0B\u7248\u672C\u914D\u7F6E\u4E0D\u53EF\u7528\uFF0C\u5DF2\u4F7F\u7528\u5185\u7F6E\u914D\u7F6E: ${fallbackReason}`);
|
|
695
|
+
}
|
|
696
|
+
return `\u7248\u672C\u914D\u7F6E\u5DF2\u52A0\u8F7D\uFF08\u5185\u7F6E\u515C\u5E95\uFF0C\u6A21\u677F ${config.template}\uFF09`;
|
|
697
|
+
}
|
|
534
698
|
program.parse();
|