@lightfish/cli 0.0.3 → 0.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/dist/create-lightfish.js +7 -23
- package/dist/lightfish.js +414 -32
- package/package.json +4 -2
package/dist/create-lightfish.js
CHANGED
|
@@ -14,9 +14,8 @@ import path from "path";
|
|
|
14
14
|
import { tmpdir } from "os";
|
|
15
15
|
import { randomUUID } from "crypto";
|
|
16
16
|
import { execa } from "execa";
|
|
17
|
-
import degit from "degit";
|
|
18
17
|
import fs from "fs-extra";
|
|
19
|
-
function
|
|
18
|
+
function parseRepo(repo) {
|
|
20
19
|
const [repoPart, branch] = repo.split("#");
|
|
21
20
|
let url;
|
|
22
21
|
if (repoPart.startsWith("http://") || repoPart.startsWith("https://")) {
|
|
@@ -26,7 +25,7 @@ function parseDegitRepo(repo) {
|
|
|
26
25
|
} else {
|
|
27
26
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790\u4ED3\u5E93\u5730\u5740: ${repo}`);
|
|
28
27
|
}
|
|
29
|
-
return { url, branch };
|
|
28
|
+
return { url, branch: branch || void 0 };
|
|
30
29
|
}
|
|
31
30
|
async function gitClone(url, branch, targetDir) {
|
|
32
31
|
const tmpDir = path.join(tmpdir(), `lightfish-template-${randomUUID()}`);
|
|
@@ -56,25 +55,10 @@ async function gitClone(url, branch, targetDir) {
|
|
|
56
55
|
}
|
|
57
56
|
async function downloadTemplate(repo, targetDir) {
|
|
58
57
|
await fs.ensureDir(targetDir);
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
verbose: true
|
|
64
|
-
// 开启详细日志,方便调试
|
|
65
|
-
});
|
|
66
|
-
try {
|
|
67
|
-
console.log(`\u6B63\u5728\u901A\u8FC7 degit \u62C9\u53D6\u6A21\u677F: ${repo}`);
|
|
68
|
-
await emitter.clone(targetDir);
|
|
69
|
-
console.log("degit \u62C9\u53D6\u5B8C\u6210");
|
|
70
|
-
} catch (error) {
|
|
71
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
72
|
-
await fs.emptyDir(targetDir);
|
|
73
|
-
const { url, branch } = parseDegitRepo(repo);
|
|
74
|
-
console.log(`\u6B63\u5728\u901A\u8FC7 git clone \u62C9\u53D6\u6A21\u677F: ${url}${branch ? ` (\u5206\u652F: ${branch})` : ""}`);
|
|
75
|
-
await gitClone(url, branch, targetDir);
|
|
76
|
-
console.log("git clone \u62C9\u53D6\u5B8C\u6210");
|
|
77
|
-
}
|
|
58
|
+
const { url, branch } = parseRepo(repo);
|
|
59
|
+
console.log(`\u6B63\u5728\u62C9\u53D6\u6A21\u677F: ${url}${branch ? ` (\u5206\u652F: ${branch})` : ""}`);
|
|
60
|
+
await gitClone(url, branch, targetDir);
|
|
61
|
+
console.log("\u6A21\u677F\u62C9\u53D6\u5B8C\u6210");
|
|
78
62
|
const gitDir = path.join(targetDir, ".git");
|
|
79
63
|
if (await fs.pathExists(gitDir)) {
|
|
80
64
|
await fs.remove(gitDir);
|
|
@@ -401,7 +385,7 @@ function readPackageVersion() {
|
|
|
401
385
|
|
|
402
386
|
// src/create-lightfish.ts
|
|
403
387
|
var cli = cac("create-lightfish");
|
|
404
|
-
cli.command("[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("--template-repo <repo>", "\u76F4\u63A5\u6307\u5B9A
|
|
388
|
+
cli.command("[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("--template-repo <repo>", "\u76F4\u63A5\u6307\u5B9A git \u4ED3\u5E93\u6E90\uFF0C\u5982 QGtiger/template-vite-server#main\uFF08\u4F18\u5148\u4E8E --template\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) => {
|
|
405
389
|
try {
|
|
406
390
|
const packageManager = options.pm ?? "pnpm";
|
|
407
391
|
if (!isPackageManager(packageManager)) {
|
package/dist/lightfish.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
8
|
import path4 from "path";
|
|
@@ -15,9 +15,8 @@ import path from "path";
|
|
|
15
15
|
import { tmpdir } from "os";
|
|
16
16
|
import { randomUUID } from "crypto";
|
|
17
17
|
import { execa } from "execa";
|
|
18
|
-
import degit from "degit";
|
|
19
18
|
import fs from "fs-extra";
|
|
20
|
-
function
|
|
19
|
+
function parseRepo(repo) {
|
|
21
20
|
const [repoPart, branch] = repo.split("#");
|
|
22
21
|
let url;
|
|
23
22
|
if (repoPart.startsWith("http://") || repoPart.startsWith("https://")) {
|
|
@@ -27,7 +26,7 @@ function parseDegitRepo(repo) {
|
|
|
27
26
|
} else {
|
|
28
27
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790\u4ED3\u5E93\u5730\u5740: ${repo}`);
|
|
29
28
|
}
|
|
30
|
-
return { url, branch };
|
|
29
|
+
return { url, branch: branch || void 0 };
|
|
31
30
|
}
|
|
32
31
|
async function gitClone(url, branch, targetDir) {
|
|
33
32
|
const tmpDir = path.join(tmpdir(), `lightfish-template-${randomUUID()}`);
|
|
@@ -57,25 +56,10 @@ async function gitClone(url, branch, targetDir) {
|
|
|
57
56
|
}
|
|
58
57
|
async function downloadTemplate(repo, targetDir) {
|
|
59
58
|
await fs.ensureDir(targetDir);
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
verbose: true
|
|
65
|
-
// 开启详细日志,方便调试
|
|
66
|
-
});
|
|
67
|
-
try {
|
|
68
|
-
console.log(`\u6B63\u5728\u901A\u8FC7 degit \u62C9\u53D6\u6A21\u677F: ${repo}`);
|
|
69
|
-
await emitter.clone(targetDir);
|
|
70
|
-
console.log("degit \u62C9\u53D6\u5B8C\u6210");
|
|
71
|
-
} catch (error) {
|
|
72
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
73
|
-
await fs.emptyDir(targetDir);
|
|
74
|
-
const { url, branch } = parseDegitRepo(repo);
|
|
75
|
-
console.log(`\u6B63\u5728\u901A\u8FC7 git clone \u62C9\u53D6\u6A21\u677F: ${url}${branch ? ` (\u5206\u652F: ${branch})` : ""}`);
|
|
76
|
-
await gitClone(url, branch, targetDir);
|
|
77
|
-
console.log("git clone \u62C9\u53D6\u5B8C\u6210");
|
|
78
|
-
}
|
|
59
|
+
const { url, branch } = parseRepo(repo);
|
|
60
|
+
console.log(`\u6B63\u5728\u62C9\u53D6\u6A21\u677F: ${url}${branch ? ` (\u5206\u652F: ${branch})` : ""}`);
|
|
61
|
+
await gitClone(url, branch, targetDir);
|
|
62
|
+
console.log("\u6A21\u677F\u62C9\u53D6\u5B8C\u6210");
|
|
79
63
|
const gitDir = path.join(targetDir, ".git");
|
|
80
64
|
if (await fs.pathExists(gitDir)) {
|
|
81
65
|
await fs.remove(gitDir);
|
|
@@ -380,36 +364,415 @@ async function createProject(options = {}) {
|
|
|
380
364
|
return resolvedOptions;
|
|
381
365
|
}
|
|
382
366
|
|
|
383
|
-
// src/
|
|
367
|
+
// src/commands/publish.ts
|
|
368
|
+
import fs8 from "fs";
|
|
369
|
+
import path8 from "path";
|
|
370
|
+
|
|
371
|
+
// src/utils/env.ts
|
|
384
372
|
import fs5 from "fs";
|
|
385
373
|
import path5 from "path";
|
|
374
|
+
import { parse } from "dotenv";
|
|
375
|
+
var DEFAULT_BASE_ROOT = "https://lf-frontend.oss-cn-beijing.aliyuncs.com";
|
|
376
|
+
function loadDotenv({ cwd, envPath }) {
|
|
377
|
+
let merged;
|
|
378
|
+
if (envPath) {
|
|
379
|
+
merged = readEnvFile(path5.resolve(cwd, envPath));
|
|
380
|
+
} else {
|
|
381
|
+
const base = readEnvFile(path5.resolve(cwd, ".env"));
|
|
382
|
+
const local = readEnvFile(path5.resolve(cwd, ".env.local"));
|
|
383
|
+
merged = { ...base, ...local };
|
|
384
|
+
}
|
|
385
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
386
|
+
if (process.env[key] === void 0) {
|
|
387
|
+
process.env[key] = value;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function readEnvFile(file) {
|
|
392
|
+
if (!fs5.existsSync(file)) return {};
|
|
393
|
+
return parse(fs5.readFileSync(file, "utf8"));
|
|
394
|
+
}
|
|
395
|
+
function getRequiredEnvNames({ notify, serverDetected }) {
|
|
396
|
+
const required = ["OSS_REGION", "OSS_BUCKET", "OSS_ACCESS_KEYID", "OSS_ACCESS_KEYSECRECT"];
|
|
397
|
+
if (notify) required.push("DEPLOY_API_URL");
|
|
398
|
+
if (serverDetected) required.push("DATABASE_URL");
|
|
399
|
+
return required;
|
|
400
|
+
}
|
|
401
|
+
function resolvePublishEnv(options, env = process.env) {
|
|
402
|
+
const required = getRequiredEnvNames(options);
|
|
403
|
+
const missing = required.filter((name) => !env[name]);
|
|
404
|
+
if (missing.length > 0) {
|
|
405
|
+
throw new Error(
|
|
406
|
+
`\u7F3A\u5C11\u5FC5\u9700\u73AF\u5883\u53D8\u91CF:${missing.join(", ")}(\u8BF7\u5728 .env / .env.local \u6216\u73AF\u5883\u53D8\u91CF\u4E2D\u914D\u7F6E)`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
ossRegion: env.OSS_REGION,
|
|
411
|
+
ossBucket: env.OSS_BUCKET,
|
|
412
|
+
ossAccessKeyId: env.OSS_ACCESS_KEYID,
|
|
413
|
+
// 注意:变量名沿用 CI secrets 的拼写(SECRECT),保持 .env 兼容
|
|
414
|
+
ossAccessKeySecret: env.OSS_ACCESS_KEYSECRECT,
|
|
415
|
+
deployApiUrl: options.notify ? env.DEPLOY_API_URL : void 0,
|
|
416
|
+
databaseUrl: env.DATABASE_URL,
|
|
417
|
+
baseRoot: env.BASE_ROOT || DEFAULT_BASE_ROOT
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/utils/build.ts
|
|
422
|
+
import fs6 from "fs";
|
|
423
|
+
import path6 from "path";
|
|
424
|
+
import { execa as execa3 } from "execa";
|
|
425
|
+
var SERVER_CONFIG_FILES = [
|
|
426
|
+
"lightfish-server.config.js",
|
|
427
|
+
"lightfish-server.config.ts",
|
|
428
|
+
"lightfish-server.config.mjs",
|
|
429
|
+
"lightfish-server.config.cjs"
|
|
430
|
+
];
|
|
431
|
+
var VITE_CONFIG_FILES = [
|
|
432
|
+
"vite.config.ts",
|
|
433
|
+
"vite.config.js",
|
|
434
|
+
"vite.config.mts",
|
|
435
|
+
"vite.config.mjs",
|
|
436
|
+
"vite.config.cjs"
|
|
437
|
+
];
|
|
438
|
+
function readPackageJson(cwd) {
|
|
439
|
+
const file = path6.join(cwd, "package.json");
|
|
440
|
+
if (!fs6.existsSync(file)) return void 0;
|
|
441
|
+
return JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
442
|
+
}
|
|
443
|
+
function hasVite(cwd) {
|
|
444
|
+
if (VITE_CONFIG_FILES.some((f) => fs6.existsSync(path6.join(cwd, f)))) return true;
|
|
445
|
+
const pkg = readPackageJson(cwd);
|
|
446
|
+
if (pkg) {
|
|
447
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
448
|
+
if ("vite" in deps) return true;
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
function detectBuild(cwd) {
|
|
453
|
+
return {
|
|
454
|
+
vite: hasVite(cwd),
|
|
455
|
+
server: SERVER_CONFIG_FILES.some((f) => fs6.existsSync(path6.join(cwd, f)))
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
async function runBuildSteps(options) {
|
|
459
|
+
const detection = detectBuild(options.cwd);
|
|
460
|
+
if (!detection.vite && !detection.server) {
|
|
461
|
+
throw new Error("\u672A\u68C0\u6D4B\u5230 Vite \u6216 lightfish-server,\u65E0\u6CD5\u6784\u5EFA");
|
|
462
|
+
}
|
|
463
|
+
const execOptions = {
|
|
464
|
+
cwd: options.cwd,
|
|
465
|
+
preferLocal: true,
|
|
466
|
+
stdio: "inherit",
|
|
467
|
+
env: { ...process.env, ...options.env }
|
|
468
|
+
};
|
|
469
|
+
if (detection.vite) {
|
|
470
|
+
await execa3("vite", ["build"], execOptions);
|
|
471
|
+
}
|
|
472
|
+
if (detection.server) {
|
|
473
|
+
await execa3("lightfish-server", ["build"], execOptions);
|
|
474
|
+
await execa3("lightfish-server", ["db", "push"], execOptions);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/utils/oss.ts
|
|
479
|
+
import OSS from "ali-oss";
|
|
480
|
+
import fs7 from "fs";
|
|
481
|
+
import path7 from "path";
|
|
482
|
+
function withPrefix(prefix, key) {
|
|
483
|
+
const normalized = prefix.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
484
|
+
if (!normalized) return key;
|
|
485
|
+
return `${normalized}/${key}`;
|
|
486
|
+
}
|
|
487
|
+
function walkDir(dir, base = "") {
|
|
488
|
+
if (!fs7.existsSync(dir)) return [];
|
|
489
|
+
const entries = fs7.readdirSync(dir, { withFileTypes: true });
|
|
490
|
+
const files = [];
|
|
491
|
+
for (const entry of entries) {
|
|
492
|
+
const fullPath = path7.join(dir, entry.name);
|
|
493
|
+
const key = base ? `${base}/${entry.name}` : entry.name;
|
|
494
|
+
if (entry.isDirectory()) {
|
|
495
|
+
files.push(...walkDir(fullPath, key));
|
|
496
|
+
} else {
|
|
497
|
+
files.push({ localPath: fullPath, key });
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return files;
|
|
501
|
+
}
|
|
502
|
+
async function uploadDist(options, client) {
|
|
503
|
+
const oss = client ?? createClient(options);
|
|
504
|
+
const files = walkDir(options.distDir);
|
|
505
|
+
console.log(`[publish] Found ${files.length} files to upload`);
|
|
506
|
+
const results = await Promise.all(
|
|
507
|
+
files.map(({ localPath, key }) => {
|
|
508
|
+
const objectKey = withPrefix(options.objectPrefix, key);
|
|
509
|
+
return oss.put(objectKey, localPath).then((res) => ({ key: objectKey, ok: res.res.status === 200 })).catch((err) => {
|
|
510
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
511
|
+
console.error(`[publish] ERROR ${objectKey}: ${message}`);
|
|
512
|
+
return { key: objectKey, ok: false };
|
|
513
|
+
});
|
|
514
|
+
})
|
|
515
|
+
);
|
|
516
|
+
for (const r of results.filter((r2) => r2.ok)) {
|
|
517
|
+
console.log(`[publish] ok ${r.key}`);
|
|
518
|
+
}
|
|
519
|
+
const ok = results.filter((r) => r.ok).length;
|
|
520
|
+
const fail = results.filter((r) => !r.ok).length;
|
|
521
|
+
console.log(`[publish] Done: ${ok} succeeded, ${fail} failed`);
|
|
522
|
+
return { ok, fail };
|
|
523
|
+
}
|
|
524
|
+
function createClient(options) {
|
|
525
|
+
return new OSS({
|
|
526
|
+
region: options.region,
|
|
527
|
+
accessKeyId: options.accessKeyId,
|
|
528
|
+
accessKeySecret: options.accessKeySecret,
|
|
529
|
+
bucket: options.bucket
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/utils/notify.ts
|
|
534
|
+
async function notifyDeploy(options) {
|
|
535
|
+
const fetchFn = options.fetchImpl ?? fetch;
|
|
536
|
+
const body = JSON.stringify(options.payload);
|
|
537
|
+
console.log(`[publish] POST ${options.apiUrl}`);
|
|
538
|
+
console.log(`[publish] body: ${body}`);
|
|
539
|
+
let res;
|
|
540
|
+
try {
|
|
541
|
+
res = await fetchFn(options.apiUrl, {
|
|
542
|
+
method: "POST",
|
|
543
|
+
headers: { "Content-Type": "application/json" },
|
|
544
|
+
body
|
|
545
|
+
});
|
|
546
|
+
} catch (err) {
|
|
547
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
548
|
+
throw new Error(`\u90E8\u7F72 API \u8BF7\u6C42\u5931\u8D25:${message}`);
|
|
549
|
+
}
|
|
550
|
+
if (!res.ok) {
|
|
551
|
+
throw new Error(`\u90E8\u7F72 API \u8FD4\u56DE HTTP ${res.status}`);
|
|
552
|
+
}
|
|
553
|
+
let data;
|
|
554
|
+
try {
|
|
555
|
+
data = await res.json();
|
|
556
|
+
} catch {
|
|
557
|
+
throw new Error("\u90E8\u7F72 API \u54CD\u5E94\u975E JSON");
|
|
558
|
+
}
|
|
559
|
+
console.log(`[publish] response: ${JSON.stringify(data)}`);
|
|
560
|
+
if (typeof data?.success !== "boolean" || data.success !== true) {
|
|
561
|
+
throw new Error("\u90E8\u7F72 API \u672A\u8FD4\u56DE success=true");
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// src/commands/publish.ts
|
|
566
|
+
function generateVersion(date = /* @__PURE__ */ new Date()) {
|
|
567
|
+
return date.toISOString().replace(/[-T:.Z]/g, "").slice(0, 14);
|
|
568
|
+
}
|
|
569
|
+
function readPackageJsonName(cwd) {
|
|
570
|
+
const file = path8.join(cwd, "package.json");
|
|
571
|
+
if (!fs8.existsSync(file)) return void 0;
|
|
572
|
+
const pkg = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
573
|
+
return pkg.name;
|
|
574
|
+
}
|
|
575
|
+
function resolveAppName(options) {
|
|
576
|
+
if (options.app) return options.app;
|
|
577
|
+
const pkgName = readPackageJsonName(options.cwd);
|
|
578
|
+
if (pkgName) {
|
|
579
|
+
return pkgName.startsWith("@") ? pkgName.replace(/^@[^/]+\//, "") : pkgName;
|
|
580
|
+
}
|
|
581
|
+
throw new Error("\u65E0\u6CD5\u786E\u5B9A APP_NAME:\u8BF7\u901A\u8FC7 --app \u6307\u5B9A,\u6216\u786E\u4FDD package.json \u542B name \u5B57\u6BB5");
|
|
582
|
+
}
|
|
583
|
+
function assertNumericVersion(version) {
|
|
584
|
+
if (!/^\d+$/.test(version)) {
|
|
585
|
+
throw new Error(`version \u5FC5\u987B\u4E3A\u7EAF\u6570\u5B57(\u4E0E\u90E8\u7F72 API \u7684 number \u5951\u7EA6\u4E00\u81F4),\u6536\u5230:${version}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
function resolveOptions(options) {
|
|
589
|
+
const cwd = options.cwd ?? process.cwd();
|
|
590
|
+
const appName = resolveAppName({ cwd, app: options.app });
|
|
591
|
+
const version = options.version ?? generateVersion();
|
|
592
|
+
assertNumericVersion(version);
|
|
593
|
+
const distDir = path8.resolve(cwd, options.dist ?? "dist");
|
|
594
|
+
const notify = !options.noNotify;
|
|
595
|
+
const baseRoot = process.env.BASE_ROOT || DEFAULT_BASE_ROOT;
|
|
596
|
+
const root = baseRoot.replace(/\/+$/, "");
|
|
597
|
+
const viteBaseUrl = root ? `${root}/${appName}/${version}/` : `/${appName}/${version}/`;
|
|
598
|
+
const objectPrefix = `${appName}/${version}`;
|
|
599
|
+
const ossIndexUrl = `${root}/${appName}/${version}/index.html`;
|
|
600
|
+
return {
|
|
601
|
+
cwd,
|
|
602
|
+
appName,
|
|
603
|
+
version,
|
|
604
|
+
distDir,
|
|
605
|
+
baseRoot,
|
|
606
|
+
root,
|
|
607
|
+
viteBaseUrl,
|
|
608
|
+
objectPrefix,
|
|
609
|
+
ossIndexUrl,
|
|
610
|
+
notify,
|
|
611
|
+
skipBuild: options.skipBuild ?? false,
|
|
612
|
+
note: options.note
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
function printBuildPlanDryRun(detection) {
|
|
616
|
+
if (!detection.vite && !detection.server) {
|
|
617
|
+
console.log("[publish] (dry-run) \u672A\u68C0\u6D4B\u5230 Vite \u6216 lightfish-server,\u6784\u5EFA\u5C06\u5931\u8D25");
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
console.log("[publish] (dry-run) \u5C06\u6267\u884C\u6784\u5EFA:");
|
|
621
|
+
if (detection.vite) console.log("[publish] (dry-run) vite build");
|
|
622
|
+
if (detection.server) {
|
|
623
|
+
console.log("[publish] (dry-run) lightfish-server build");
|
|
624
|
+
console.log("[publish] (dry-run) lightfish-server db push");
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function printEnvDryRun(serverDetected, notify) {
|
|
628
|
+
const required = getRequiredEnvNames({ notify, serverDetected });
|
|
629
|
+
console.log("[publish] (dry-run) \u73AF\u5883\u53D8\u91CF\u9884\u68C0:");
|
|
630
|
+
for (const name of required) {
|
|
631
|
+
const present = process.env[name] !== void 0 && process.env[name] !== "";
|
|
632
|
+
console.log(`[publish] (dry-run) ${present ? "\u2713" : "\u2717"} ${name}`);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function printUploadPlanDryRun(options) {
|
|
636
|
+
const files = walkDir(options.distDir);
|
|
637
|
+
if (files.length === 0) {
|
|
638
|
+
console.log(`[publish] (dry-run) \u4EA7\u7269\u76EE\u5F55 ${options.distDir} \u4E3A\u7A7A\u6216\u4E0D\u5B58\u5728(\u6784\u5EFA\u540E\u5C06\u751F\u6210)`);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
console.log(`[publish] (dry-run) \u5C06\u4E0A\u4F20 ${files.length} \u4E2A\u6587\u4EF6\u5230 OSS \u524D\u7F00:${options.objectPrefix}/`);
|
|
642
|
+
for (const { key } of files) {
|
|
643
|
+
console.log(`[publish] (dry-run) ${withPrefix(options.objectPrefix, key)}`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function buildNotifyPayload(params) {
|
|
647
|
+
const payload = {
|
|
648
|
+
id: params.appName,
|
|
649
|
+
version: Number(params.version),
|
|
650
|
+
ossIndexUrl: params.ossIndexUrl,
|
|
651
|
+
domain: `https://${params.appName}.lightfish.top`
|
|
652
|
+
};
|
|
653
|
+
if (params.note) payload.note = params.note;
|
|
654
|
+
return payload;
|
|
655
|
+
}
|
|
656
|
+
function printNotifyPlanDryRun(options, pubEnv) {
|
|
657
|
+
if (!options.notify) {
|
|
658
|
+
console.log("[publish] (dry-run) \u901A\u77E5\u5DF2\u8DF3\u8FC7(--no-notify)");
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const payload = buildNotifyPayload({
|
|
662
|
+
appName: options.appName,
|
|
663
|
+
version: options.version,
|
|
664
|
+
ossIndexUrl: options.ossIndexUrl,
|
|
665
|
+
note: options.note
|
|
666
|
+
});
|
|
667
|
+
console.log("[publish] (dry-run) \u5C06 POST \u901A\u77E5:");
|
|
668
|
+
console.log(`[publish] (dry-run) url: ${pubEnv.deployApiUrl ?? "(\u672A\u914D\u7F6E DEPLOY_API_URL)"}`);
|
|
669
|
+
console.log(`[publish] (dry-run) body: ${JSON.stringify(payload)}`);
|
|
670
|
+
}
|
|
671
|
+
async function runPublish(options = {}) {
|
|
672
|
+
const cwd = options.cwd ?? process.cwd();
|
|
673
|
+
loadDotenv({ cwd, envPath: options.envPath });
|
|
674
|
+
const resolved = resolveOptions(options);
|
|
675
|
+
console.log(`[publish] app=${resolved.appName} version=${resolved.version} dist=${resolved.distDir}`);
|
|
676
|
+
let serverDetected = false;
|
|
677
|
+
if (!resolved.skipBuild) {
|
|
678
|
+
const detection = detectBuild(resolved.cwd);
|
|
679
|
+
serverDetected = detection.server;
|
|
680
|
+
if (options.dryRun) {
|
|
681
|
+
printBuildPlanDryRun(detection);
|
|
682
|
+
} else {
|
|
683
|
+
console.log("[publish] \u5F00\u59CB\u6784\u5EFA...");
|
|
684
|
+
const buildEnv = {
|
|
685
|
+
VITE_BASE_URL: resolved.viteBaseUrl,
|
|
686
|
+
BASE_ROOT: resolved.baseRoot,
|
|
687
|
+
APP_NAME: resolved.appName,
|
|
688
|
+
BUILD_VERSION: resolved.version
|
|
689
|
+
};
|
|
690
|
+
if (process.env.DATABASE_URL) buildEnv.DATABASE_URL = process.env.DATABASE_URL;
|
|
691
|
+
await runBuildSteps({
|
|
692
|
+
cwd: resolved.cwd,
|
|
693
|
+
env: buildEnv
|
|
694
|
+
});
|
|
695
|
+
console.log("[publish] \u6784\u5EFA\u5B8C\u6210");
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const pubEnv = options.dryRun ? softResolveEnv(resolved.notify, serverDetected) : resolvePublishEnv({ notify: resolved.notify, serverDetected });
|
|
699
|
+
if (options.dryRun) {
|
|
700
|
+
printEnvDryRun(serverDetected, resolved.notify);
|
|
701
|
+
printUploadPlanDryRun(resolved);
|
|
702
|
+
printNotifyPlanDryRun(resolved, pubEnv);
|
|
703
|
+
console.log("[publish] (dry-run) \u5B8C\u6210,\u672A\u6267\u884C\u4EFB\u4F55\u526F\u4F5C\u7528");
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (!fs8.existsSync(resolved.distDir)) {
|
|
707
|
+
throw new Error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728:${resolved.distDir}`);
|
|
708
|
+
}
|
|
709
|
+
console.log("[publish] \u5F00\u59CB\u4E0A\u4F20 OSS...");
|
|
710
|
+
const { fail } = await uploadDist({
|
|
711
|
+
region: pubEnv.ossRegion,
|
|
712
|
+
bucket: pubEnv.ossBucket,
|
|
713
|
+
accessKeyId: pubEnv.ossAccessKeyId,
|
|
714
|
+
accessKeySecret: pubEnv.ossAccessKeySecret,
|
|
715
|
+
objectPrefix: resolved.objectPrefix,
|
|
716
|
+
distDir: resolved.distDir
|
|
717
|
+
});
|
|
718
|
+
if (fail > 0) {
|
|
719
|
+
throw new Error(`OSS \u4E0A\u4F20\u6709 ${fail} \u4E2A\u6587\u4EF6\u5931\u8D25`);
|
|
720
|
+
}
|
|
721
|
+
console.log("[publish] OSS \u4E0A\u4F20\u5B8C\u6210");
|
|
722
|
+
if (resolved.notify) {
|
|
723
|
+
console.log("[publish] \u5F00\u59CB\u901A\u77E5\u90E8\u7F72 API...");
|
|
724
|
+
const payload = buildNotifyPayload({
|
|
725
|
+
appName: resolved.appName,
|
|
726
|
+
version: resolved.version,
|
|
727
|
+
ossIndexUrl: resolved.ossIndexUrl,
|
|
728
|
+
note: resolved.note
|
|
729
|
+
});
|
|
730
|
+
await notifyDeploy({ apiUrl: pubEnv.deployApiUrl, payload });
|
|
731
|
+
console.log("[publish] \u901A\u77E5\u5B8C\u6210");
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function softResolveEnv(notify, serverDetected) {
|
|
735
|
+
return {
|
|
736
|
+
ossRegion: process.env.OSS_REGION ?? "",
|
|
737
|
+
ossBucket: process.env.OSS_BUCKET ?? "",
|
|
738
|
+
ossAccessKeyId: process.env.OSS_ACCESS_KEYID ?? "",
|
|
739
|
+
ossAccessKeySecret: process.env.OSS_ACCESS_KEYSECRECT ?? "",
|
|
740
|
+
deployApiUrl: notify ? process.env.DEPLOY_API_URL : void 0,
|
|
741
|
+
databaseUrl: process.env.DATABASE_URL,
|
|
742
|
+
baseRoot: process.env.BASE_ROOT || DEFAULT_BASE_ROOT
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/utils/package-info.ts
|
|
747
|
+
import fs9 from "fs";
|
|
748
|
+
import path9 from "path";
|
|
386
749
|
import { fileURLToPath } from "url";
|
|
387
|
-
var dirname =
|
|
750
|
+
var dirname = path9.dirname(fileURLToPath(import.meta.url));
|
|
388
751
|
function readPackageVersion() {
|
|
389
752
|
const packageJsonPathCandidates = [
|
|
390
753
|
// 源码模式:src/utils/package-info.ts -> packages/cli/package.json
|
|
391
|
-
|
|
754
|
+
path9.resolve(dirname, "../../package.json"),
|
|
392
755
|
// 构建产物模式:dist/lightfish.js -> packages/cli/package.json
|
|
393
|
-
|
|
756
|
+
path9.resolve(dirname, "../package.json")
|
|
394
757
|
];
|
|
395
|
-
const packageJsonPath = packageJsonPathCandidates.find((candidate) =>
|
|
758
|
+
const packageJsonPath = packageJsonPathCandidates.find((candidate) => fs9.existsSync(candidate));
|
|
396
759
|
if (!packageJsonPath) {
|
|
397
760
|
return "0.0.0";
|
|
398
761
|
}
|
|
399
|
-
const packageJson = JSON.parse(
|
|
762
|
+
const packageJson = JSON.parse(fs9.readFileSync(packageJsonPath, "utf8"));
|
|
400
763
|
return packageJson.version ?? "0.0.0";
|
|
401
764
|
}
|
|
402
765
|
|
|
403
766
|
// src/lightfish.ts
|
|
404
767
|
var cli = cac("lightfish");
|
|
405
768
|
async function runBuild() {
|
|
406
|
-
await
|
|
769
|
+
await execa4("vite", ["build"], {
|
|
407
770
|
cwd: process.cwd(),
|
|
408
771
|
preferLocal: true,
|
|
409
772
|
stdio: "inherit"
|
|
410
773
|
});
|
|
411
774
|
}
|
|
412
|
-
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("--template-repo <repo>", "\u76F4\u63A5\u6307\u5B9A
|
|
775
|
+
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("--template-repo <repo>", "\u76F4\u63A5\u6307\u5B9A git \u4ED3\u5E93\u6E90\uFF0C\u5982 QGtiger/template-vite-server#main\uFF08\u4F18\u5148\u4E8E --template\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) => {
|
|
413
776
|
try {
|
|
414
777
|
const packageManager = options.pm ?? "pnpm";
|
|
415
778
|
if (!isPackageManager(packageManager)) {
|
|
@@ -440,9 +803,28 @@ cli.command("build", "\u6267\u884C Vite \u6784\u5EFA\uFF0C\u540E\u7EED\u4F1A\u53
|
|
|
440
803
|
process.exitCode = 1;
|
|
441
804
|
}
|
|
442
805
|
});
|
|
806
|
+
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) => {
|
|
807
|
+
try {
|
|
808
|
+
await runPublish({
|
|
809
|
+
app: options.app,
|
|
810
|
+
version: options.version,
|
|
811
|
+
dist: options.dist,
|
|
812
|
+
note: options.note,
|
|
813
|
+
envPath: options.env,
|
|
814
|
+
skipBuild: options.build === false,
|
|
815
|
+
noNotify: options.notify === false,
|
|
816
|
+
dryRun: options.dryRun
|
|
817
|
+
});
|
|
818
|
+
process.exit(0);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
821
|
+
console.error(`\u53D1\u5E03\u5931\u8D25\uFF1A${message}`);
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
443
825
|
cli.command("", "\u663E\u793A\u5E2E\u52A9").action(() => {
|
|
444
826
|
cli.outputHelp();
|
|
445
827
|
});
|
|
446
828
|
cli.help();
|
|
447
|
-
cli.version(readPackageVersion());
|
|
829
|
+
cli.version(readPackageVersion(), "-v, --cli-version");
|
|
448
830
|
cli.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightfish/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -18,13 +18,15 @@
|
|
|
18
18
|
"lint": "tsc --noEmit"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"ali-oss": "^6.23.0",
|
|
21
22
|
"cac": "^7.0.0",
|
|
22
|
-
"
|
|
23
|
+
"dotenv": "^16.6.1",
|
|
23
24
|
"execa": "^9.6.1",
|
|
24
25
|
"fs-extra": "^11.3.5",
|
|
25
26
|
"prompts": "^2.4.2"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
29
|
+
"@types/ali-oss": "^6.23.3",
|
|
28
30
|
"@types/fs-extra": "^11.0.4",
|
|
29
31
|
"@types/prompts": "^2.4.9"
|
|
30
32
|
}
|