@lambertkeith/spec-go 0.2.5 → 0.3.0
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 +60 -0
- package/dist/{index.js → cli/index.js} +418 -387
- package/dist/cli/index.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +591 -0
- package/dist/mcp/index.js.map +1 -0
- package/package.json +8 -4
- package/dist/index.js.map +0 -1
- /package/dist/{index.d.ts → cli/index.d.ts} +0 -0
|
@@ -6,27 +6,75 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
6
6
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
// src/cli.ts
|
|
9
|
+
// src/cli/cli.ts
|
|
10
10
|
import path7 from "path";
|
|
11
11
|
import fs5 from "fs";
|
|
12
12
|
import { Command } from "commander";
|
|
13
|
-
import
|
|
13
|
+
import pc5 from "picocolors";
|
|
14
14
|
|
|
15
15
|
// package.json
|
|
16
|
-
var version = "0.
|
|
16
|
+
var version = "0.3.0";
|
|
17
17
|
|
|
18
|
-
// src/prompts.ts
|
|
19
|
-
import
|
|
20
|
-
import
|
|
18
|
+
// src/cli/prompts.ts
|
|
19
|
+
import path5 from "path";
|
|
20
|
+
import fs3 from "fs";
|
|
21
21
|
import { input, select, confirm, Separator } from "@inquirer/prompts";
|
|
22
22
|
import pc from "picocolors";
|
|
23
23
|
|
|
24
|
-
// src/
|
|
24
|
+
// src/core/types.ts
|
|
25
|
+
var silentLogger = {
|
|
26
|
+
info: () => {
|
|
27
|
+
},
|
|
28
|
+
success: () => {
|
|
29
|
+
},
|
|
30
|
+
warn: () => {
|
|
31
|
+
},
|
|
32
|
+
error: () => {
|
|
33
|
+
},
|
|
34
|
+
dim: () => {
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/core/config.ts
|
|
39
|
+
import fs from "fs";
|
|
25
40
|
import path from "path";
|
|
26
|
-
import
|
|
27
|
-
|
|
41
|
+
import os from "os";
|
|
42
|
+
var CONFIG_PATH = path.join(os.homedir(), ".spec-go.json");
|
|
43
|
+
var DEFAULT_CONFIG = {
|
|
44
|
+
defaults: {
|
|
45
|
+
github: false,
|
|
46
|
+
public: false,
|
|
47
|
+
template: ""
|
|
48
|
+
},
|
|
49
|
+
github: {
|
|
50
|
+
token: "",
|
|
51
|
+
defaultOrg: ""
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
function ensureConfigExists() {
|
|
55
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
56
|
+
saveConfig(DEFAULT_CONFIG);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function loadConfig() {
|
|
60
|
+
try {
|
|
61
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
62
|
+
const content = fs.readFileSync(CONFIG_PATH, "utf-8");
|
|
63
|
+
return JSON.parse(content);
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
function saveConfig(config) {
|
|
70
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
71
|
+
fs.chmodSync(CONFIG_PATH, 384);
|
|
72
|
+
}
|
|
73
|
+
function getConfigPath() {
|
|
74
|
+
return CONFIG_PATH;
|
|
75
|
+
}
|
|
28
76
|
|
|
29
|
-
// src/debug.ts
|
|
77
|
+
// src/core/debug.ts
|
|
30
78
|
var debugEnabled = false;
|
|
31
79
|
function setDebugEnabled(enabled) {
|
|
32
80
|
debugEnabled = enabled;
|
|
@@ -57,11 +105,39 @@ function formatData(data) {
|
|
|
57
105
|
}
|
|
58
106
|
}
|
|
59
107
|
|
|
60
|
-
// src/
|
|
108
|
+
// src/core/exit-codes.ts
|
|
109
|
+
var ExitCodes = {
|
|
110
|
+
/** 成功完成 */
|
|
111
|
+
SUCCESS: 0,
|
|
112
|
+
/** 用户错误:参数错误、输入无效 */
|
|
113
|
+
USER_ERROR: 1,
|
|
114
|
+
/** 外部错误:网络、GitHub API、包管理器 */
|
|
115
|
+
EXTERNAL_ERROR: 2,
|
|
116
|
+
/** 未知内部错误 */
|
|
117
|
+
INTERNAL_ERROR: 10,
|
|
118
|
+
/** 模板相关错误 */
|
|
119
|
+
TEMPLATE_ERROR: 11,
|
|
120
|
+
/** 文件系统错误 */
|
|
121
|
+
FILE_SYSTEM_ERROR: 12,
|
|
122
|
+
/** Git 操作错误 */
|
|
123
|
+
GIT_ERROR: 13
|
|
124
|
+
};
|
|
125
|
+
var CliError = class extends Error {
|
|
126
|
+
constructor(message, exitCode = ExitCodes.INTERNAL_ERROR) {
|
|
127
|
+
super(message);
|
|
128
|
+
this.exitCode = exitCode;
|
|
129
|
+
this.name = "CliError";
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/core/utils.ts
|
|
134
|
+
import path2 from "path";
|
|
135
|
+
import { fileURLToPath } from "url";
|
|
136
|
+
import { spawn } from "child_process";
|
|
61
137
|
var __filename = fileURLToPath(import.meta.url);
|
|
62
|
-
var __dirname =
|
|
138
|
+
var __dirname = path2.dirname(__filename);
|
|
63
139
|
function getTemplatesDir() {
|
|
64
|
-
return
|
|
140
|
+
return path2.resolve(__dirname, "..", "..", "templates");
|
|
65
141
|
}
|
|
66
142
|
function isValidPackageName(name) {
|
|
67
143
|
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name);
|
|
@@ -108,157 +184,7 @@ function execAsync(command, args, options = {}) {
|
|
|
108
184
|
});
|
|
109
185
|
}
|
|
110
186
|
|
|
111
|
-
// src/
|
|
112
|
-
var ExitCodes = {
|
|
113
|
-
/** 成功完成 */
|
|
114
|
-
SUCCESS: 0,
|
|
115
|
-
/** 用户错误:参数错误、输入无效 */
|
|
116
|
-
USER_ERROR: 1,
|
|
117
|
-
/** 外部错误:网络、GitHub API、包管理器 */
|
|
118
|
-
EXTERNAL_ERROR: 2,
|
|
119
|
-
/** 未知内部错误 */
|
|
120
|
-
INTERNAL_ERROR: 10,
|
|
121
|
-
/** 模板相关错误 */
|
|
122
|
-
TEMPLATE_ERROR: 11,
|
|
123
|
-
/** 文件系统错误 */
|
|
124
|
-
FILE_SYSTEM_ERROR: 12,
|
|
125
|
-
/** Git 操作错误 */
|
|
126
|
-
GIT_ERROR: 13
|
|
127
|
-
};
|
|
128
|
-
var CliError = class extends Error {
|
|
129
|
-
constructor(message, exitCode = ExitCodes.INTERNAL_ERROR) {
|
|
130
|
-
super(message);
|
|
131
|
-
this.exitCode = exitCode;
|
|
132
|
-
this.name = "CliError";
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// src/prompts.ts
|
|
137
|
-
var DEFAULT_TEMPLATE = "node-ts";
|
|
138
|
-
async function runPrompts(argProjectName, options, userConfig = {}, interactive = true) {
|
|
139
|
-
const defaults = userConfig.defaults ?? {};
|
|
140
|
-
const templatesDir = getTemplatesDir();
|
|
141
|
-
const registryPath = path2.join(templatesDir, "template.config.json");
|
|
142
|
-
const registry = JSON.parse(
|
|
143
|
-
fs.readFileSync(registryPath, "utf-8")
|
|
144
|
-
);
|
|
145
|
-
debug("prompts", `\u4EA4\u4E92\u6A21\u5F0F: ${interactive}`);
|
|
146
|
-
debug("prompts", `CLI \u53C2\u6570`, { argProjectName, options });
|
|
147
|
-
let projectName = argProjectName;
|
|
148
|
-
if (!projectName) {
|
|
149
|
-
if (!interactive) {
|
|
150
|
-
console.log(pc.red("\u9519\u8BEF: \u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0"));
|
|
151
|
-
throw new CliError("\u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0", ExitCodes.USER_ERROR);
|
|
152
|
-
}
|
|
153
|
-
projectName = await input({
|
|
154
|
-
message: "\u9879\u76EE\u540D\u79F0:",
|
|
155
|
-
default: "my-project",
|
|
156
|
-
validate: (value) => {
|
|
157
|
-
if (!value.trim()) {
|
|
158
|
-
return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
|
|
159
|
-
}
|
|
160
|
-
if (!isValidPackageName(toValidPackageName(value))) {
|
|
161
|
-
return "\u65E0\u6548\u7684\u9879\u76EE\u540D\u79F0";
|
|
162
|
-
}
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
const validName = toValidPackageName(projectName);
|
|
168
|
-
const targetDir = path2.resolve(process.cwd(), validName);
|
|
169
|
-
if (fs.existsSync(targetDir) && !isEmpty(targetDir)) {
|
|
170
|
-
if (!interactive) {
|
|
171
|
-
console.log(pc.yellow(`\u8B66\u544A: \u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6`));
|
|
172
|
-
} else {
|
|
173
|
-
const overwrite = await confirm({
|
|
174
|
-
message: `\u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u662F\u5426\u7EE7\u7EED? (\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6)`,
|
|
175
|
-
default: false
|
|
176
|
-
});
|
|
177
|
-
if (!overwrite) {
|
|
178
|
-
const error = new Error("PROMPT_CANCELLED");
|
|
179
|
-
throw error;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
let template = options.template;
|
|
184
|
-
if (!template) {
|
|
185
|
-
if (!interactive) {
|
|
186
|
-
template = defaults.template || DEFAULT_TEMPLATE;
|
|
187
|
-
console.log(pc.dim(`\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F: ${template}`));
|
|
188
|
-
} else {
|
|
189
|
-
const defaultTemplate = defaults.template;
|
|
190
|
-
const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
|
|
191
|
-
const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
|
|
192
|
-
const templateChoices = [];
|
|
193
|
-
templateChoices.push(new Separator("\u2500\u2500 \u5355\u4F53\u9879\u76EE \u2500\u2500"));
|
|
194
|
-
for (const t of singleTemplates) {
|
|
195
|
-
templateChoices.push({
|
|
196
|
-
name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
|
|
197
|
-
value: t.name
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
if (fullstackTemplates.length > 0) {
|
|
201
|
-
templateChoices.push(new Separator("\u2500\u2500 \u524D\u540E\u7AEF\u5206\u79BB \u2500\u2500"));
|
|
202
|
-
for (const t of fullstackTemplates) {
|
|
203
|
-
templateChoices.push({
|
|
204
|
-
name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
|
|
205
|
-
value: t.name
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
template = await select({
|
|
210
|
-
message: "\u9009\u62E9\u6A21\u677F:",
|
|
211
|
-
choices: templateChoices,
|
|
212
|
-
default: defaultTemplate && registry.templates.some((t) => t.name === defaultTemplate) ? defaultTemplate : void 0
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
const found = registry.templates.find((t) => t.name === template);
|
|
217
|
-
if (!found) {
|
|
218
|
-
console.log(pc.red(`\u9519\u8BEF: \u672A\u627E\u5230\u6A21\u677F "${template}"`));
|
|
219
|
-
console.log(pc.dim(`\u53EF\u7528\u6A21\u677F: ${registry.templates.map((t) => t.name).join(", ")}`));
|
|
220
|
-
throw new CliError(`\u672A\u627E\u5230\u6A21\u677F "${template}"`, ExitCodes.USER_ERROR);
|
|
221
|
-
}
|
|
222
|
-
let initGit2 = options.git !== false;
|
|
223
|
-
if (options.git === void 0 && interactive) {
|
|
224
|
-
initGit2 = await confirm({
|
|
225
|
-
message: "\u521D\u59CB\u5316 Git \u4ED3\u5E93?",
|
|
226
|
-
default: true
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
let createGithub = options.github ?? false;
|
|
230
|
-
if (initGit2 && options.github === void 0 && interactive) {
|
|
231
|
-
createGithub = await confirm({
|
|
232
|
-
message: "\u5728 GitHub \u521B\u5EFA\u8FDC\u7A0B\u4ED3\u5E93?",
|
|
233
|
-
default: defaults.github ?? false
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
let isPublic = options.public ?? false;
|
|
237
|
-
if (createGithub && options.public === void 0 && interactive) {
|
|
238
|
-
const publicDefault = defaults.public ?? false;
|
|
239
|
-
isPublic = await select({
|
|
240
|
-
message: "\u4ED3\u5E93\u53EF\u89C1\u6027:",
|
|
241
|
-
choices: [
|
|
242
|
-
{ name: "private (\u79C1\u6709)", value: false },
|
|
243
|
-
{ name: "public (\u516C\u5F00)", value: true }
|
|
244
|
-
],
|
|
245
|
-
default: publicDefault
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
const result = {
|
|
249
|
-
projectName: validName,
|
|
250
|
-
template,
|
|
251
|
-
targetDir,
|
|
252
|
-
initGit: initGit2,
|
|
253
|
-
createGithub,
|
|
254
|
-
isPublic,
|
|
255
|
-
noInstall: options.install === false
|
|
256
|
-
};
|
|
257
|
-
debug("prompts", "\u6700\u7EC8\u9879\u76EE\u9009\u9879", result);
|
|
258
|
-
return result;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/scaffold.ts
|
|
187
|
+
// src/core/scaffold.ts
|
|
262
188
|
import path3 from "path";
|
|
263
189
|
import fs2 from "fs-extra";
|
|
264
190
|
async function scaffoldProject(options) {
|
|
@@ -361,49 +287,7 @@ async function copyFile(srcPath, destPath, projectName, config) {
|
|
|
361
287
|
await fs2.writeFile(destPath, content, "utf-8");
|
|
362
288
|
}
|
|
363
289
|
|
|
364
|
-
// src/
|
|
365
|
-
import pc2 from "picocolors";
|
|
366
|
-
|
|
367
|
-
// src/config.ts
|
|
368
|
-
import fs3 from "fs";
|
|
369
|
-
import path4 from "path";
|
|
370
|
-
import os from "os";
|
|
371
|
-
var CONFIG_PATH = path4.join(os.homedir(), ".spec-go.json");
|
|
372
|
-
var DEFAULT_CONFIG = {
|
|
373
|
-
defaults: {
|
|
374
|
-
github: false,
|
|
375
|
-
public: false,
|
|
376
|
-
template: ""
|
|
377
|
-
},
|
|
378
|
-
github: {
|
|
379
|
-
token: "",
|
|
380
|
-
defaultOrg: ""
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
function ensureConfigExists() {
|
|
384
|
-
if (!fs3.existsSync(CONFIG_PATH)) {
|
|
385
|
-
saveConfig(DEFAULT_CONFIG);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
function loadConfig() {
|
|
389
|
-
try {
|
|
390
|
-
if (fs3.existsSync(CONFIG_PATH)) {
|
|
391
|
-
const content = fs3.readFileSync(CONFIG_PATH, "utf-8");
|
|
392
|
-
return JSON.parse(content);
|
|
393
|
-
}
|
|
394
|
-
} catch {
|
|
395
|
-
}
|
|
396
|
-
return {};
|
|
397
|
-
}
|
|
398
|
-
function saveConfig(config) {
|
|
399
|
-
fs3.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
400
|
-
fs3.chmodSync(CONFIG_PATH, 384);
|
|
401
|
-
}
|
|
402
|
-
function getConfigPath() {
|
|
403
|
-
return CONFIG_PATH;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// src/github-api.ts
|
|
290
|
+
// src/core/github-api.ts
|
|
407
291
|
var GITHUB_API_BASE = "https://api.github.com";
|
|
408
292
|
async function githubFetch(endpoint, token, options = {}) {
|
|
409
293
|
const url = `${GITHUB_API_BASE}${endpoint}`;
|
|
@@ -413,7 +297,7 @@ async function githubFetch(endpoint, token, options = {}) {
|
|
|
413
297
|
...options,
|
|
414
298
|
headers: {
|
|
415
299
|
"Accept": "application/vnd.github+json",
|
|
416
|
-
"Authorization": `Bearer ${token
|
|
300
|
+
"Authorization": `Bearer ${token}`,
|
|
417
301
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
418
302
|
"User-Agent": "spec-go",
|
|
419
303
|
...options.headers
|
|
@@ -479,7 +363,7 @@ async function createRepository(token, options) {
|
|
|
479
363
|
});
|
|
480
364
|
}
|
|
481
365
|
|
|
482
|
-
// src/git.ts
|
|
366
|
+
// src/core/git.ts
|
|
483
367
|
async function initGit(targetDir) {
|
|
484
368
|
debug("git", `\u521D\u59CB\u5316 Git \u4ED3\u5E93: ${targetDir}`);
|
|
485
369
|
await execAsync("git", ["init"], { cwd: targetDir });
|
|
@@ -487,27 +371,27 @@ async function initGit(targetDir) {
|
|
|
487
371
|
await execAsync("git", ["commit", "-m", "Initial commit"], { cwd: targetDir });
|
|
488
372
|
debug("git", "Git \u521D\u59CB\u5316\u5B8C\u6210");
|
|
489
373
|
}
|
|
490
|
-
async function createGithubRepo(
|
|
374
|
+
async function createGithubRepo(options) {
|
|
375
|
+
const { repoName, targetDir, isPublic, organization, logger = silentLogger } = options;
|
|
491
376
|
debug("git", `\u521B\u5EFA GitHub \u4ED3\u5E93: ${repoName}, \u516C\u5F00: ${isPublic}`);
|
|
492
377
|
const config = loadConfig();
|
|
493
378
|
if (!config.github?.token) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
379
|
+
logger.warn("\u26A0 \u672A\u914D\u7F6E GitHub Token\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u521B\u5EFA");
|
|
380
|
+
logger.dim(` \u8FD0\u884C spec-go config --setup-github \u914D\u7F6E Token`);
|
|
381
|
+
logger.dim(` \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${getConfigPath()}`);
|
|
497
382
|
return null;
|
|
498
383
|
}
|
|
499
384
|
const token = config.github.token;
|
|
500
|
-
const defaultOrg = config.github.defaultOrg;
|
|
385
|
+
const defaultOrg = organization || config.github.defaultOrg;
|
|
501
386
|
const validateResult = await validateToken(token);
|
|
502
387
|
if (!validateResult.success) {
|
|
503
|
-
|
|
388
|
+
logger.warn(`\u26A0 GitHub Token \u65E0\u6548: ${validateResult.error}`);
|
|
504
389
|
if (validateResult.rateLimitReset) {
|
|
505
|
-
|
|
390
|
+
logger.dim(` Rate limit \u5C06\u5728 ${validateResult.rateLimitReset.toLocaleString()} \u91CD\u7F6E`);
|
|
506
391
|
}
|
|
507
|
-
|
|
392
|
+
logger.dim(` \u8FD0\u884C spec-go config --setup-github \u91CD\u65B0\u914D\u7F6E`);
|
|
508
393
|
return null;
|
|
509
394
|
}
|
|
510
|
-
const username = validateResult.data.login;
|
|
511
395
|
const createResult = await createRepository(token, {
|
|
512
396
|
name: repoName,
|
|
513
397
|
isPrivate: !isPublic,
|
|
@@ -515,12 +399,12 @@ async function createGithubRepo(repoName, targetDir, isPublic) {
|
|
|
515
399
|
});
|
|
516
400
|
if (!createResult.success) {
|
|
517
401
|
if (createResult.statusCode === 422) {
|
|
518
|
-
|
|
402
|
+
logger.warn(`\u26A0 \u4ED3\u5E93 "${repoName}" \u5DF2\u5B58\u5728\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u540D\u79F0`);
|
|
519
403
|
} else if (createResult.rateLimitReset) {
|
|
520
|
-
|
|
521
|
-
|
|
404
|
+
logger.warn(`\u26A0 API \u8BF7\u6C42\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650`);
|
|
405
|
+
logger.dim(` \u5C06\u5728 ${createResult.rateLimitReset.toLocaleString()} \u91CD\u7F6E`);
|
|
522
406
|
} else {
|
|
523
|
-
|
|
407
|
+
logger.warn(`\u26A0 \u521B\u5EFA\u4ED3\u5E93\u5931\u8D25: ${createResult.error}`);
|
|
524
408
|
}
|
|
525
409
|
return null;
|
|
526
410
|
}
|
|
@@ -549,165 +433,178 @@ async function createGithubRepo(repoName, targetDir, isPublic) {
|
|
|
549
433
|
{ cwd: targetDir }
|
|
550
434
|
);
|
|
551
435
|
if (pushMaster.code !== 0) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
436
|
+
logger.warn(`\u26A0 \u63A8\u9001\u5931\u8D25: ${pushResult.stderr || pushMaster.stderr}`);
|
|
437
|
+
logger.dim(` \u4ED3\u5E93\u5DF2\u521B\u5EFA: ${repo.html_url}`);
|
|
438
|
+
logger.dim(" \u8BF7\u624B\u52A8\u63A8\u9001\u4EE3\u7801");
|
|
555
439
|
return repo.html_url;
|
|
556
440
|
}
|
|
557
441
|
}
|
|
558
442
|
return repo.html_url;
|
|
559
443
|
}
|
|
560
444
|
|
|
561
|
-
// src/post-init.ts
|
|
562
|
-
import
|
|
563
|
-
|
|
564
|
-
|
|
445
|
+
// src/core/post-init.ts
|
|
446
|
+
import path4 from "path";
|
|
447
|
+
async function runPostInit(targetDir, commands, options = {}) {
|
|
448
|
+
const { logger = silentLogger } = options;
|
|
565
449
|
for (const cmd of commands) {
|
|
566
|
-
|
|
450
|
+
logger.dim(` ${cmd.description}...`);
|
|
567
451
|
const [command, ...args] = cmd.command.split(" ");
|
|
568
452
|
const result = await execAsync(command, args, {
|
|
569
453
|
cwd: targetDir,
|
|
570
454
|
stdio: "pipe"
|
|
571
455
|
});
|
|
572
456
|
if (result.code !== 0) {
|
|
573
|
-
|
|
457
|
+
logger.warn(`\u26A0 ${cmd.description} \u5931\u8D25`);
|
|
574
458
|
if (result.stderr) {
|
|
575
|
-
|
|
459
|
+
logger.dim(result.stderr.slice(0, 200));
|
|
576
460
|
}
|
|
577
461
|
}
|
|
578
462
|
}
|
|
579
463
|
}
|
|
580
|
-
async function runWorkspacePostInit(targetDir, config) {
|
|
464
|
+
async function runWorkspacePostInit(targetDir, config, options = {}) {
|
|
465
|
+
const { logger = silentLogger } = options;
|
|
581
466
|
if (config.postInit && config.postInit.length > 0) {
|
|
582
|
-
await runPostInit(targetDir, config.postInit);
|
|
467
|
+
await runPostInit(targetDir, config.postInit, options);
|
|
583
468
|
}
|
|
584
469
|
if (config.workspaces) {
|
|
585
470
|
for (const ws of config.workspaces) {
|
|
586
471
|
if (ws.postInit && ws.postInit.length > 0) {
|
|
587
|
-
const wsDir =
|
|
588
|
-
|
|
589
|
-
await runPostInit(wsDir, ws.postInit);
|
|
472
|
+
const wsDir = path4.join(targetDir, ws.name);
|
|
473
|
+
logger.dim(` [${ws.name}]`);
|
|
474
|
+
await runPostInit(wsDir, ws.postInit, options);
|
|
590
475
|
}
|
|
591
476
|
}
|
|
592
477
|
}
|
|
593
478
|
}
|
|
594
479
|
|
|
595
|
-
// src/
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
480
|
+
// src/cli/prompts.ts
|
|
481
|
+
var DEFAULT_TEMPLATE = "node-ts";
|
|
482
|
+
async function runPrompts(argProjectName, options, userConfig = {}, interactive = true) {
|
|
483
|
+
const defaults = userConfig.defaults ?? {};
|
|
484
|
+
const templatesDir = getTemplatesDir();
|
|
485
|
+
const registryPath = path5.join(templatesDir, "template.config.json");
|
|
486
|
+
const registry = JSON.parse(
|
|
487
|
+
fs3.readFileSync(registryPath, "utf-8")
|
|
488
|
+
);
|
|
489
|
+
debug("prompts", `\u4EA4\u4E92\u6A21\u5F0F: ${interactive}`);
|
|
490
|
+
debug("prompts", `CLI \u53C2\u6570`, { argProjectName, options });
|
|
491
|
+
let projectName = argProjectName;
|
|
492
|
+
if (!projectName) {
|
|
493
|
+
if (!interactive) {
|
|
494
|
+
console.log(pc.red("\u9519\u8BEF: \u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0"));
|
|
495
|
+
throw new CliError("\u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0", ExitCodes.USER_ERROR);
|
|
609
496
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
497
|
+
projectName = await input({
|
|
498
|
+
message: "\u9879\u76EE\u540D\u79F0:",
|
|
499
|
+
default: "my-project",
|
|
500
|
+
validate: (value) => {
|
|
501
|
+
if (!value.trim()) {
|
|
502
|
+
return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
|
|
503
|
+
}
|
|
504
|
+
if (!isValidPackageName(toValidPackageName(value))) {
|
|
505
|
+
return "\u65E0\u6548\u7684\u9879\u76EE\u540D\u79F0";
|
|
506
|
+
}
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
});
|
|
618
510
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
511
|
+
const validName = toValidPackageName(projectName);
|
|
512
|
+
const targetDir = path5.resolve(process.cwd(), validName);
|
|
513
|
+
if (fs3.existsSync(targetDir) && !isEmpty(targetDir)) {
|
|
514
|
+
if (!interactive) {
|
|
515
|
+
console.log(pc.yellow(`\u8B66\u544A: \u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6`));
|
|
516
|
+
} else {
|
|
517
|
+
const overwrite = await confirm({
|
|
518
|
+
message: `\u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u662F\u5426\u7EE7\u7EED? (\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6)`,
|
|
519
|
+
default: false
|
|
520
|
+
});
|
|
521
|
+
if (!overwrite) {
|
|
522
|
+
const error = new Error("PROMPT_CANCELLED");
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
625
525
|
}
|
|
626
|
-
const data = await response.json();
|
|
627
|
-
return data.version;
|
|
628
|
-
} catch {
|
|
629
|
-
return null;
|
|
630
526
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
527
|
+
let template = options.template;
|
|
528
|
+
if (!template) {
|
|
529
|
+
if (!interactive) {
|
|
530
|
+
template = defaults.template || DEFAULT_TEMPLATE;
|
|
531
|
+
console.log(pc.dim(`\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F: ${template}`));
|
|
532
|
+
} else {
|
|
533
|
+
const defaultTemplate = defaults.template;
|
|
534
|
+
const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
|
|
535
|
+
const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
|
|
536
|
+
const templateChoices = [];
|
|
537
|
+
templateChoices.push(new Separator("\u2500\u2500 \u5355\u4F53\u9879\u76EE \u2500\u2500"));
|
|
538
|
+
for (const t of singleTemplates) {
|
|
539
|
+
templateChoices.push({
|
|
540
|
+
name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
|
|
541
|
+
value: t.name
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (fullstackTemplates.length > 0) {
|
|
545
|
+
templateChoices.push(new Separator("\u2500\u2500 \u524D\u540E\u7AEF\u5206\u79BB \u2500\u2500"));
|
|
546
|
+
for (const t of fullstackTemplates) {
|
|
547
|
+
templateChoices.push({
|
|
548
|
+
name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
|
|
549
|
+
value: t.name
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
template = await select({
|
|
554
|
+
message: "\u9009\u62E9\u6A21\u677F:",
|
|
555
|
+
choices: templateChoices,
|
|
556
|
+
default: defaultTemplate && registry.templates.some((t) => t.name === defaultTemplate) ? defaultTemplate : void 0
|
|
557
|
+
});
|
|
653
558
|
}
|
|
654
559
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
console.log(
|
|
658
|
-
|
|
659
|
-
);
|
|
660
|
-
console.log(pc4.cyan(` \u8FD0\u884C ${pc4.bold(`npm update -g ${PACKAGE_NAME}`)} \u66F4\u65B0`));
|
|
661
|
-
console.log();
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
function detectPackageManager() {
|
|
665
|
-
const execPath = process.argv[1] || "";
|
|
666
|
-
if (execPath.includes("pnpm")) return "pnpm";
|
|
667
|
-
if (execPath.includes("yarn")) return "yarn";
|
|
668
|
-
return "npm";
|
|
669
|
-
}
|
|
670
|
-
function getUpdateCommand(pm) {
|
|
671
|
-
switch (pm) {
|
|
672
|
-
case "pnpm":
|
|
673
|
-
return `pnpm update -g ${PACKAGE_NAME}`;
|
|
674
|
-
case "yarn":
|
|
675
|
-
return `yarn global upgrade ${PACKAGE_NAME}`;
|
|
676
|
-
default:
|
|
677
|
-
return `npm update -g ${PACKAGE_NAME}`;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
async function runUpdate(currentVersion, checkOnly) {
|
|
681
|
-
const latestVersion = await fetchLatestVersion();
|
|
682
|
-
if (!latestVersion) {
|
|
683
|
-
console.log(pc4.red("\u2717 \u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
|
|
684
|
-
process.exit(ExitCodes.EXTERNAL_ERROR);
|
|
560
|
+
const found = registry.templates.find((t) => t.name === template);
|
|
561
|
+
if (!found) {
|
|
562
|
+
console.log(pc.red(`\u9519\u8BEF: \u672A\u627E\u5230\u6A21\u677F "${template}"`));
|
|
563
|
+
console.log(pc.dim(`\u53EF\u7528\u6A21\u677F: ${registry.templates.map((t) => t.name).join(", ")}`));
|
|
564
|
+
throw new CliError(`\u672A\u627E\u5230\u6A21\u677F "${template}"`, ExitCodes.USER_ERROR);
|
|
685
565
|
}
|
|
686
|
-
|
|
687
|
-
if (
|
|
688
|
-
|
|
689
|
-
|
|
566
|
+
let initGit2 = options.git !== false;
|
|
567
|
+
if (options.git === void 0 && interactive) {
|
|
568
|
+
initGit2 = await confirm({
|
|
569
|
+
message: "\u521D\u59CB\u5316 Git \u4ED3\u5E93?",
|
|
570
|
+
default: true
|
|
571
|
+
});
|
|
690
572
|
}
|
|
691
|
-
|
|
692
|
-
if (
|
|
693
|
-
|
|
694
|
-
|
|
573
|
+
let createGithub = options.github ?? false;
|
|
574
|
+
if (initGit2 && options.github === void 0 && interactive) {
|
|
575
|
+
createGithub = await confirm({
|
|
576
|
+
message: "\u5728 GitHub \u521B\u5EFA\u8FDC\u7A0B\u4ED3\u5E93?",
|
|
577
|
+
default: defaults.github ?? false
|
|
578
|
+
});
|
|
695
579
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
580
|
+
let isPublic = options.public ?? false;
|
|
581
|
+
if (createGithub && options.public === void 0 && interactive) {
|
|
582
|
+
const publicDefault = defaults.public ?? false;
|
|
583
|
+
isPublic = await select({
|
|
584
|
+
message: "\u4ED3\u5E93\u53EF\u89C1\u6027:",
|
|
585
|
+
choices: [
|
|
586
|
+
{ name: "private (\u79C1\u6709)", value: false },
|
|
587
|
+
{ name: "public (\u516C\u5F00)", value: true }
|
|
588
|
+
],
|
|
589
|
+
default: publicDefault
|
|
590
|
+
});
|
|
705
591
|
}
|
|
592
|
+
const result = {
|
|
593
|
+
projectName: validName,
|
|
594
|
+
template,
|
|
595
|
+
targetDir,
|
|
596
|
+
initGit: initGit2,
|
|
597
|
+
createGithub,
|
|
598
|
+
isPublic,
|
|
599
|
+
noInstall: options.install === false
|
|
600
|
+
};
|
|
601
|
+
debug("prompts", "\u6700\u7EC8\u9879\u76EE\u9009\u9879", result);
|
|
602
|
+
return result;
|
|
706
603
|
}
|
|
707
604
|
|
|
708
|
-
// src/github-setup.ts
|
|
605
|
+
// src/cli/github-setup.ts
|
|
709
606
|
import { input as input2, confirm as confirm2 } from "@inquirer/prompts";
|
|
710
|
-
import
|
|
607
|
+
import pc2 from "picocolors";
|
|
711
608
|
var TOKEN_HELP_URL = "https://github.com/settings/tokens/new?scopes=repo&description=spec-go";
|
|
712
609
|
function isUserCancelled(err) {
|
|
713
610
|
const error = err;
|
|
@@ -715,17 +612,17 @@ function isUserCancelled(err) {
|
|
|
715
612
|
}
|
|
716
613
|
async function runGitHubSetup() {
|
|
717
614
|
console.log();
|
|
718
|
-
console.log(
|
|
615
|
+
console.log(pc2.cyan("\u914D\u7F6E GitHub Token"));
|
|
719
616
|
console.log();
|
|
720
617
|
console.log("\u9700\u8981\u4E00\u4E2A\u5177\u6709 repo \u6743\u9650\u7684 Personal Access Token (Classic) \u6765\u521B\u5EFA\u4ED3\u5E93\u3002");
|
|
721
618
|
console.log();
|
|
722
|
-
console.log(
|
|
723
|
-
console.log(
|
|
724
|
-
console.log(
|
|
725
|
-
console.log(
|
|
726
|
-
console.log(
|
|
619
|
+
console.log(pc2.dim("\u83B7\u53D6\u6B65\u9AA4:"));
|
|
620
|
+
console.log(pc2.dim("1. \u8BBF\u95EE GitHub Settings > Developer settings > Personal access tokens"));
|
|
621
|
+
console.log(pc2.dim('2. \u70B9\u51FB "Generate new token (classic)"'));
|
|
622
|
+
console.log(pc2.dim('3. \u52FE\u9009 "repo" \u6743\u9650'));
|
|
623
|
+
console.log(pc2.dim("4. \u751F\u6210\u5E76\u590D\u5236 Token"));
|
|
727
624
|
console.log();
|
|
728
|
-
console.log(
|
|
625
|
+
console.log(pc2.dim(`\u5FEB\u6377\u94FE\u63A5: ${TOKEN_HELP_URL}`));
|
|
729
626
|
console.log();
|
|
730
627
|
try {
|
|
731
628
|
const token = await input2({
|
|
@@ -741,17 +638,17 @@ async function runGitHubSetup() {
|
|
|
741
638
|
}
|
|
742
639
|
});
|
|
743
640
|
console.log();
|
|
744
|
-
console.log(
|
|
641
|
+
console.log(pc2.dim("\u6B63\u5728\u9A8C\u8BC1 Token..."));
|
|
745
642
|
const result = await validateToken(token.trim());
|
|
746
643
|
if (!result.success) {
|
|
747
|
-
console.log(
|
|
644
|
+
console.log(pc2.red(`\u2717 Token \u9A8C\u8BC1\u5931\u8D25: ${result.error}`));
|
|
748
645
|
if (result.rateLimitReset) {
|
|
749
|
-
console.log(
|
|
646
|
+
console.log(pc2.dim(` Rate limit \u5C06\u5728 ${result.rateLimitReset.toLocaleString()} \u91CD\u7F6E`));
|
|
750
647
|
}
|
|
751
648
|
return false;
|
|
752
649
|
}
|
|
753
650
|
const user = result.data;
|
|
754
|
-
console.log(
|
|
651
|
+
console.log(pc2.green(`\u2714 Token \u6709\u6548\uFF0C\u5DF2\u767B\u5F55\u4E3A: ${user.login}${user.name ? ` (${user.name})` : ""}`));
|
|
755
652
|
console.log();
|
|
756
653
|
const setOrg = await confirm2({
|
|
757
654
|
message: "\u662F\u5426\u8BBE\u7F6E\u9ED8\u8BA4\u7EC4\u7EC7? (\u5426\u5219\u4ED3\u5E93\u5C06\u521B\u5EFA\u5728\u4E2A\u4EBA\u8D26\u6237\u4E0B)",
|
|
@@ -777,12 +674,12 @@ async function runGitHubSetup() {
|
|
|
777
674
|
};
|
|
778
675
|
saveConfig(config);
|
|
779
676
|
console.log();
|
|
780
|
-
console.log(
|
|
781
|
-
console.log(
|
|
677
|
+
console.log(pc2.green("\u2714 GitHub \u914D\u7F6E\u5DF2\u4FDD\u5B58"));
|
|
678
|
+
console.log(pc2.dim(" \u914D\u7F6E\u6587\u4EF6\u5DF2\u8BBE\u7F6E\u4E3A\u4EC5\u5F53\u524D\u7528\u6237\u53EF\u8BFB"));
|
|
782
679
|
return true;
|
|
783
680
|
} catch (err) {
|
|
784
681
|
if (isUserCancelled(err)) {
|
|
785
|
-
console.log(
|
|
682
|
+
console.log(pc2.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
|
|
786
683
|
return false;
|
|
787
684
|
}
|
|
788
685
|
throw err;
|
|
@@ -791,10 +688,10 @@ async function runGitHubSetup() {
|
|
|
791
688
|
function showConfig() {
|
|
792
689
|
const config = loadConfig();
|
|
793
690
|
console.log();
|
|
794
|
-
console.log(
|
|
691
|
+
console.log(pc2.cyan("\u5F53\u524D\u914D\u7F6E:"));
|
|
795
692
|
console.log();
|
|
796
693
|
if (config.defaults) {
|
|
797
|
-
console.log(
|
|
694
|
+
console.log(pc2.dim("defaults:"));
|
|
798
695
|
if (config.defaults.github !== void 0) {
|
|
799
696
|
console.log(` github: ${config.defaults.github}`);
|
|
800
697
|
}
|
|
@@ -806,7 +703,7 @@ function showConfig() {
|
|
|
806
703
|
}
|
|
807
704
|
}
|
|
808
705
|
if (config.github) {
|
|
809
|
-
console.log(
|
|
706
|
+
console.log(pc2.dim("github:"));
|
|
810
707
|
if (config.github.token) {
|
|
811
708
|
const masked = config.github.token.slice(0, 8) + "..." + config.github.token.slice(-4);
|
|
812
709
|
console.log(` token: ${masked}`);
|
|
@@ -816,12 +713,145 @@ function showConfig() {
|
|
|
816
713
|
}
|
|
817
714
|
}
|
|
818
715
|
if (!config.defaults && !config.github) {
|
|
819
|
-
console.log(
|
|
716
|
+
console.log(pc2.dim("(\u65E0\u914D\u7F6E)"));
|
|
820
717
|
}
|
|
821
718
|
console.log();
|
|
822
719
|
}
|
|
823
720
|
|
|
824
|
-
// src/cli.ts
|
|
721
|
+
// src/cli/update-check.ts
|
|
722
|
+
import fs4 from "fs";
|
|
723
|
+
import path6 from "path";
|
|
724
|
+
import os2 from "os";
|
|
725
|
+
import { execSync } from "child_process";
|
|
726
|
+
import pc3 from "picocolors";
|
|
727
|
+
var CACHE_PATH = path6.join(os2.homedir(), ".spec-go-update-check");
|
|
728
|
+
var CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
729
|
+
var PACKAGE_NAME = "@lambertkeith/spec-go";
|
|
730
|
+
function readCache() {
|
|
731
|
+
try {
|
|
732
|
+
if (fs4.existsSync(CACHE_PATH)) {
|
|
733
|
+
const content = fs4.readFileSync(CACHE_PATH, "utf-8");
|
|
734
|
+
return JSON.parse(content);
|
|
735
|
+
}
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
function writeCache(cache) {
|
|
741
|
+
try {
|
|
742
|
+
fs4.writeFileSync(CACHE_PATH, JSON.stringify(cache), "utf-8");
|
|
743
|
+
} catch {
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
async function fetchLatestVersion() {
|
|
747
|
+
try {
|
|
748
|
+
const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
|
|
749
|
+
if (!response.ok) {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
const data = await response.json();
|
|
753
|
+
return data.version;
|
|
754
|
+
} catch {
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
function compareVersions(current, latest) {
|
|
759
|
+
const currentParts = current.split(".").map(Number);
|
|
760
|
+
const latestParts = latest.split(".").map(Number);
|
|
761
|
+
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
762
|
+
const c = currentParts[i] || 0;
|
|
763
|
+
const l = latestParts[i] || 0;
|
|
764
|
+
if (c < l) return -1;
|
|
765
|
+
if (c > l) return 1;
|
|
766
|
+
}
|
|
767
|
+
return 0;
|
|
768
|
+
}
|
|
769
|
+
async function checkForUpdates(currentVersion) {
|
|
770
|
+
const cache = readCache();
|
|
771
|
+
const now = Date.now();
|
|
772
|
+
let latestVersion = null;
|
|
773
|
+
if (cache && now - cache.lastCheck < CACHE_TTL) {
|
|
774
|
+
latestVersion = cache.latestVersion;
|
|
775
|
+
} else {
|
|
776
|
+
latestVersion = await fetchLatestVersion();
|
|
777
|
+
if (latestVersion) {
|
|
778
|
+
writeCache({ lastCheck: now, latestVersion });
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (latestVersion && compareVersions(currentVersion, latestVersion) < 0) {
|
|
782
|
+
console.log();
|
|
783
|
+
console.log(
|
|
784
|
+
pc3.yellow(` \u26A0 \u53D1\u73B0\u65B0\u7248\u672C ${pc3.bold(latestVersion)}\uFF0C\u5F53\u524D ${currentVersion}`)
|
|
785
|
+
);
|
|
786
|
+
console.log(pc3.cyan(` \u8FD0\u884C ${pc3.bold(`npm update -g ${PACKAGE_NAME}`)} \u66F4\u65B0`));
|
|
787
|
+
console.log();
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function detectPackageManager() {
|
|
791
|
+
const execPath = process.argv[1] || "";
|
|
792
|
+
if (execPath.includes("pnpm")) return "pnpm";
|
|
793
|
+
if (execPath.includes("yarn")) return "yarn";
|
|
794
|
+
return "npm";
|
|
795
|
+
}
|
|
796
|
+
function getUpdateCommand(pm) {
|
|
797
|
+
switch (pm) {
|
|
798
|
+
case "pnpm":
|
|
799
|
+
return `pnpm update -g ${PACKAGE_NAME}`;
|
|
800
|
+
case "yarn":
|
|
801
|
+
return `yarn global upgrade ${PACKAGE_NAME}`;
|
|
802
|
+
default:
|
|
803
|
+
return `npm update -g ${PACKAGE_NAME}`;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
async function runUpdate(currentVersion, checkOnly) {
|
|
807
|
+
const latestVersion = await fetchLatestVersion();
|
|
808
|
+
if (!latestVersion) {
|
|
809
|
+
console.log(pc3.red("\u2717 \u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
|
|
810
|
+
process.exit(ExitCodes.EXTERNAL_ERROR);
|
|
811
|
+
}
|
|
812
|
+
const comparison = compareVersions(currentVersion, latestVersion);
|
|
813
|
+
if (comparison >= 0) {
|
|
814
|
+
console.log(pc3.green(`\u2714 \u5DF2\u662F\u6700\u65B0\u7248\u672C (${currentVersion})`));
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
console.log(pc3.cyan(`\u53D1\u73B0\u65B0\u7248\u672C: ${currentVersion} \u2192 ${latestVersion}`));
|
|
818
|
+
if (checkOnly) {
|
|
819
|
+
console.log(pc3.dim(`\u8FD0\u884C spec-go update \u66F4\u65B0`));
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const pm = detectPackageManager();
|
|
823
|
+
const updateCmd = getUpdateCommand(pm);
|
|
824
|
+
console.log(pc3.dim(`\u6B63\u5728\u6267\u884C: ${updateCmd}`));
|
|
825
|
+
try {
|
|
826
|
+
execSync(updateCmd, { stdio: "inherit" });
|
|
827
|
+
console.log(pc3.green(`\u2714 \u66F4\u65B0\u5B8C\u6210`));
|
|
828
|
+
} catch {
|
|
829
|
+
console.log(pc3.red("\u2717 \u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\u66F4\u65B0\u547D\u4EE4"));
|
|
830
|
+
process.exit(ExitCodes.EXTERNAL_ERROR);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/cli/console-logger.ts
|
|
835
|
+
import pc4 from "picocolors";
|
|
836
|
+
var consoleLogger = {
|
|
837
|
+
info(message) {
|
|
838
|
+
console.log(message);
|
|
839
|
+
},
|
|
840
|
+
success(message) {
|
|
841
|
+
console.log(pc4.green(message));
|
|
842
|
+
},
|
|
843
|
+
warn(message) {
|
|
844
|
+
console.log(pc4.yellow(message));
|
|
845
|
+
},
|
|
846
|
+
error(message) {
|
|
847
|
+
console.log(pc4.red(message));
|
|
848
|
+
},
|
|
849
|
+
dim(message) {
|
|
850
|
+
console.log(pc4.dim(message));
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
// src/cli/cli.ts
|
|
825
855
|
function isInteractive(options) {
|
|
826
856
|
if (options.yes) return false;
|
|
827
857
|
return process.stdin.isTTY === true;
|
|
@@ -833,7 +863,7 @@ async function createCli() {
|
|
|
833
863
|
setDebugEnabled(true);
|
|
834
864
|
}
|
|
835
865
|
console.log();
|
|
836
|
-
console.log(` ${
|
|
866
|
+
console.log(` ${pc5.cyan("spec-go")} ${pc5.dim(`v${version}`)}`);
|
|
837
867
|
console.log();
|
|
838
868
|
checkForUpdates(version).catch(() => {
|
|
839
869
|
});
|
|
@@ -845,7 +875,7 @@ async function createCli() {
|
|
|
845
875
|
} catch (err) {
|
|
846
876
|
const error = err;
|
|
847
877
|
if (error.message === "PROMPT_CANCELLED" || error.name === "ExitPromptError" || error.message.includes("User force closed")) {
|
|
848
|
-
console.log(
|
|
878
|
+
console.log(pc5.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
|
|
849
879
|
process.exit(ExitCodes.SUCCESS);
|
|
850
880
|
}
|
|
851
881
|
throw err;
|
|
@@ -872,15 +902,15 @@ async function createCli() {
|
|
|
872
902
|
const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
|
|
873
903
|
const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
|
|
874
904
|
console.log();
|
|
875
|
-
console.log(
|
|
905
|
+
console.log(pc5.cyan(" \u5355\u4F53\u9879\u76EE:"));
|
|
876
906
|
for (const t of singleTemplates) {
|
|
877
|
-
console.log(` ${
|
|
907
|
+
console.log(` ${pc5.green(t.name.padEnd(24))} ${pc5.dim(t.displayName)}`);
|
|
878
908
|
}
|
|
879
909
|
if (fullstackTemplates.length > 0) {
|
|
880
910
|
console.log();
|
|
881
|
-
console.log(
|
|
911
|
+
console.log(pc5.cyan(" \u524D\u540E\u7AEF\u5206\u79BB:"));
|
|
882
912
|
for (const t of fullstackTemplates) {
|
|
883
|
-
console.log(` ${
|
|
913
|
+
console.log(` ${pc5.green(t.name.padEnd(24))} ${pc5.dim(t.displayName)}`);
|
|
884
914
|
}
|
|
885
915
|
}
|
|
886
916
|
console.log();
|
|
@@ -895,11 +925,11 @@ async function createCli() {
|
|
|
895
925
|
console.log(getConfigPath());
|
|
896
926
|
} else {
|
|
897
927
|
console.log();
|
|
898
|
-
console.log(
|
|
928
|
+
console.log(pc5.cyan("\u914D\u7F6E\u7BA1\u7406\u547D\u4EE4:"));
|
|
899
929
|
console.log();
|
|
900
|
-
console.log(` ${
|
|
901
|
-
console.log(` ${
|
|
902
|
-
console.log(` ${
|
|
930
|
+
console.log(` ${pc5.dim("spec-go config --setup-github")} \u914D\u7F6E GitHub Token`);
|
|
931
|
+
console.log(` ${pc5.dim("spec-go config --show")} \u663E\u793A\u5F53\u524D\u914D\u7F6E`);
|
|
932
|
+
console.log(` ${pc5.dim("spec-go config --path")} \u663E\u793A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84`);
|
|
903
933
|
console.log();
|
|
904
934
|
}
|
|
905
935
|
});
|
|
@@ -910,28 +940,29 @@ async function createCli() {
|
|
|
910
940
|
}
|
|
911
941
|
async function executeProject(options) {
|
|
912
942
|
const templateConfig = await scaffoldProject(options);
|
|
913
|
-
console.log(
|
|
943
|
+
console.log(pc5.green("\u2714 \u9879\u76EE\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
914
944
|
if (!options.noInstall && templateConfig) {
|
|
915
945
|
if (templateConfig.type === "workspace") {
|
|
916
|
-
await runWorkspacePostInit(options.targetDir, templateConfig);
|
|
917
|
-
console.log(
|
|
946
|
+
await runWorkspacePostInit(options.targetDir, templateConfig, { logger: consoleLogger });
|
|
947
|
+
console.log(pc5.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
|
|
918
948
|
} else if (templateConfig.postInit) {
|
|
919
|
-
await runPostInit(options.targetDir, templateConfig.postInit);
|
|
920
|
-
console.log(
|
|
949
|
+
await runPostInit(options.targetDir, templateConfig.postInit, { logger: consoleLogger });
|
|
950
|
+
console.log(pc5.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
|
|
921
951
|
}
|
|
922
952
|
}
|
|
923
953
|
if (options.initGit) {
|
|
924
954
|
await initGit(options.targetDir);
|
|
925
|
-
console.log(
|
|
955
|
+
console.log(pc5.green("\u2714 Git \u4ED3\u5E93\u5DF2\u521D\u59CB\u5316"));
|
|
926
956
|
}
|
|
927
957
|
if (options.createGithub) {
|
|
928
|
-
const repoUrl = await createGithubRepo(
|
|
929
|
-
options.projectName,
|
|
930
|
-
options.targetDir,
|
|
931
|
-
options.isPublic
|
|
932
|
-
|
|
958
|
+
const repoUrl = await createGithubRepo({
|
|
959
|
+
repoName: options.projectName,
|
|
960
|
+
targetDir: options.targetDir,
|
|
961
|
+
isPublic: options.isPublic,
|
|
962
|
+
logger: consoleLogger
|
|
963
|
+
});
|
|
933
964
|
if (repoUrl) {
|
|
934
|
-
console.log(
|
|
965
|
+
console.log(pc5.green(`\u2714 GitHub \u4ED3\u5E93\u5DF2\u521B\u5EFA: ${pc5.cyan(repoUrl)}`));
|
|
935
966
|
}
|
|
936
967
|
}
|
|
937
968
|
console.log();
|
|
@@ -939,21 +970,21 @@ async function executeProject(options) {
|
|
|
939
970
|
const cdCmd = options.targetDir !== process.cwd() ? `cd ${options.projectName} && ` : "";
|
|
940
971
|
if (templateConfig?.type === "workspace") {
|
|
941
972
|
if (pm === "pnpm") {
|
|
942
|
-
console.log(
|
|
973
|
+
console.log(pc5.dim(` ${cdCmd}pnpm dev`));
|
|
943
974
|
} else {
|
|
944
|
-
console.log(
|
|
975
|
+
console.log(pc5.dim(` ${cdCmd}make dev`));
|
|
945
976
|
}
|
|
946
977
|
} else if (pm === "maven") {
|
|
947
|
-
console.log(
|
|
978
|
+
console.log(pc5.dim(` ${cdCmd}./mvnw spring-boot:run`));
|
|
948
979
|
} else if (pm === "gradle") {
|
|
949
|
-
console.log(
|
|
980
|
+
console.log(pc5.dim(` ${cdCmd}./gradlew bootRun`));
|
|
950
981
|
} else {
|
|
951
|
-
console.log(
|
|
982
|
+
console.log(pc5.dim(` ${cdCmd}${pm} dev`));
|
|
952
983
|
}
|
|
953
984
|
console.log();
|
|
954
985
|
}
|
|
955
986
|
|
|
956
|
-
// src/index.ts
|
|
987
|
+
// src/cli/index.ts
|
|
957
988
|
async function main() {
|
|
958
989
|
ensureConfigExists();
|
|
959
990
|
const program = await createCli();
|