@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.
@@ -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 parseDegitRepo(repo) {
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 emitter = degit(repo, {
60
- cache: false,
61
- // 禁用缓存,避免使用损坏的本地缓存
62
- force: true,
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 degit \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) => {
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 execa3 } from "execa";
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 parseDegitRepo(repo) {
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 emitter = degit(repo, {
61
- cache: false,
62
- // 禁用缓存,避免使用损坏的本地缓存
63
- force: true,
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/utils/package-info.ts
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 = path5.dirname(fileURLToPath(import.meta.url));
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
- path5.resolve(dirname, "../../package.json"),
754
+ path9.resolve(dirname, "../../package.json"),
392
755
  // 构建产物模式:dist/lightfish.js -> packages/cli/package.json
393
- path5.resolve(dirname, "../package.json")
756
+ path9.resolve(dirname, "../package.json")
394
757
  ];
395
- const packageJsonPath = packageJsonPathCandidates.find((candidate) => fs5.existsSync(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(fs5.readFileSync(packageJsonPath, "utf8"));
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 execa3("vite", ["build"], {
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 degit \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) => {
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",
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
- "degit": "^2.8.4",
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
  }