@lightfish/cli 0.0.4 → 0.0.6
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/create-lightfish.js +126 -60
- package/dist/lightfish.js +527 -63
- package/dist/templates/react-vite/.agents/skills/lightfish-server/SKILL.md +447 -0
- package/dist/templates/react-vite/.vscode/settings.json +42 -0
- package/dist/templates/react-vite/AGENTS.md +254 -0
- package/dist/templates/react-vite/README.md +150 -0
- package/dist/templates/react-vite/eslint.config.js +25 -0
- package/dist/templates/react-vite/index.html +13 -0
- package/dist/templates/react-vite/lightfish-server.config.js +6 -0
- package/dist/templates/react-vite/package.json +41 -0
- package/dist/templates/react-vite/pnpm-lock.yaml +3649 -0
- package/dist/templates/react-vite/public/favicon.svg +1 -0
- package/dist/templates/react-vite/public/icons.svg +24 -0
- package/dist/templates/react-vite/server/router/hello/index.ts +20 -0
- package/dist/templates/react-vite/server/schema/index.ts +18 -0
- package/dist/templates/react-vite/src/assets/hero.png +0 -0
- package/dist/templates/react-vite/src/assets/react.svg +1 -0
- package/dist/templates/react-vite/src/assets/vite.svg +1 -0
- package/dist/templates/react-vite/src/main.css +1 -0
- package/dist/templates/react-vite/src/main.tsx +43 -0
- package/dist/templates/react-vite/src/pages/example/index.tsx +26 -0
- package/dist/templates/react-vite/src/pages/example/layout.tsx +31 -0
- package/dist/templates/react-vite/src/pages/example/model.ts +20 -0
- package/dist/templates/react-vite/src/pages/index.tsx +17 -0
- package/dist/templates/react-vite/src/pages/layout.tsx +15 -0
- package/dist/templates/react-vite/tsconfig.app.json +30 -0
- package/dist/templates/react-vite/tsconfig.json +7 -0
- package/dist/templates/react-vite/tsconfig.node.json +24 -0
- package/dist/templates/react-vite/vite.config.ts +61 -0
- package/package.json +6 -2
package/dist/lightfish.js
CHANGED
|
@@ -2,20 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
// src/lightfish.ts
|
|
4
4
|
import cac from "cac";
|
|
5
|
-
import { execa as
|
|
5
|
+
import { execa as execa4 } from "execa";
|
|
6
6
|
|
|
7
7
|
// src/commands/create.ts
|
|
8
|
-
import
|
|
8
|
+
import path6 from "path";
|
|
9
9
|
import { execa as execa2 } from "execa";
|
|
10
|
-
import
|
|
10
|
+
import fs6 from "fs-extra";
|
|
11
11
|
import prompts3 from "prompts";
|
|
12
12
|
|
|
13
|
-
// src/utils/
|
|
13
|
+
// src/utils/copy-template.ts
|
|
14
14
|
import path from "path";
|
|
15
|
+
import fs from "fs-extra";
|
|
16
|
+
var SKIP_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", "dist"]);
|
|
17
|
+
async function copyLocalTemplate(srcDir, targetDir) {
|
|
18
|
+
await fs.ensureDir(targetDir);
|
|
19
|
+
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
if (SKIP_NAMES.has(entry.name)) continue;
|
|
22
|
+
const src = path.join(srcDir, entry.name);
|
|
23
|
+
const dest = path.join(targetDir, entry.name);
|
|
24
|
+
if (entry.isDirectory()) {
|
|
25
|
+
await copyLocalTemplate(src, dest);
|
|
26
|
+
} else if (entry.isSymbolicLink()) {
|
|
27
|
+
const linkTarget = await fs.readlink(src);
|
|
28
|
+
await fs.symlink(linkTarget, dest);
|
|
29
|
+
} else {
|
|
30
|
+
await fs.copy(src, dest);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/utils/download-template.ts
|
|
36
|
+
import path2 from "path";
|
|
15
37
|
import { tmpdir } from "os";
|
|
16
38
|
import { randomUUID } from "crypto";
|
|
17
39
|
import { execa } from "execa";
|
|
18
|
-
import
|
|
40
|
+
import fs2 from "fs-extra";
|
|
19
41
|
function parseRepo(repo) {
|
|
20
42
|
const [repoPart, branch] = repo.split("#");
|
|
21
43
|
let url;
|
|
@@ -29,8 +51,8 @@ function parseRepo(repo) {
|
|
|
29
51
|
return { url, branch: branch || void 0 };
|
|
30
52
|
}
|
|
31
53
|
async function gitClone(url, branch, targetDir) {
|
|
32
|
-
const tmpDir =
|
|
33
|
-
await
|
|
54
|
+
const tmpDir = path2.join(tmpdir(), `lightfish-template-${randomUUID()}`);
|
|
55
|
+
await fs2.ensureDir(tmpDir);
|
|
34
56
|
const args = ["clone", "--depth", "1"];
|
|
35
57
|
if (branch) {
|
|
36
58
|
args.push("--branch", branch);
|
|
@@ -39,30 +61,30 @@ async function gitClone(url, branch, targetDir) {
|
|
|
39
61
|
try {
|
|
40
62
|
await execa("git", args, { stdio: "inherit" });
|
|
41
63
|
} catch (error) {
|
|
42
|
-
await
|
|
64
|
+
await fs2.remove(tmpDir).catch(() => {
|
|
43
65
|
});
|
|
44
66
|
const detail = error instanceof Error ? error.message : String(error);
|
|
45
67
|
throw new Error(`Git \u514B\u9686\u5931\u8D25\uFF08${url}\uFF09\uFF1A${detail}`);
|
|
46
68
|
}
|
|
47
|
-
const entries = await
|
|
69
|
+
const entries = await fs2.readdir(tmpDir);
|
|
48
70
|
for (const entry of entries) {
|
|
49
71
|
if (entry === ".git") continue;
|
|
50
|
-
const src =
|
|
51
|
-
const dest =
|
|
52
|
-
await
|
|
72
|
+
const src = path2.join(tmpDir, entry);
|
|
73
|
+
const dest = path2.join(targetDir, entry);
|
|
74
|
+
await fs2.move(src, dest, { overwrite: true });
|
|
53
75
|
}
|
|
54
|
-
await
|
|
76
|
+
await fs2.remove(tmpDir).catch(() => {
|
|
55
77
|
});
|
|
56
78
|
}
|
|
57
79
|
async function downloadTemplate(repo, targetDir) {
|
|
58
|
-
await
|
|
80
|
+
await fs2.ensureDir(targetDir);
|
|
59
81
|
const { url, branch } = parseRepo(repo);
|
|
60
82
|
console.log(`\u6B63\u5728\u62C9\u53D6\u6A21\u677F: ${url}${branch ? ` (\u5206\u652F: ${branch})` : ""}`);
|
|
61
83
|
await gitClone(url, branch, targetDir);
|
|
62
84
|
console.log("\u6A21\u677F\u62C9\u53D6\u5B8C\u6210");
|
|
63
|
-
const gitDir =
|
|
64
|
-
if (await
|
|
65
|
-
await
|
|
85
|
+
const gitDir = path2.join(targetDir, ".git");
|
|
86
|
+
if (await fs2.pathExists(gitDir)) {
|
|
87
|
+
await fs2.remove(gitDir);
|
|
66
88
|
console.log("\u5DF2\u5220\u9664 .git \u76EE\u5F55");
|
|
67
89
|
}
|
|
68
90
|
}
|
|
@@ -86,26 +108,26 @@ function installCommand(packageManager) {
|
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
// src/utils/project.ts
|
|
89
|
-
import
|
|
90
|
-
import
|
|
111
|
+
import path3 from "path";
|
|
112
|
+
import fs3 from "fs-extra";
|
|
91
113
|
import prompts from "prompts";
|
|
92
114
|
function normalizeProjectName(name) {
|
|
93
115
|
return name.trim();
|
|
94
116
|
}
|
|
95
117
|
function resolveTargetDir(cwd, projectName) {
|
|
96
|
-
return
|
|
118
|
+
return path3.resolve(cwd, projectName);
|
|
97
119
|
}
|
|
98
120
|
async function assertCanCreateProject(targetDir, yes) {
|
|
99
|
-
if (!await
|
|
121
|
+
if (!await fs3.pathExists(targetDir)) {
|
|
100
122
|
return true;
|
|
101
123
|
}
|
|
102
|
-
const files = await
|
|
124
|
+
const files = await fs3.readdir(targetDir);
|
|
103
125
|
if (files.length === 0) {
|
|
104
126
|
return true;
|
|
105
127
|
}
|
|
106
128
|
if (yes) {
|
|
107
|
-
await
|
|
108
|
-
await
|
|
129
|
+
await fs3.remove(targetDir);
|
|
130
|
+
await fs3.ensureDir(targetDir);
|
|
109
131
|
return true;
|
|
110
132
|
}
|
|
111
133
|
const { overwrite } = await prompts(
|
|
@@ -124,8 +146,8 @@ async function assertCanCreateProject(targetDir, yes) {
|
|
|
124
146
|
if (!overwrite) {
|
|
125
147
|
return false;
|
|
126
148
|
}
|
|
127
|
-
await
|
|
128
|
-
await
|
|
149
|
+
await fs3.remove(targetDir);
|
|
150
|
+
await fs3.ensureDir(targetDir);
|
|
129
151
|
return true;
|
|
130
152
|
}
|
|
131
153
|
function toPackageName(projectName) {
|
|
@@ -133,8 +155,8 @@ function toPackageName(projectName) {
|
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
// src/utils/replace-token.ts
|
|
136
|
-
import
|
|
137
|
-
import
|
|
158
|
+
import path4 from "path";
|
|
159
|
+
import fs4 from "fs-extra";
|
|
138
160
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
139
161
|
".git",
|
|
140
162
|
"node_modules",
|
|
@@ -166,22 +188,22 @@ var SKIP_EXTS = /* @__PURE__ */ new Set([
|
|
|
166
188
|
".lock"
|
|
167
189
|
]);
|
|
168
190
|
async function replaceToken(targetDir, token, value) {
|
|
169
|
-
const entries = await
|
|
191
|
+
const entries = await fs4.readdir(targetDir);
|
|
170
192
|
for (const entry of entries) {
|
|
171
|
-
const fullPath =
|
|
172
|
-
const stat = await
|
|
193
|
+
const fullPath = path4.join(targetDir, entry);
|
|
194
|
+
const stat = await fs4.stat(fullPath);
|
|
173
195
|
if (stat.isDirectory()) {
|
|
174
196
|
if (SKIP_DIRS.has(entry)) continue;
|
|
175
197
|
await replaceToken(fullPath, token, value);
|
|
176
198
|
continue;
|
|
177
199
|
}
|
|
178
|
-
const ext =
|
|
200
|
+
const ext = path4.extname(entry).toLowerCase();
|
|
179
201
|
if (SKIP_EXTS.has(ext)) continue;
|
|
180
202
|
try {
|
|
181
|
-
const content = await
|
|
203
|
+
const content = await fs4.readFile(fullPath, "utf8");
|
|
182
204
|
if (content.includes(token)) {
|
|
183
205
|
const replaced = content.replaceAll(token, value);
|
|
184
|
-
await
|
|
206
|
+
await fs4.writeFile(fullPath, replaced, "utf8");
|
|
185
207
|
console.log(` [replace] ${entry}`);
|
|
186
208
|
}
|
|
187
209
|
} catch {
|
|
@@ -197,6 +219,7 @@ var BUILTIN_TEMPLATES = [
|
|
|
197
219
|
{
|
|
198
220
|
id: "react-vite",
|
|
199
221
|
label: "React + Vite",
|
|
222
|
+
localDir: "react-vite",
|
|
200
223
|
repo: "QGtiger/template-vite-server#main"
|
|
201
224
|
}
|
|
202
225
|
];
|
|
@@ -208,6 +231,21 @@ function listTemplateIds() {
|
|
|
208
231
|
return BUILTIN_TEMPLATES.map((entry) => entry.id);
|
|
209
232
|
}
|
|
210
233
|
|
|
234
|
+
// src/utils/builtin-template-dir.ts
|
|
235
|
+
import path5 from "path";
|
|
236
|
+
import fs5 from "fs";
|
|
237
|
+
import { fileURLToPath } from "url";
|
|
238
|
+
var dirname = path5.dirname(fileURLToPath(import.meta.url));
|
|
239
|
+
function resolveBuiltinTemplateDir(localDir) {
|
|
240
|
+
const candidates = [
|
|
241
|
+
// 产物模式:dirname=dist -> dist/templates/<localDir>
|
|
242
|
+
path5.resolve(dirname, "templates", localDir),
|
|
243
|
+
// 源码模式:dirname=src/utils -> packages/cli/templates/<localDir>
|
|
244
|
+
path5.resolve(dirname, "..", "..", "templates", localDir)
|
|
245
|
+
];
|
|
246
|
+
return candidates.find((candidate) => fs5.existsSync(candidate));
|
|
247
|
+
}
|
|
248
|
+
|
|
211
249
|
// src/utils/resolve-template.ts
|
|
212
250
|
async function promptTemplateId() {
|
|
213
251
|
const { templateId } = await prompts2(
|
|
@@ -231,41 +269,59 @@ async function promptTemplateId() {
|
|
|
231
269
|
}
|
|
232
270
|
return templateId;
|
|
233
271
|
}
|
|
234
|
-
async function
|
|
235
|
-
if (options.templateRepo) {
|
|
236
|
-
return options.templateRepo;
|
|
237
|
-
}
|
|
272
|
+
async function selectEntry(options) {
|
|
238
273
|
if (options.template) {
|
|
239
274
|
const entry2 = getTemplateById(options.template);
|
|
240
275
|
if (!entry2) {
|
|
241
276
|
throw new Error(`\u672A\u77E5\u6A21\u677F\uFF1A${options.template}\u3002\u53EF\u9009\uFF1A${listTemplateIds().join("\u3001")}`);
|
|
242
277
|
}
|
|
243
|
-
return entry2
|
|
278
|
+
return entry2;
|
|
279
|
+
}
|
|
280
|
+
if (BUILTIN_TEMPLATES.length === 1) {
|
|
281
|
+
const entry2 = getTemplateById(DEFAULT_TEMPLATE_ID ?? BUILTIN_TEMPLATES[0].id);
|
|
282
|
+
if (!entry2) {
|
|
283
|
+
throw new Error("\u5185\u7F6E\u6A21\u677F\u5217\u8868\u4E3A\u7A7A");
|
|
284
|
+
}
|
|
285
|
+
return entry2;
|
|
244
286
|
}
|
|
245
287
|
if (options.yes) {
|
|
246
288
|
const entry2 = getTemplateById(DEFAULT_TEMPLATE_ID ?? BUILTIN_TEMPLATES[0].id);
|
|
247
289
|
if (!entry2) {
|
|
248
290
|
throw new Error("\u5185\u7F6E\u6A21\u677F\u5217\u8868\u4E3A\u7A7A");
|
|
249
291
|
}
|
|
250
|
-
return entry2
|
|
292
|
+
return entry2;
|
|
251
293
|
}
|
|
252
294
|
const templateId = await promptTemplateId();
|
|
253
295
|
const entry = getTemplateById(templateId);
|
|
254
296
|
if (!entry) {
|
|
255
297
|
throw new Error(`\u672A\u77E5\u6A21\u677F\uFF1A${templateId}`);
|
|
256
298
|
}
|
|
257
|
-
return entry
|
|
299
|
+
return entry;
|
|
300
|
+
}
|
|
301
|
+
async function resolveTemplate(options) {
|
|
302
|
+
const entry = await selectEntry(options);
|
|
303
|
+
if (!options.remote && entry.localDir) {
|
|
304
|
+
const dir = resolveBuiltinTemplateDir(entry.localDir);
|
|
305
|
+
if (dir) {
|
|
306
|
+
return { type: "local", dir, id: entry.id };
|
|
307
|
+
}
|
|
308
|
+
console.warn(`[template] \u5185\u7F6E\u6A21\u677F\u526F\u672C\u7F3A\u5931\uFF08${entry.localDir}\uFF09\uFF0C\u56DE\u9000\u5230 git \u62C9\u53D6\u3002`);
|
|
309
|
+
}
|
|
310
|
+
if (!entry.repo) {
|
|
311
|
+
throw new Error(`\u6A21\u677F ${entry.id} \u672A\u914D\u7F6E git \u6E90\uFF0C\u4E14\u672C\u5730\u526F\u672C\u4E0D\u53EF\u7528`);
|
|
312
|
+
}
|
|
313
|
+
return { type: "git", repo: entry.repo, id: entry.id };
|
|
258
314
|
}
|
|
259
315
|
|
|
260
316
|
// src/commands/create.ts
|
|
261
317
|
async function patchPackageJsonName(targetDir, projectName) {
|
|
262
|
-
const packageJsonPath =
|
|
263
|
-
if (!await
|
|
318
|
+
const packageJsonPath = path6.join(targetDir, "package.json");
|
|
319
|
+
if (!await fs6.pathExists(packageJsonPath)) {
|
|
264
320
|
throw new Error("\u6A21\u677F\u7F3A\u5C11 package.json");
|
|
265
321
|
}
|
|
266
|
-
const packageJson = await
|
|
322
|
+
const packageJson = await fs6.readJson(packageJsonPath);
|
|
267
323
|
packageJson.name = toPackageName(projectName);
|
|
268
|
-
await
|
|
324
|
+
await fs6.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
269
325
|
}
|
|
270
326
|
async function resolveCreateOptions(options) {
|
|
271
327
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -305,9 +361,9 @@ async function resolveCreateOptions(options) {
|
|
|
305
361
|
if (!isPackageManager(packageManager)) {
|
|
306
362
|
throw new Error(`\u4E0D\u652F\u6301\u7684\u5305\u7BA1\u7406\u5668\uFF1A${String(packageManager)}`);
|
|
307
363
|
}
|
|
308
|
-
const
|
|
364
|
+
const template = await resolveTemplate({
|
|
309
365
|
template: options.template,
|
|
310
|
-
|
|
366
|
+
remote: options.remote,
|
|
311
367
|
yes: options.yes
|
|
312
368
|
});
|
|
313
369
|
const yesMode = options.yes === true;
|
|
@@ -316,7 +372,7 @@ async function resolveCreateOptions(options) {
|
|
|
316
372
|
projectName,
|
|
317
373
|
targetDir: resolveTargetDir(cwd, projectName),
|
|
318
374
|
packageManager,
|
|
319
|
-
|
|
375
|
+
template,
|
|
320
376
|
install: options.skipInstall ? false : options.install ?? (yesMode ? true : answers.install ?? false),
|
|
321
377
|
git: options.git ?? (yesMode ? true : answers.git ?? false)
|
|
322
378
|
};
|
|
@@ -340,15 +396,25 @@ async function maybeInitGit(options) {
|
|
|
340
396
|
stdio: "inherit"
|
|
341
397
|
});
|
|
342
398
|
}
|
|
399
|
+
async function writeTemplate(template, targetDir) {
|
|
400
|
+
if (template.type === "local") {
|
|
401
|
+
console.log(`[create] \u4F7F\u7528\u5185\u7F6E\u6A21\u677F\uFF1A${template.id}`);
|
|
402
|
+
await copyLocalTemplate(template.dir, targetDir);
|
|
403
|
+
console.log("[create] \u5185\u7F6E\u6A21\u677F\u62F7\u8D1D\u5B8C\u6210");
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
console.log(`[create] \u4F7F\u7528 git \u6A21\u677F\uFF1A${template.id}`);
|
|
407
|
+
await downloadTemplate(template.repo, targetDir);
|
|
408
|
+
}
|
|
343
409
|
async function createProject(options = {}) {
|
|
344
410
|
const resolvedOptions = await resolveCreateOptions(options);
|
|
345
411
|
const canCreate = await assertCanCreateProject(resolvedOptions.targetDir, options.yes);
|
|
346
412
|
if (!canCreate) {
|
|
347
413
|
throw new Error("\u5DF2\u53D6\u6D88\u521B\u5EFA\u9879\u76EE");
|
|
348
414
|
}
|
|
349
|
-
console.log("[create] \u5F00\u59CB\
|
|
350
|
-
await
|
|
351
|
-
console.log("[create] \u6A21\u677F\
|
|
415
|
+
console.log("[create] \u5F00\u59CB\u5199\u5165\u6A21\u677F...");
|
|
416
|
+
await writeTemplate(resolvedOptions.template, resolvedOptions.targetDir);
|
|
417
|
+
console.log("[create] \u6A21\u677F\u5199\u5165\u5B8C\u6210");
|
|
352
418
|
console.log("[create] \u5F00\u59CB\u4FEE\u6539 package.json...");
|
|
353
419
|
await patchPackageJsonName(resolvedOptions.targetDir, resolvedOptions.projectName);
|
|
354
420
|
console.log("[create] package.json \u4FEE\u6539\u5B8C\u6210");
|
|
@@ -364,36 +430,415 @@ async function createProject(options = {}) {
|
|
|
364
430
|
return resolvedOptions;
|
|
365
431
|
}
|
|
366
432
|
|
|
433
|
+
// src/commands/publish.ts
|
|
434
|
+
import fs10 from "fs";
|
|
435
|
+
import path10 from "path";
|
|
436
|
+
|
|
437
|
+
// src/utils/env.ts
|
|
438
|
+
import fs7 from "fs";
|
|
439
|
+
import path7 from "path";
|
|
440
|
+
import { parse } from "dotenv";
|
|
441
|
+
var DEFAULT_BASE_ROOT = "https://lf-frontend.oss-cn-beijing.aliyuncs.com";
|
|
442
|
+
function loadDotenv({ cwd, envPath }) {
|
|
443
|
+
let merged;
|
|
444
|
+
if (envPath) {
|
|
445
|
+
merged = readEnvFile(path7.resolve(cwd, envPath));
|
|
446
|
+
} else {
|
|
447
|
+
const base = readEnvFile(path7.resolve(cwd, ".env"));
|
|
448
|
+
const local = readEnvFile(path7.resolve(cwd, ".env.local"));
|
|
449
|
+
merged = { ...base, ...local };
|
|
450
|
+
}
|
|
451
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
452
|
+
if (process.env[key] === void 0) {
|
|
453
|
+
process.env[key] = value;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function readEnvFile(file) {
|
|
458
|
+
if (!fs7.existsSync(file)) return {};
|
|
459
|
+
return parse(fs7.readFileSync(file, "utf8"));
|
|
460
|
+
}
|
|
461
|
+
function getRequiredEnvNames({ notify, serverDetected }) {
|
|
462
|
+
const required = ["OSS_REGION", "OSS_BUCKET", "OSS_ACCESS_KEYID", "OSS_ACCESS_KEYSECRECT"];
|
|
463
|
+
if (notify) required.push("DEPLOY_API_URL");
|
|
464
|
+
if (serverDetected) required.push("DATABASE_URL");
|
|
465
|
+
return required;
|
|
466
|
+
}
|
|
467
|
+
function resolvePublishEnv(options, env = process.env) {
|
|
468
|
+
const required = getRequiredEnvNames(options);
|
|
469
|
+
const missing = required.filter((name) => !env[name]);
|
|
470
|
+
if (missing.length > 0) {
|
|
471
|
+
throw new Error(
|
|
472
|
+
`\u7F3A\u5C11\u5FC5\u9700\u73AF\u5883\u53D8\u91CF:${missing.join(", ")}(\u8BF7\u5728 .env / .env.local \u6216\u73AF\u5883\u53D8\u91CF\u4E2D\u914D\u7F6E)`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
ossRegion: env.OSS_REGION,
|
|
477
|
+
ossBucket: env.OSS_BUCKET,
|
|
478
|
+
ossAccessKeyId: env.OSS_ACCESS_KEYID,
|
|
479
|
+
// 注意:变量名沿用 CI secrets 的拼写(SECRECT),保持 .env 兼容
|
|
480
|
+
ossAccessKeySecret: env.OSS_ACCESS_KEYSECRECT,
|
|
481
|
+
deployApiUrl: options.notify ? env.DEPLOY_API_URL : void 0,
|
|
482
|
+
databaseUrl: env.DATABASE_URL,
|
|
483
|
+
baseRoot: env.BASE_ROOT || DEFAULT_BASE_ROOT
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/utils/build.ts
|
|
488
|
+
import fs8 from "fs";
|
|
489
|
+
import path8 from "path";
|
|
490
|
+
import { execa as execa3 } from "execa";
|
|
491
|
+
var SERVER_CONFIG_FILES = [
|
|
492
|
+
"lightfish-server.config.js",
|
|
493
|
+
"lightfish-server.config.ts",
|
|
494
|
+
"lightfish-server.config.mjs",
|
|
495
|
+
"lightfish-server.config.cjs"
|
|
496
|
+
];
|
|
497
|
+
var VITE_CONFIG_FILES = [
|
|
498
|
+
"vite.config.ts",
|
|
499
|
+
"vite.config.js",
|
|
500
|
+
"vite.config.mts",
|
|
501
|
+
"vite.config.mjs",
|
|
502
|
+
"vite.config.cjs"
|
|
503
|
+
];
|
|
504
|
+
function readPackageJson(cwd) {
|
|
505
|
+
const file = path8.join(cwd, "package.json");
|
|
506
|
+
if (!fs8.existsSync(file)) return void 0;
|
|
507
|
+
return JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
508
|
+
}
|
|
509
|
+
function hasVite(cwd) {
|
|
510
|
+
if (VITE_CONFIG_FILES.some((f) => fs8.existsSync(path8.join(cwd, f)))) return true;
|
|
511
|
+
const pkg = readPackageJson(cwd);
|
|
512
|
+
if (pkg) {
|
|
513
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
514
|
+
if ("vite" in deps) return true;
|
|
515
|
+
}
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
function detectBuild(cwd) {
|
|
519
|
+
return {
|
|
520
|
+
vite: hasVite(cwd),
|
|
521
|
+
server: SERVER_CONFIG_FILES.some((f) => fs8.existsSync(path8.join(cwd, f)))
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
async function runBuildSteps(options) {
|
|
525
|
+
const detection = detectBuild(options.cwd);
|
|
526
|
+
if (!detection.vite && !detection.server) {
|
|
527
|
+
throw new Error("\u672A\u68C0\u6D4B\u5230 Vite \u6216 lightfish-server,\u65E0\u6CD5\u6784\u5EFA");
|
|
528
|
+
}
|
|
529
|
+
const execOptions = {
|
|
530
|
+
cwd: options.cwd,
|
|
531
|
+
preferLocal: true,
|
|
532
|
+
stdio: "inherit",
|
|
533
|
+
env: { ...process.env, ...options.env }
|
|
534
|
+
};
|
|
535
|
+
if (detection.vite) {
|
|
536
|
+
await execa3("vite", ["build"], execOptions);
|
|
537
|
+
}
|
|
538
|
+
if (detection.server) {
|
|
539
|
+
await execa3("lightfish-server", ["build"], execOptions);
|
|
540
|
+
await execa3("lightfish-server", ["db", "push"], execOptions);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/utils/oss.ts
|
|
545
|
+
import OSS from "ali-oss";
|
|
546
|
+
import fs9 from "fs";
|
|
547
|
+
import path9 from "path";
|
|
548
|
+
function withPrefix(prefix, key) {
|
|
549
|
+
const normalized = prefix.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
550
|
+
if (!normalized) return key;
|
|
551
|
+
return `${normalized}/${key}`;
|
|
552
|
+
}
|
|
553
|
+
function walkDir(dir, base = "") {
|
|
554
|
+
if (!fs9.existsSync(dir)) return [];
|
|
555
|
+
const entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
556
|
+
const files = [];
|
|
557
|
+
for (const entry of entries) {
|
|
558
|
+
const fullPath = path9.join(dir, entry.name);
|
|
559
|
+
const key = base ? `${base}/${entry.name}` : entry.name;
|
|
560
|
+
if (entry.isDirectory()) {
|
|
561
|
+
files.push(...walkDir(fullPath, key));
|
|
562
|
+
} else {
|
|
563
|
+
files.push({ localPath: fullPath, key });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return files;
|
|
567
|
+
}
|
|
568
|
+
async function uploadDist(options, client) {
|
|
569
|
+
const oss = client ?? createClient(options);
|
|
570
|
+
const files = walkDir(options.distDir);
|
|
571
|
+
console.log(`[publish] Found ${files.length} files to upload`);
|
|
572
|
+
const results = await Promise.all(
|
|
573
|
+
files.map(({ localPath, key }) => {
|
|
574
|
+
const objectKey = withPrefix(options.objectPrefix, key);
|
|
575
|
+
return oss.put(objectKey, localPath).then((res) => ({ key: objectKey, ok: res.res.status === 200 })).catch((err) => {
|
|
576
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
577
|
+
console.error(`[publish] ERROR ${objectKey}: ${message}`);
|
|
578
|
+
return { key: objectKey, ok: false };
|
|
579
|
+
});
|
|
580
|
+
})
|
|
581
|
+
);
|
|
582
|
+
for (const r of results.filter((r2) => r2.ok)) {
|
|
583
|
+
console.log(`[publish] ok ${r.key}`);
|
|
584
|
+
}
|
|
585
|
+
const ok = results.filter((r) => r.ok).length;
|
|
586
|
+
const fail = results.filter((r) => !r.ok).length;
|
|
587
|
+
console.log(`[publish] Done: ${ok} succeeded, ${fail} failed`);
|
|
588
|
+
return { ok, fail };
|
|
589
|
+
}
|
|
590
|
+
function createClient(options) {
|
|
591
|
+
return new OSS({
|
|
592
|
+
region: options.region,
|
|
593
|
+
accessKeyId: options.accessKeyId,
|
|
594
|
+
accessKeySecret: options.accessKeySecret,
|
|
595
|
+
bucket: options.bucket
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/utils/notify.ts
|
|
600
|
+
async function notifyDeploy(options) {
|
|
601
|
+
const fetchFn = options.fetchImpl ?? fetch;
|
|
602
|
+
const body = JSON.stringify(options.payload);
|
|
603
|
+
console.log(`[publish] POST ${options.apiUrl}`);
|
|
604
|
+
console.log(`[publish] body: ${body}`);
|
|
605
|
+
let res;
|
|
606
|
+
try {
|
|
607
|
+
res = await fetchFn(options.apiUrl, {
|
|
608
|
+
method: "POST",
|
|
609
|
+
headers: { "Content-Type": "application/json" },
|
|
610
|
+
body
|
|
611
|
+
});
|
|
612
|
+
} catch (err) {
|
|
613
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
614
|
+
throw new Error(`\u90E8\u7F72 API \u8BF7\u6C42\u5931\u8D25:${message}`);
|
|
615
|
+
}
|
|
616
|
+
if (!res.ok) {
|
|
617
|
+
throw new Error(`\u90E8\u7F72 API \u8FD4\u56DE HTTP ${res.status}`);
|
|
618
|
+
}
|
|
619
|
+
let data;
|
|
620
|
+
try {
|
|
621
|
+
data = await res.json();
|
|
622
|
+
} catch {
|
|
623
|
+
throw new Error("\u90E8\u7F72 API \u54CD\u5E94\u975E JSON");
|
|
624
|
+
}
|
|
625
|
+
console.log(`[publish] response: ${JSON.stringify(data)}`);
|
|
626
|
+
if (typeof data?.success !== "boolean" || data.success !== true) {
|
|
627
|
+
throw new Error("\u90E8\u7F72 API \u672A\u8FD4\u56DE success=true");
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/commands/publish.ts
|
|
632
|
+
function generateVersion(date = /* @__PURE__ */ new Date()) {
|
|
633
|
+
return date.toISOString().replace(/[-T:.Z]/g, "").slice(0, 14);
|
|
634
|
+
}
|
|
635
|
+
function readPackageJsonName(cwd) {
|
|
636
|
+
const file = path10.join(cwd, "package.json");
|
|
637
|
+
if (!fs10.existsSync(file)) return void 0;
|
|
638
|
+
const pkg = JSON.parse(fs10.readFileSync(file, "utf8"));
|
|
639
|
+
return pkg.name;
|
|
640
|
+
}
|
|
641
|
+
function resolveAppName(options) {
|
|
642
|
+
if (options.app) return options.app;
|
|
643
|
+
const pkgName = readPackageJsonName(options.cwd);
|
|
644
|
+
if (pkgName) {
|
|
645
|
+
return pkgName.startsWith("@") ? pkgName.replace(/^@[^/]+\//, "") : pkgName;
|
|
646
|
+
}
|
|
647
|
+
throw new Error("\u65E0\u6CD5\u786E\u5B9A APP_NAME:\u8BF7\u901A\u8FC7 --app \u6307\u5B9A,\u6216\u786E\u4FDD package.json \u542B name \u5B57\u6BB5");
|
|
648
|
+
}
|
|
649
|
+
function assertNumericVersion(version) {
|
|
650
|
+
if (!/^\d+$/.test(version)) {
|
|
651
|
+
throw new Error(`version \u5FC5\u987B\u4E3A\u7EAF\u6570\u5B57(\u4E0E\u90E8\u7F72 API \u7684 number \u5951\u7EA6\u4E00\u81F4),\u6536\u5230:${version}`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
function resolveOptions(options) {
|
|
655
|
+
const cwd = options.cwd ?? process.cwd();
|
|
656
|
+
const appName = resolveAppName({ cwd, app: options.app });
|
|
657
|
+
const version = options.version ?? generateVersion();
|
|
658
|
+
assertNumericVersion(version);
|
|
659
|
+
const distDir = path10.resolve(cwd, options.dist ?? "dist");
|
|
660
|
+
const notify = !options.noNotify;
|
|
661
|
+
const baseRoot = process.env.BASE_ROOT || DEFAULT_BASE_ROOT;
|
|
662
|
+
const root = baseRoot.replace(/\/+$/, "");
|
|
663
|
+
const viteBaseUrl = root ? `${root}/${appName}/${version}/` : `/${appName}/${version}/`;
|
|
664
|
+
const objectPrefix = `${appName}/${version}`;
|
|
665
|
+
const ossIndexUrl = `${root}/${appName}/${version}/index.html`;
|
|
666
|
+
return {
|
|
667
|
+
cwd,
|
|
668
|
+
appName,
|
|
669
|
+
version,
|
|
670
|
+
distDir,
|
|
671
|
+
baseRoot,
|
|
672
|
+
root,
|
|
673
|
+
viteBaseUrl,
|
|
674
|
+
objectPrefix,
|
|
675
|
+
ossIndexUrl,
|
|
676
|
+
notify,
|
|
677
|
+
skipBuild: options.skipBuild ?? false,
|
|
678
|
+
note: options.note
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function printBuildPlanDryRun(detection) {
|
|
682
|
+
if (!detection.vite && !detection.server) {
|
|
683
|
+
console.log("[publish] (dry-run) \u672A\u68C0\u6D4B\u5230 Vite \u6216 lightfish-server,\u6784\u5EFA\u5C06\u5931\u8D25");
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
console.log("[publish] (dry-run) \u5C06\u6267\u884C\u6784\u5EFA:");
|
|
687
|
+
if (detection.vite) console.log("[publish] (dry-run) vite build");
|
|
688
|
+
if (detection.server) {
|
|
689
|
+
console.log("[publish] (dry-run) lightfish-server build");
|
|
690
|
+
console.log("[publish] (dry-run) lightfish-server db push");
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function printEnvDryRun(serverDetected, notify) {
|
|
694
|
+
const required = getRequiredEnvNames({ notify, serverDetected });
|
|
695
|
+
console.log("[publish] (dry-run) \u73AF\u5883\u53D8\u91CF\u9884\u68C0:");
|
|
696
|
+
for (const name of required) {
|
|
697
|
+
const present = process.env[name] !== void 0 && process.env[name] !== "";
|
|
698
|
+
console.log(`[publish] (dry-run) ${present ? "\u2713" : "\u2717"} ${name}`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
function printUploadPlanDryRun(options) {
|
|
702
|
+
const files = walkDir(options.distDir);
|
|
703
|
+
if (files.length === 0) {
|
|
704
|
+
console.log(`[publish] (dry-run) \u4EA7\u7269\u76EE\u5F55 ${options.distDir} \u4E3A\u7A7A\u6216\u4E0D\u5B58\u5728(\u6784\u5EFA\u540E\u5C06\u751F\u6210)`);
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
console.log(`[publish] (dry-run) \u5C06\u4E0A\u4F20 ${files.length} \u4E2A\u6587\u4EF6\u5230 OSS \u524D\u7F00:${options.objectPrefix}/`);
|
|
708
|
+
for (const { key } of files) {
|
|
709
|
+
console.log(`[publish] (dry-run) ${withPrefix(options.objectPrefix, key)}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function buildNotifyPayload(params) {
|
|
713
|
+
const payload = {
|
|
714
|
+
id: params.appName,
|
|
715
|
+
version: Number(params.version),
|
|
716
|
+
ossIndexUrl: params.ossIndexUrl,
|
|
717
|
+
domain: `${params.appName}.lightfish.top`
|
|
718
|
+
};
|
|
719
|
+
if (params.note) payload.note = params.note;
|
|
720
|
+
return payload;
|
|
721
|
+
}
|
|
722
|
+
function printNotifyPlanDryRun(options, pubEnv) {
|
|
723
|
+
if (!options.notify) {
|
|
724
|
+
console.log("[publish] (dry-run) \u901A\u77E5\u5DF2\u8DF3\u8FC7(--no-notify)");
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const payload = buildNotifyPayload({
|
|
728
|
+
appName: options.appName,
|
|
729
|
+
version: options.version,
|
|
730
|
+
ossIndexUrl: options.ossIndexUrl,
|
|
731
|
+
note: options.note
|
|
732
|
+
});
|
|
733
|
+
console.log("[publish] (dry-run) \u5C06 POST \u901A\u77E5:");
|
|
734
|
+
console.log(`[publish] (dry-run) url: ${pubEnv.deployApiUrl ?? "(\u672A\u914D\u7F6E DEPLOY_API_URL)"}`);
|
|
735
|
+
console.log(`[publish] (dry-run) body: ${JSON.stringify(payload)}`);
|
|
736
|
+
}
|
|
737
|
+
async function runPublish(options = {}) {
|
|
738
|
+
const cwd = options.cwd ?? process.cwd();
|
|
739
|
+
loadDotenv({ cwd, envPath: options.envPath });
|
|
740
|
+
const resolved = resolveOptions(options);
|
|
741
|
+
console.log(`[publish] app=${resolved.appName} version=${resolved.version} dist=${resolved.distDir}`);
|
|
742
|
+
let serverDetected = false;
|
|
743
|
+
if (!resolved.skipBuild) {
|
|
744
|
+
const detection = detectBuild(resolved.cwd);
|
|
745
|
+
serverDetected = detection.server;
|
|
746
|
+
if (options.dryRun) {
|
|
747
|
+
printBuildPlanDryRun(detection);
|
|
748
|
+
} else {
|
|
749
|
+
console.log("[publish] \u5F00\u59CB\u6784\u5EFA...");
|
|
750
|
+
const buildEnv = {
|
|
751
|
+
VITE_BASE_URL: resolved.viteBaseUrl,
|
|
752
|
+
BASE_ROOT: resolved.baseRoot,
|
|
753
|
+
APP_NAME: resolved.appName,
|
|
754
|
+
BUILD_VERSION: resolved.version
|
|
755
|
+
};
|
|
756
|
+
if (process.env.DATABASE_URL) buildEnv.DATABASE_URL = process.env.DATABASE_URL;
|
|
757
|
+
await runBuildSteps({
|
|
758
|
+
cwd: resolved.cwd,
|
|
759
|
+
env: buildEnv
|
|
760
|
+
});
|
|
761
|
+
console.log("[publish] \u6784\u5EFA\u5B8C\u6210");
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const pubEnv = options.dryRun ? softResolveEnv(resolved.notify, serverDetected) : resolvePublishEnv({ notify: resolved.notify, serverDetected });
|
|
765
|
+
if (options.dryRun) {
|
|
766
|
+
printEnvDryRun(serverDetected, resolved.notify);
|
|
767
|
+
printUploadPlanDryRun(resolved);
|
|
768
|
+
printNotifyPlanDryRun(resolved, pubEnv);
|
|
769
|
+
console.log("[publish] (dry-run) \u5B8C\u6210,\u672A\u6267\u884C\u4EFB\u4F55\u526F\u4F5C\u7528");
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
if (!fs10.existsSync(resolved.distDir)) {
|
|
773
|
+
throw new Error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728:${resolved.distDir}`);
|
|
774
|
+
}
|
|
775
|
+
console.log("[publish] \u5F00\u59CB\u4E0A\u4F20 OSS...");
|
|
776
|
+
const { fail } = await uploadDist({
|
|
777
|
+
region: pubEnv.ossRegion,
|
|
778
|
+
bucket: pubEnv.ossBucket,
|
|
779
|
+
accessKeyId: pubEnv.ossAccessKeyId,
|
|
780
|
+
accessKeySecret: pubEnv.ossAccessKeySecret,
|
|
781
|
+
objectPrefix: resolved.objectPrefix,
|
|
782
|
+
distDir: resolved.distDir
|
|
783
|
+
});
|
|
784
|
+
if (fail > 0) {
|
|
785
|
+
throw new Error(`OSS \u4E0A\u4F20\u6709 ${fail} \u4E2A\u6587\u4EF6\u5931\u8D25`);
|
|
786
|
+
}
|
|
787
|
+
console.log("[publish] OSS \u4E0A\u4F20\u5B8C\u6210");
|
|
788
|
+
if (resolved.notify) {
|
|
789
|
+
console.log("[publish] \u5F00\u59CB\u901A\u77E5\u90E8\u7F72 API...");
|
|
790
|
+
const payload = buildNotifyPayload({
|
|
791
|
+
appName: resolved.appName,
|
|
792
|
+
version: resolved.version,
|
|
793
|
+
ossIndexUrl: resolved.ossIndexUrl,
|
|
794
|
+
note: resolved.note
|
|
795
|
+
});
|
|
796
|
+
await notifyDeploy({ apiUrl: pubEnv.deployApiUrl, payload });
|
|
797
|
+
console.log("[publish] \u901A\u77E5\u5B8C\u6210");
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function softResolveEnv(notify, serverDetected) {
|
|
801
|
+
return {
|
|
802
|
+
ossRegion: process.env.OSS_REGION ?? "",
|
|
803
|
+
ossBucket: process.env.OSS_BUCKET ?? "",
|
|
804
|
+
ossAccessKeyId: process.env.OSS_ACCESS_KEYID ?? "",
|
|
805
|
+
ossAccessKeySecret: process.env.OSS_ACCESS_KEYSECRECT ?? "",
|
|
806
|
+
deployApiUrl: notify ? process.env.DEPLOY_API_URL : void 0,
|
|
807
|
+
databaseUrl: process.env.DATABASE_URL,
|
|
808
|
+
baseRoot: process.env.BASE_ROOT || DEFAULT_BASE_ROOT
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
|
|
367
812
|
// src/utils/package-info.ts
|
|
368
|
-
import
|
|
369
|
-
import
|
|
370
|
-
import { fileURLToPath } from "url";
|
|
371
|
-
var
|
|
813
|
+
import fs11 from "fs";
|
|
814
|
+
import path11 from "path";
|
|
815
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
816
|
+
var dirname2 = path11.dirname(fileURLToPath2(import.meta.url));
|
|
372
817
|
function readPackageVersion() {
|
|
373
818
|
const packageJsonPathCandidates = [
|
|
374
819
|
// 源码模式:src/utils/package-info.ts -> packages/cli/package.json
|
|
375
|
-
|
|
820
|
+
path11.resolve(dirname2, "../../package.json"),
|
|
376
821
|
// 构建产物模式:dist/lightfish.js -> packages/cli/package.json
|
|
377
|
-
|
|
822
|
+
path11.resolve(dirname2, "../package.json")
|
|
378
823
|
];
|
|
379
|
-
const packageJsonPath = packageJsonPathCandidates.find((candidate) =>
|
|
824
|
+
const packageJsonPath = packageJsonPathCandidates.find((candidate) => fs11.existsSync(candidate));
|
|
380
825
|
if (!packageJsonPath) {
|
|
381
826
|
return "0.0.0";
|
|
382
827
|
}
|
|
383
|
-
const packageJson = JSON.parse(
|
|
828
|
+
const packageJson = JSON.parse(fs11.readFileSync(packageJsonPath, "utf8"));
|
|
384
829
|
return packageJson.version ?? "0.0.0";
|
|
385
830
|
}
|
|
386
831
|
|
|
387
832
|
// src/lightfish.ts
|
|
388
833
|
var cli = cac("lightfish");
|
|
389
834
|
async function runBuild() {
|
|
390
|
-
await
|
|
835
|
+
await execa4("vite", ["build"], {
|
|
391
836
|
cwd: process.cwd(),
|
|
392
837
|
preferLocal: true,
|
|
393
838
|
stdio: "inherit"
|
|
394
839
|
});
|
|
395
840
|
}
|
|
396
|
-
cli.command("create [projectName]", "\u521B\u5EFA Lightfish React Vite \u9879\u76EE").option("--pm <pm>", "\u5305\u7BA1\u7406\u5668\uFF1Anpm\u3001yarn\u3001pnpm\u3001bun", { default: "pnpm" }).option("--template <id>", "\u5185\u7F6E\u6A21\u677F id\uFF0C\u4F8B\u5982 react-vite").option("--
|
|
841
|
+
cli.command("create [projectName]", "\u521B\u5EFA Lightfish React Vite \u9879\u76EE").option("--pm <pm>", "\u5305\u7BA1\u7406\u5668\uFF1Anpm\u3001yarn\u3001pnpm\u3001bun", { default: "pnpm" }).option("--template <id>", "\u5185\u7F6E\u6A21\u677F id\uFF0C\u4F8B\u5982 react-vite").option("--remote", "\u5F3A\u5236\u8D70 git \u62C9\u53D6\u5185\u7F6E\u6A21\u677F\uFF08\u5FFD\u7565\u672C\u5730\u526F\u672C\uFF09").option("--skip-install", "\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5").option("--git", "\u521B\u5EFA\u540E\u521D\u59CB\u5316 git").option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u9009\u9879\u5E76\u8DF3\u8FC7\u786E\u8BA4").action(async (projectName, options) => {
|
|
397
842
|
try {
|
|
398
843
|
const packageManager = options.pm ?? "pnpm";
|
|
399
844
|
if (!isPackageManager(packageManager)) {
|
|
@@ -403,7 +848,7 @@ cli.command("create [projectName]", "\u521B\u5EFA Lightfish React Vite \u9879\u7
|
|
|
403
848
|
projectName,
|
|
404
849
|
packageManager,
|
|
405
850
|
template: options.template,
|
|
406
|
-
|
|
851
|
+
remote: options.remote,
|
|
407
852
|
skipInstall: options.skipInstall,
|
|
408
853
|
git: options.git,
|
|
409
854
|
yes: options.yes
|
|
@@ -424,9 +869,28 @@ cli.command("build", "\u6267\u884C Vite \u6784\u5EFA\uFF0C\u540E\u7EED\u4F1A\u53
|
|
|
424
869
|
process.exitCode = 1;
|
|
425
870
|
}
|
|
426
871
|
});
|
|
872
|
+
cli.command("publish", "\u6784\u5EFA\u5E76\u90E8\u7F72\u5230 OSS\uFF0C\u901A\u77E5\u90E8\u7F72 API").option("--app <name>", "\u8986\u76D6 APP_NAME\uFF08\u9ED8\u8BA4 package.json#name \u53BB scope\uFF09").option("--version <v>", "\u8986\u76D6 BUILD_VERSION\uFF08\u9ED8\u8BA4 UTC \u65F6\u95F4\u6233 YYYYMMDDHHmmss\uFF09").option("--dist <dir>", "\u4EA7\u7269\u76EE\u5F55", { default: "dist" }).option("--note <text>", "\u901A\u77E5 body \u7684 note \u5B57\u6BB5\uFF0C\u4E0D\u4F20\u5219\u7701\u7565").option("--env <path>", "\u6307\u5B9A .env \u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 cwd/.env + .env.local\uFF09").option("--no-build", "\u8DF3\u8FC7\u6784\u5EFA\uFF0C\u4F7F\u7528\u5DF2\u6709 dist").option("--no-notify", "\u8DF3\u8FC7\u901A\u77E5\u90E8\u7F72 API").option("--dry-run", "\u53EA\u6253\u5370\u5C06\u505A\u4EC0\u4E48\uFF0C\u4E0D\u6267\u884C").action(async (options) => {
|
|
873
|
+
try {
|
|
874
|
+
await runPublish({
|
|
875
|
+
app: options.app,
|
|
876
|
+
version: options.version,
|
|
877
|
+
dist: options.dist,
|
|
878
|
+
note: options.note,
|
|
879
|
+
envPath: options.env,
|
|
880
|
+
skipBuild: options.build === false,
|
|
881
|
+
noNotify: options.notify === false,
|
|
882
|
+
dryRun: options.dryRun
|
|
883
|
+
});
|
|
884
|
+
process.exit(0);
|
|
885
|
+
} catch (error) {
|
|
886
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
887
|
+
console.error(`\u53D1\u5E03\u5931\u8D25\uFF1A${message}`);
|
|
888
|
+
process.exit(1);
|
|
889
|
+
}
|
|
890
|
+
});
|
|
427
891
|
cli.command("", "\u663E\u793A\u5E2E\u52A9").action(() => {
|
|
428
892
|
cli.outputHelp();
|
|
429
893
|
});
|
|
430
894
|
cli.help();
|
|
431
|
-
cli.version(readPackageVersion());
|
|
895
|
+
cli.version(readPackageVersion(), "-v, --cli-version");
|
|
432
896
|
cli.parse();
|