@lark-apaas/fullstack-cli 1.1.16-beta.7 → 1.1.16-beta.9

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/index.js CHANGED
@@ -1414,14 +1414,15 @@ async function fetchSyncedTables(appId, workspace) {
1414
1414
  DEFAULT_TIMEOUT_MS2
1415
1415
  );
1416
1416
  });
1417
+ const dbBranch = process.env.FORCE_DB_BRANCH || "main";
1417
1418
  const start = Date.now();
1418
1419
  console.log(
1419
- `[fetchSyncedTables] \u2192 GET listTableView (dbBranch=main) appId=${appId ? "set" : "unset"} workspace=${workspace ? "set" : "unset"}`
1420
+ `[fetchSyncedTables] \u2192 GET listTableView (dbBranch=${dbBranch}) appId=${appId ? "set" : "unset"} workspace=${workspace ? "set" : "unset"}`
1420
1421
  );
1421
1422
  const response = await Promise.race([
1422
1423
  client.get(
1423
1424
  `/api/v1/dataloom/inner/app/${appId}/workspaces/${workspace}/listTableView`,
1424
- { params: { dbBranch: "main" }, headers: { "x-supaas-bizsource": "miaoda" } }
1425
+ { params: { dbBranch }, headers: { "x-supaas-bizsource": "miaoda" } }
1425
1426
  ),
1426
1427
  timeoutPromise
1427
1428
  ]);
@@ -2377,12 +2378,15 @@ var syncConfig = {
2377
2378
  type: "directory",
2378
2379
  overwrite: true
2379
2380
  },
2380
- // 2. 覆写 nest-cli.json 配置,禁止用户修改
2381
+ // 2. 智能合并 nest-cli.json 配置(保留用户自定义的 assets、plugins 等)
2381
2382
  {
2382
2383
  from: "templates/nest-cli.json",
2383
2384
  to: "nest-cli.json",
2384
- type: "file",
2385
- overwrite: true
2385
+ type: "merge-json",
2386
+ arrayMerge: {
2387
+ "compilerOptions.assets": { key: "include" },
2388
+ "compilerOptions.plugins": { key: "name" }
2389
+ }
2386
2390
  },
2387
2391
  // // 2. 追加内容到 .gitignore
2388
2392
  // {
@@ -2475,6 +2479,52 @@ function removeLineFromFile(filePath, pattern) {
2475
2479
  return true;
2476
2480
  }
2477
2481
 
2482
+ // src/utils/merge-json.ts
2483
+ function isPlainObject(value) {
2484
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2485
+ }
2486
+ function mergeArrayByKey(userArr, templateArr, key) {
2487
+ const result = [...userArr];
2488
+ for (const templateItem of templateArr) {
2489
+ if (!isPlainObject(templateItem)) continue;
2490
+ const templateKey = templateItem[key];
2491
+ const existingIndex = result.findIndex(
2492
+ (item) => isPlainObject(item) && item[key] === templateKey
2493
+ );
2494
+ if (existingIndex >= 0) {
2495
+ result[existingIndex] = templateItem;
2496
+ } else {
2497
+ result.push(templateItem);
2498
+ }
2499
+ }
2500
+ return result;
2501
+ }
2502
+ function deepMergeJson(user, template, arrayMerge = {}, currentPath = "") {
2503
+ const result = { ...user };
2504
+ for (const key of Object.keys(template)) {
2505
+ const fullPath = currentPath ? `${currentPath}.${key}` : key;
2506
+ const templateValue = template[key];
2507
+ const userValue = user[key];
2508
+ if (Array.isArray(templateValue)) {
2509
+ const mergeConfig = arrayMerge[fullPath];
2510
+ if (mergeConfig && Array.isArray(userValue)) {
2511
+ result[key] = mergeArrayByKey(userValue, templateValue, mergeConfig.key);
2512
+ } else {
2513
+ result[key] = templateValue;
2514
+ }
2515
+ } else if (isPlainObject(templateValue)) {
2516
+ if (isPlainObject(userValue)) {
2517
+ result[key] = deepMergeJson(userValue, templateValue, arrayMerge, fullPath);
2518
+ } else {
2519
+ result[key] = templateValue;
2520
+ }
2521
+ } else {
2522
+ result[key] = templateValue;
2523
+ }
2524
+ }
2525
+ return result;
2526
+ }
2527
+
2478
2528
  // src/commands/sync/run.handler.ts
2479
2529
  async function run2(options) {
2480
2530
  const userProjectRoot = process.env.INIT_CWD || process.cwd();
@@ -2537,6 +2587,12 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2537
2587
  addLineToFile(destPath2, rule.line);
2538
2588
  return;
2539
2589
  }
2590
+ if (rule.type === "merge-json") {
2591
+ const srcPath2 = path4.join(pluginRoot, rule.from);
2592
+ const destPath2 = path4.join(userProjectRoot, rule.to);
2593
+ mergeJsonFile(srcPath2, destPath2, rule.arrayMerge);
2594
+ return;
2595
+ }
2540
2596
  if (!("from" in rule)) {
2541
2597
  return;
2542
2598
  }
@@ -2679,6 +2735,33 @@ function addLineToFile(filePath, line) {
2679
2735
  fs6.appendFileSync(filePath, appendContent);
2680
2736
  console.log(`[fullstack-cli] \u2713 ${fileName} (added: ${line})`);
2681
2737
  }
2738
+ function mergeJsonFile(src, dest, arrayMerge) {
2739
+ const fileName = path4.basename(dest);
2740
+ if (!fs6.existsSync(src)) {
2741
+ console.warn(`[fullstack-cli] Source not found: ${src}`);
2742
+ return;
2743
+ }
2744
+ const templateContent = JSON.parse(fs6.readFileSync(src, "utf-8"));
2745
+ if (!fs6.existsSync(dest)) {
2746
+ const destDir = path4.dirname(dest);
2747
+ if (!fs6.existsSync(destDir)) {
2748
+ fs6.mkdirSync(destDir, { recursive: true });
2749
+ }
2750
+ fs6.writeFileSync(dest, JSON.stringify(templateContent, null, 2) + "\n");
2751
+ console.log(`[fullstack-cli] \u2713 ${fileName} (created)`);
2752
+ return;
2753
+ }
2754
+ const userContent = JSON.parse(fs6.readFileSync(dest, "utf-8"));
2755
+ const merged = deepMergeJson(userContent, templateContent, arrayMerge ?? {});
2756
+ const userStr = JSON.stringify(userContent, null, 2);
2757
+ const mergedStr = JSON.stringify(merged, null, 2);
2758
+ if (userStr === mergedStr) {
2759
+ console.log(`[fullstack-cli] \u25CB ${fileName} (already up to date)`);
2760
+ return;
2761
+ }
2762
+ fs6.writeFileSync(dest, mergedStr + "\n");
2763
+ console.log(`[fullstack-cli] \u2713 ${fileName} (merged)`);
2764
+ }
2682
2765
 
2683
2766
  // src/commands/sync/index.ts
2684
2767
  var syncCommand = {
@@ -2935,7 +3018,7 @@ function getUpgradeFilesToStage(disableGenOpenapi = true) {
2935
3018
  const syncConfig2 = genSyncConfig({ disableGenOpenapi });
2936
3019
  const filesToStage = /* @__PURE__ */ new Set();
2937
3020
  syncConfig2.sync.forEach((rule) => {
2938
- if (rule.type === "file" || rule.type === "directory") {
3021
+ if (rule.type === "file" || rule.type === "directory" || rule.type === "merge-json") {
2939
3022
  filesToStage.add(rule.to);
2940
3023
  } else if (rule.type === "remove-line" || rule.type === "add-line") {
2941
3024
  filesToStage.add(rule.to);
@@ -7173,6 +7256,12 @@ var SCENE_CONFIGS = {
7173
7256
  buildRequestBody: () => ({ commitID: "" })
7174
7257
  }
7175
7258
  };
7259
+ var UPLOAD_STATIC_DEFAULTS = {
7260
+ staticDir: "shared/static",
7261
+ tosutilPath: "tosutil",
7262
+ endpoint: "tos-cn-beijing.volces.com",
7263
+ region: "cn-beijing"
7264
+ };
7176
7265
 
7177
7266
  // src/commands/build/api-client.ts
7178
7267
  async function genArtifactUploadCredential(appId, body) {
@@ -7188,17 +7277,20 @@ async function genArtifactUploadCredential(appId, body) {
7188
7277
  }
7189
7278
  async function getDefaultBucketId(appId) {
7190
7279
  const client = getHttpClient();
7191
- const url = `/b/${appId}/get_published_v2`;
7192
- const response = await client.get(url);
7280
+ const url = `/v1/app/${appId}/storage/inner/staticBucket`;
7281
+ const response = await client.post(url, {});
7193
7282
  if (!response.ok || response.status !== 200) {
7194
7283
  throw new Error(
7195
- `get_published_v2 \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
7284
+ `getOrCreateStaticBucket \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
7196
7285
  );
7197
7286
  }
7198
7287
  const data = await response.json();
7199
- const bucketId = data?.data?.app_runtime_extra?.bucket?.default_bucket_id;
7288
+ if (data.status_code !== "0") {
7289
+ throw new Error(`getOrCreateStaticBucket \u8FD4\u56DE\u5F02\u5E38, status_code: ${data.status_code}`);
7290
+ }
7291
+ const bucketId = data?.data?.bucketID;
7200
7292
  if (!bucketId) {
7201
- throw new Error(`\u672A\u627E\u5230\u5E94\u7528 ${appId} \u7684\u9ED8\u8BA4\u5B58\u50A8\u6876`);
7293
+ throw new Error(`\u672A\u627E\u5230\u5E94\u7528 ${appId} \u7684\u9759\u6001\u8D44\u6E90\u5B58\u50A8\u6876`);
7202
7294
  }
7203
7295
  return bucketId;
7204
7296
  }
@@ -7278,10 +7370,10 @@ async function uploadStatic(options) {
7278
7370
  try {
7279
7371
  const {
7280
7372
  appId,
7281
- staticDir = "shared/static",
7282
- tosutilPath = "/workspace/tosutil",
7283
- endpoint = "tos-cn-beijing.volces.com",
7284
- region = "cn-beijing"
7373
+ staticDir = UPLOAD_STATIC_DEFAULTS.staticDir,
7374
+ tosutilPath = UPLOAD_STATIC_DEFAULTS.tosutilPath,
7375
+ endpoint = UPLOAD_STATIC_DEFAULTS.endpoint,
7376
+ region = UPLOAD_STATIC_DEFAULTS.region
7285
7377
  } = options;
7286
7378
  const resolvedStaticDir = path21.resolve(staticDir);
7287
7379
  if (!fs25.existsSync(resolvedStaticDir)) {
@@ -7292,9 +7384,10 @@ async function uploadStatic(options) {
7292
7384
  console.error(`${LOG_PREFIX} \u76EE\u5F55\u4E3A\u7A7A: ${resolvedStaticDir}\uFF0C\u8DF3\u8FC7\u4E0A\u4F20`);
7293
7385
  return;
7294
7386
  }
7295
- if (!fs25.existsSync(tosutilPath)) {
7387
+ const resolvedTosutil = resolveTosutilPath(tosutilPath);
7388
+ if (!resolvedTosutil) {
7296
7389
  throw new Error(
7297
- `tosutil \u4E0D\u5B58\u5728: ${tosutilPath}\u3002\u8BF7\u786E\u4FDD\u6D41\u6C34\u7EBF\u5DF2\u5728"\u4EA7\u7269\u6253\u5305\u4E0A\u4F20"\u6B65\u9AA4\u4E2D\u4E0B\u8F7D tosutil\u3002`
7390
+ `tosutil \u4E0D\u5B58\u5728: ${tosutilPath}\u3002\u8BF7\u786E\u4FDD tosutil \u5DF2\u5B89\u88C5\u5728\u7CFB\u7EDF PATH \u4E2D\u6216\u901A\u8FC7 --tosutil-path \u6307\u5B9A\u8DEF\u5F84\u3002`
7298
7391
  );
7299
7392
  }
7300
7393
  let uploadPrefix;
@@ -7317,7 +7410,7 @@ async function uploadStatic(options) {
7317
7410
  }
7318
7411
  console.error(`${LOG_PREFIX} \u4E0A\u4F20\u76EE\u6807: ${uploadPrefix}`);
7319
7412
  console.error(`${LOG_PREFIX} \u914D\u7F6E tosutil...`);
7320
- configureTosutil(tosutilPath, {
7413
+ configureTosutil(resolvedTosutil, {
7321
7414
  endpoint,
7322
7415
  region,
7323
7416
  accessKeyID,
@@ -7325,7 +7418,7 @@ async function uploadStatic(options) {
7325
7418
  sessionToken
7326
7419
  });
7327
7420
  console.error(`${LOG_PREFIX} \u4E0A\u4F20 ${resolvedStaticDir} -> ${uploadPrefix}`);
7328
- uploadToTos(tosutilPath, resolvedStaticDir, uploadPrefix);
7421
+ uploadToTos(resolvedTosutil, resolvedStaticDir, uploadPrefix);
7329
7422
  console.error(`${LOG_PREFIX} tosutil \u4E0A\u4F20\u5B8C\u6210`);
7330
7423
  console.error(`${LOG_PREFIX} \u8C03\u7528 callbackStatic (uploadID: ${uploadID})...`);
7331
7424
  const callbackResp = await uploadStaticAttachmentCallback(appId, bucketId, { uploadID });
@@ -7341,6 +7434,17 @@ async function uploadStatic(options) {
7341
7434
  process.exit(1);
7342
7435
  }
7343
7436
  }
7437
+ function resolveTosutilPath(tosutilPath) {
7438
+ if (path21.isAbsolute(tosutilPath)) {
7439
+ return fs25.existsSync(tosutilPath) ? tosutilPath : null;
7440
+ }
7441
+ try {
7442
+ const resolved = execFileSync("which", [tosutilPath], { encoding: "utf-8" }).trim();
7443
+ return resolved || null;
7444
+ } catch {
7445
+ return null;
7446
+ }
7447
+ }
7344
7448
  async function fetchPreUpload(appId, bucketId) {
7345
7449
  const response = await preUploadStaticAttachment(appId, bucketId);
7346
7450
  if (response.status_code !== "0") {
@@ -7435,7 +7539,7 @@ var uploadStaticCommand = {
7435
7539
  name: "upload-static",
7436
7540
  description: "Upload shared/static files to TOS",
7437
7541
  register(program) {
7438
- program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option("--static-dir <dir>", "Static files directory", "shared/static").option("--tosutil-path <path>", "Path to tosutil binary", "/workspace/tosutil").option("--endpoint <endpoint>", "TOS endpoint", "tos-cn-beijing.volces.com").option("--region <region>", "TOS region", "cn-beijing").action(async (options) => {
7542
+ program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option("--static-dir <dir>", "Static files directory", UPLOAD_STATIC_DEFAULTS.staticDir).option("--tosutil-path <path>", "Path to tosutil binary", UPLOAD_STATIC_DEFAULTS.tosutilPath).option("--endpoint <endpoint>", "TOS endpoint", UPLOAD_STATIC_DEFAULTS.endpoint).option("--region <region>", "TOS region", UPLOAD_STATIC_DEFAULTS.region).action(async (options) => {
7439
7543
  await uploadStatic(options);
7440
7544
  });
7441
7545
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.16-beta.7",
3
+ "version": "1.1.16-beta.9",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -3,7 +3,7 @@
3
3
  set -euo pipefail
4
4
 
5
5
  ROOT_DIR="$(pwd)"
6
- OUT_DIR="$ROOT_DIR/dist/server"
6
+ DIST_DIR="$ROOT_DIR/dist"
7
7
 
8
8
  # 记录总开始时间
9
9
  TOTAL_START=$(node -e "console.log(Date.now())")
@@ -78,25 +78,26 @@ print_time $STEP_START
78
78
  echo ""
79
79
 
80
80
  # ==================== 步骤 4 ====================
81
- echo "📦 [4/5] 准备 server 依赖产物"
81
+ echo "📦 [4/5] 准备产物"
82
82
  STEP_START=$(node -e "console.log(Date.now())")
83
83
 
84
- mkdir -p "$OUT_DIR/dist/client"
85
-
86
- # 移动 HTML(从 dist/client 移走,避免残留)
87
- mv "$ROOT_DIR/dist/client/"*.html "$OUT_DIR/dist/client/" || true
88
-
89
- # 拷贝 run.sh 文件
90
- cp "$ROOT_DIR/scripts/run.sh" "$OUT_DIR/"
84
+ # 拷贝 run.sh 到 dist/(prod 从 dist/ 启动,确保 cwd 一致性)
85
+ cp "$ROOT_DIR/scripts/run.sh" "$DIST_DIR/"
91
86
 
92
87
  # 拷贝 .env 文件(如果存在)
93
88
  if [ -f "$ROOT_DIR/.env" ]; then
94
- cp "$ROOT_DIR/.env" "$OUT_DIR/"
89
+ cp "$ROOT_DIR/.env" "$DIST_DIR/"
90
+ fi
91
+
92
+ # 移动 client 下的 HTML 文件到 dist/dist/client,保证 views 路径在 dev/prod 下一致
93
+ if [ -d "$DIST_DIR/client" ]; then
94
+ mkdir -p "$DIST_DIR/dist/client"
95
+ find "$DIST_DIR/client" -maxdepth 1 -name "*.html" -exec mv {} "$DIST_DIR/dist/client/" \;
95
96
  fi
96
97
 
97
98
  # 清理无用文件
98
- rm -rf "$ROOT_DIR/dist/scripts"
99
- rm -rf "$ROOT_DIR/dist/tsconfig.node.tsbuildinfo"
99
+ rm -rf "$DIST_DIR/scripts"
100
+ rm -rf "$DIST_DIR/tsconfig.node.tsbuildinfo"
100
101
 
101
102
  print_time $STEP_START
102
103
  echo ""
@@ -116,8 +117,8 @@ echo "构建完成"
116
117
  print_time $TOTAL_START
117
118
 
118
119
  # 输出产物信息
119
- DIST_SIZE=$(du -sh "$OUT_DIR" | cut -f1)
120
- NODE_MODULES_SIZE=$(du -sh "$ROOT_DIR/dist/node_modules" | cut -f1)
120
+ DIST_SIZE=$(du -sh "$DIST_DIR" | cut -f1)
121
+ NODE_MODULES_SIZE=$(du -sh "$DIST_DIR/node_modules" | cut -f1)
121
122
  echo ""
122
123
  echo "📊 构建产物统计:"
123
124
  echo " 产物大小: $DIST_SIZE"
@@ -6,11 +6,12 @@ const fs = require('fs');
6
6
  const path = require('path');
7
7
 
8
8
  const ROOT_DIR = path.resolve(__dirname, '..');
9
- const DIST_SERVER_DIR = path.join(ROOT_DIR, 'dist/server');
9
+ const DIST_DIR = path.join(ROOT_DIR, 'dist');
10
+ const DIST_SERVER_DIR = path.join(DIST_DIR, 'server');
10
11
  const ROOT_PACKAGE_JSON = path.join(ROOT_DIR, 'package.json');
11
12
  const ROOT_NODE_MODULES = path.join(ROOT_DIR, 'node_modules');
12
- const OUT_NODE_MODULES = path.join(ROOT_DIR, 'dist/node_modules');
13
- const OUT_PACKAGE_JSON = path.join(DIST_SERVER_DIR, 'package.json');
13
+ const OUT_NODE_MODULES = path.join(DIST_DIR, 'node_modules');
14
+ const OUT_PACKAGE_JSON = path.join(DIST_DIR, 'package.json');
14
15
 
15
16
  // Server 入口文件
16
17
  const SERVER_ENTRY = path.join(DIST_SERVER_DIR, 'main.js');
@@ -294,9 +295,6 @@ async function smartPrune() {
294
295
  version: originalPackage.version,
295
296
  private: true,
296
297
  dependencies: prunedDependencies,
297
- scripts: {
298
- start: originalPackage.scripts?.start || 'node main.js'
299
- },
300
298
  engines: originalPackage.engines
301
299
  };
302
300
 
@@ -2,4 +2,7 @@
2
2
  # This file is auto-generated by @lark-apaas/fullstack-cli
3
3
 
4
4
  # 生产环境下,启动服务
5
- npm run start
5
+ # dist/ 根目录执行,确保 process.cwd() 与 dev 模式一致
6
+ # dev: cwd = 项目根, shared/ client/ 可达
7
+ # prod: cwd = dist/, shared/ client/ 可达
8
+ NODE_ENV=production node server/main.js