@lark-apaas/fullstack-cli 1.1.63-alpha.20260826101001 → 1.1.63-alpha.20260826101330

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
@@ -2514,6 +2514,17 @@ function buildDefaultRules(opts) {
2514
2514
  command: "node scripts/hooks/run-precommit.js",
2515
2515
  overwrite: false
2516
2516
  },
2517
+ // 1d. Nest 开发态由平台 launcher 选择 SWC;已有声明保持用户版本/依赖分区。
2518
+ {
2519
+ type: "ensure-dev-dependency",
2520
+ name: "@swc/cli",
2521
+ version: "^0.5.2"
2522
+ },
2523
+ {
2524
+ type: "ensure-dev-dependency",
2525
+ name: "@swc/core",
2526
+ version: "^1.16.1"
2527
+ },
2517
2528
  // 2. 智能合并 nest-cli.json 配置(保留用户自定义的 assets、plugins 等)
2518
2529
  {
2519
2530
  from: "templates/nest-cli.json",
@@ -2588,6 +2599,19 @@ function buildDefaultRules(opts) {
2588
2599
  name: "lint",
2589
2600
  to: "node ./scripts/lint.js",
2590
2601
  ifStartsWith: "concurrently "
2602
+ },
2603
+ // 9a. 只迁移模板生成的精确默认值;用户追加参数或自定义命令保持不动。
2604
+ {
2605
+ type: "patch-script",
2606
+ name: "dev:server",
2607
+ to: "NODE_ENV=development node ./scripts/dev-server.js",
2608
+ ifEquals: "NODE_ENV=development nest start --watch"
2609
+ },
2610
+ {
2611
+ type: "patch-script",
2612
+ name: "dev:server",
2613
+ to: "NODE_ENV=development node ./scripts/dev-server.js",
2614
+ ifEquals: "nest start --watch"
2591
2615
  }
2592
2616
  ];
2593
2617
  if (!opts.disableGenOpenapi) {
@@ -2617,7 +2641,15 @@ function emptyProfile() {
2617
2641
  }
2618
2642
  function viteReactProfile() {
2619
2643
  return {
2620
- sync: [],
2644
+ sync: [
2645
+ // 覆盖 scripts/build.sh(回溯 public/* → dist/output/ 的修复)
2646
+ {
2647
+ from: "templates/vite-react/build.sh",
2648
+ to: "scripts/build.sh",
2649
+ type: "file",
2650
+ overwrite: true
2651
+ }
2652
+ ],
2621
2653
  permissions: {},
2622
2654
  activateGitHooks: true
2623
2655
  };
@@ -2759,7 +2791,9 @@ async function run2(options) {
2759
2791
  }
2760
2792
  const stack = resolveStack(userProjectRoot);
2761
2793
  try {
2762
- console.log(`[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`);
2794
+ console.log(
2795
+ `[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`
2796
+ );
2763
2797
  const config = genSyncConfig(stack, {
2764
2798
  disableGenOpenapi: options.disableGenOpenapi ?? false
2765
2799
  });
@@ -2778,7 +2812,9 @@ async function run2(options) {
2778
2812
  activateGitHooks(userProjectRoot);
2779
2813
  } catch (error) {
2780
2814
  const message = error instanceof Error ? error.message : String(error);
2781
- console.warn(`[fullstack-cli] \u26A0 Failed to activate git hooks: ${message}`);
2815
+ console.warn(
2816
+ `[fullstack-cli] \u26A0 Failed to activate git hooks: ${message}`
2817
+ );
2782
2818
  }
2783
2819
  }
2784
2820
  console.log("[fullstack-cli] Sync completed successfully \u2705");
@@ -2798,7 +2834,9 @@ function resolveStack(userProjectRoot) {
2798
2834
  return typeof meta.stack === "string" ? meta.stack : void 0;
2799
2835
  } catch (error) {
2800
2836
  const message = error instanceof Error ? error.message : String(error);
2801
- console.warn(`[fullstack-cli] \u26A0 Failed to read .spark/meta.json, fallback to default sync: ${message}`);
2837
+ console.warn(
2838
+ `[fullstack-cli] \u26A0 Failed to read .spark/meta.json, fallback to default sync: ${message}`
2839
+ );
2802
2840
  return void 0;
2803
2841
  }
2804
2842
  }
@@ -2819,12 +2857,22 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2819
2857
  }
2820
2858
  if (rule.type === "add-script") {
2821
2859
  const packageJsonPath = path5.join(userProjectRoot, "package.json");
2822
- addScript(packageJsonPath, rule.name, rule.command, rule.overwrite ?? false);
2860
+ addScript(
2861
+ packageJsonPath,
2862
+ rule.name,
2863
+ rule.command,
2864
+ rule.overwrite ?? false
2865
+ );
2866
+ return;
2867
+ }
2868
+ if (rule.type === "ensure-dev-dependency") {
2869
+ const packageJsonPath = path5.join(userProjectRoot, "package.json");
2870
+ ensureDevDependency(packageJsonPath, rule.name, rule.version);
2823
2871
  return;
2824
2872
  }
2825
2873
  if (rule.type === "patch-script") {
2826
2874
  const packageJsonPath = path5.join(userProjectRoot, "package.json");
2827
- patchScript(packageJsonPath, rule.name, rule.to, rule.ifStartsWith);
2875
+ patchScript(packageJsonPath, rule);
2828
2876
  return;
2829
2877
  }
2830
2878
  if (rule.type === "add-line") {
@@ -2852,7 +2900,12 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2852
2900
  syncDirectory(srcPath, destPath, rule.overwrite ?? true);
2853
2901
  break;
2854
2902
  case "file":
2855
- syncFile(srcPath, destPath, rule.overwrite ?? true, rule.onlyIfExists ?? false);
2903
+ syncFile(
2904
+ srcPath,
2905
+ destPath,
2906
+ rule.overwrite ?? true,
2907
+ rule.onlyIfExists ?? false
2908
+ );
2856
2909
  break;
2857
2910
  case "append":
2858
2911
  appendToFile(srcPath, destPath);
@@ -2861,7 +2914,9 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2861
2914
  }
2862
2915
  function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2863
2916
  if (onlyIfExists && !fs7.existsSync(dest)) {
2864
- console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`);
2917
+ console.log(
2918
+ `[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`
2919
+ );
2865
2920
  return;
2866
2921
  }
2867
2922
  const destDir = path5.dirname(dest);
@@ -2869,7 +2924,9 @@ function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2869
2924
  fs7.mkdirSync(destDir, { recursive: true });
2870
2925
  }
2871
2926
  if (fs7.existsSync(dest) && !overwrite) {
2872
- console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`);
2927
+ console.log(
2928
+ `[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`
2929
+ );
2873
2930
  return;
2874
2931
  }
2875
2932
  fs7.copyFileSync(src, dest);
@@ -2896,7 +2953,9 @@ function syncDirectory(src, dest, overwrite = true) {
2896
2953
  }
2897
2954
  });
2898
2955
  if (count > 0) {
2899
- console.log(`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`);
2956
+ console.log(
2957
+ `[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`
2958
+ );
2900
2959
  }
2901
2960
  }
2902
2961
  function appendToFile(src, dest) {
@@ -2906,7 +2965,9 @@ function appendToFile(src, dest) {
2906
2965
  existingContent = fs7.readFileSync(dest, "utf-8");
2907
2966
  }
2908
2967
  if (existingContent.includes(content.trim())) {
2909
- console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`);
2968
+ console.log(
2969
+ `[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`
2970
+ );
2910
2971
  return;
2911
2972
  }
2912
2973
  fs7.appendFileSync(dest, content);
@@ -2964,7 +3025,34 @@ function addScript(packageJsonPath, name, command, overwrite) {
2964
3025
  fs7.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
2965
3026
  console.log(`[fullstack-cli] \u2713 scripts.${name}`);
2966
3027
  }
2967
- function patchScript(packageJsonPath, name, to, ifStartsWith) {
3028
+ function ensureDevDependency(packageJsonPath, name, version) {
3029
+ if (!fs7.existsSync(packageJsonPath)) {
3030
+ console.log("[fullstack-cli] \u25CB package.json (not found)");
3031
+ return;
3032
+ }
3033
+ try {
3034
+ const content = fs7.readFileSync(packageJsonPath, "utf-8");
3035
+ const pkg2 = JSON.parse(content);
3036
+ const existingVersion = pkg2.dependencies?.[name] ?? pkg2.devDependencies?.[name];
3037
+ if (existingVersion !== void 0) {
3038
+ console.log(
3039
+ `[fullstack-cli] \u25CB ${name} (already declared as ${existingVersion})`
3040
+ );
3041
+ return;
3042
+ }
3043
+ pkg2.devDependencies ?? (pkg2.devDependencies = {});
3044
+ pkg2.devDependencies[name] = version;
3045
+ fs7.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
3046
+ console.log(`[fullstack-cli] \u2713 devDependencies.${name} = ${version}`);
3047
+ } catch (error) {
3048
+ const message = error instanceof Error ? error.message : String(error);
3049
+ console.warn(
3050
+ `[fullstack-cli] \u26A0 Could not ensure dependency ${name}: ${message}`
3051
+ );
3052
+ }
3053
+ }
3054
+ function patchScript(packageJsonPath, rule) {
3055
+ const { name, to } = rule;
2968
3056
  if (!fs7.existsSync(packageJsonPath)) {
2969
3057
  console.log(`[fullstack-cli] \u25CB package.json (not found)`);
2970
3058
  return;
@@ -2981,7 +3069,8 @@ function patchScript(packageJsonPath, name, to, ifStartsWith) {
2981
3069
  console.log(`[fullstack-cli] \u25CB scripts.${name} (already patched)`);
2982
3070
  return;
2983
3071
  }
2984
- if (!current.startsWith(ifStartsWith)) {
3072
+ const matches = rule.ifEquals !== void 0 ? current === rule.ifEquals : current.startsWith(rule.ifStartsWith);
3073
+ if (!matches) {
2985
3074
  console.warn(
2986
3075
  `[fullstack-cli] \u26A0 Skipped patching scripts.${name} because it has been customized`
2987
3076
  );
@@ -2992,7 +3081,9 @@ function patchScript(packageJsonPath, name, to, ifStartsWith) {
2992
3081
  console.log(`[fullstack-cli] \u2713 Patched scripts.${name}`);
2993
3082
  } catch (error) {
2994
3083
  const message = error instanceof Error ? error.message : String(error);
2995
- console.warn(`[fullstack-cli] \u26A0 Could not patch scripts.${name}: ${message}`);
3084
+ console.warn(
3085
+ `[fullstack-cli] \u26A0 Could not patch scripts.${name}: ${message}`
3086
+ );
2996
3087
  }
2997
3088
  }
2998
3089
  function addLineToFile(filePath, line) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.63-alpha.20260826101001",
3
+ "version": "1.1.63-alpha.20260826101330",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -146,21 +146,14 @@ STEP_START=$(node -e "console.log(Date.now())")
146
146
  # 使用 mv 而非 cp:HTML 不能上传到公网 CDN,移走后 dist/client 中不再包含 HTML
147
147
  if [ -d "$DIST_DIR/client" ]; then
148
148
  mkdir -p "$DIST_DIR/dist/client"
149
+ # public 先进(顶层 client 打包被排除;server setBaseViewsDir + 中间件同源 serve)
150
+ if [ -d "$ROOT_DIR/client/public" ]; then
151
+ cp -R "$ROOT_DIR/client/public/." "$DIST_DIR/dist/client/"
152
+ fi
153
+ # 构建产物 HTML 随后 move,覆盖 public 里的同名文件(保证入口页是构建版,不被 public 静默覆盖)
149
154
  find "$DIST_DIR/client" -maxdepth 1 -name "*.html" -exec mv {} "$DIST_DIR/dist/client/" \;
150
155
  fi
151
156
 
152
- # 拷贝 shared/static → dist/shared/static
153
- # Nest server 生产从 process.cwd() + shared/static 读 (nestjs-core static.controller.ts:100),
154
- # 生产 cwd = dist/, 所以目标是 dist/shared/static。coding-preset 的 static-assets plugin
155
- # 之前 closeBundle 拷贝, 现在删除后交给 build.sh 显式做 (跟 jsPage 的 output_static rsync 对齐)。
156
- # 排除 .ts/.tsx/.js/.jsx: 避免 shared/static/ 里若有开发脚本被误传到生产。
157
- if [ -d "$ROOT_DIR/shared/static" ]; then
158
- mkdir -p "$DIST_DIR/shared/static"
159
- rsync -a --exclude='*.ts' --exclude='*.tsx' --exclude='*.js' --exclude='*.jsx' \
160
- "$ROOT_DIR/shared/static/" "$DIST_DIR/shared/static/"
161
- echo " Static → dist/shared/static/"
162
- fi
163
-
164
157
  # server 相关产物准备(only_frontend_change=true 时跳过)
165
158
  if [[ "${only_frontend_change:-false}" == "true" ]]; then
166
159
  echo " [skip] 跳过 run.sh/.env 复制 (only_frontend_change=true)"
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { spawn } = require('node:child_process');
6
+
7
+ const FORWARDED_SIGNALS = ['SIGINT', 'SIGTERM'];
8
+
9
+ function resolveBuilder(projectRoot) {
10
+ const configPath = path.join(projectRoot, 'nest-cli.json');
11
+ if (!fs.existsSync(configPath)) {
12
+ return undefined;
13
+ }
14
+
15
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
16
+ const builder = config?.compilerOptions?.builder;
17
+ if (typeof builder === 'string') {
18
+ return builder;
19
+ }
20
+ if (
21
+ builder &&
22
+ typeof builder === 'object' &&
23
+ typeof builder.type === 'string'
24
+ ) {
25
+ return builder.type;
26
+ }
27
+ return builder === undefined ? undefined : 'custom';
28
+ }
29
+
30
+ function findMissingSwcDependency(projectRoot) {
31
+ for (const dependency of ['@swc/cli', '@swc/core']) {
32
+ try {
33
+ require.resolve(`${dependency}/package.json`, { paths: [projectRoot] });
34
+ } catch {
35
+ return dependency;
36
+ }
37
+ }
38
+ return undefined;
39
+ }
40
+
41
+ function resolveNestEntrypoint(projectRoot) {
42
+ return require.resolve('@nestjs/cli/bin/nest.js', { paths: [projectRoot] });
43
+ }
44
+
45
+ function startNest(projectRoot, nestArgs) {
46
+ const child = spawn(
47
+ process.execPath,
48
+ [resolveNestEntrypoint(projectRoot), ...nestArgs],
49
+ {
50
+ cwd: projectRoot,
51
+ env: process.env,
52
+ stdio: 'inherit',
53
+ }
54
+ );
55
+
56
+ const signalHandlers = new Map();
57
+ for (const signal of FORWARDED_SIGNALS) {
58
+ const handler = () => child.kill(signal);
59
+ signalHandlers.set(signal, handler);
60
+ process.once(signal, handler);
61
+ }
62
+
63
+ function removeSignalHandlers() {
64
+ for (const [signal, handler] of signalHandlers) {
65
+ process.removeListener(signal, handler);
66
+ }
67
+ }
68
+
69
+ child.once('error', error => {
70
+ removeSignalHandlers();
71
+ console.error(
72
+ `[miaoda:nest-compiler] failed to start Nest CLI: ${error.message}`
73
+ );
74
+ process.exitCode = 1;
75
+ });
76
+
77
+ child.once('exit', (code, signal) => {
78
+ removeSignalHandlers();
79
+ if (signal) {
80
+ process.kill(process.pid, signal);
81
+ return;
82
+ }
83
+ process.exitCode = code ?? 1;
84
+ });
85
+ }
86
+
87
+ function main() {
88
+ const projectRoot = process.cwd();
89
+ const forwardedArgs = process.argv.slice(2);
90
+ let explicitBuilder;
91
+
92
+ try {
93
+ explicitBuilder = resolveBuilder(projectRoot);
94
+ } catch (error) {
95
+ console.error(
96
+ `[miaoda:nest-compiler] MIAODA_NEST_SWC_FALLBACK reason=invalid_nest_cli_config message=${JSON.stringify(error.message)}`
97
+ );
98
+ console.log('[miaoda:nest-compiler] MIAODA_NEST_COMPILER=tsc');
99
+ startNest(projectRoot, ['start', '--watch', ...forwardedArgs]);
100
+ return;
101
+ }
102
+
103
+ if (explicitBuilder && explicitBuilder !== 'swc') {
104
+ console.log(
105
+ `[miaoda:nest-compiler] MIAODA_NEST_COMPILER=${explicitBuilder}`
106
+ );
107
+ startNest(projectRoot, ['start', '--watch', ...forwardedArgs]);
108
+ return;
109
+ }
110
+
111
+ const missingDependency = findMissingSwcDependency(projectRoot);
112
+ if (missingDependency) {
113
+ console.error(
114
+ `[miaoda:nest-compiler] MIAODA_NEST_SWC_FALLBACK reason=missing_dependency dependency=${missingDependency}`
115
+ );
116
+ console.log('[miaoda:nest-compiler] MIAODA_NEST_COMPILER=tsc');
117
+ startNest(projectRoot, ['start', '--watch', ...forwardedArgs]);
118
+ return;
119
+ }
120
+
121
+ console.log('[miaoda:nest-compiler] MIAODA_NEST_COMPILER=swc');
122
+ startNest(projectRoot, [
123
+ 'start',
124
+ '--watch',
125
+ '--builder',
126
+ 'swc',
127
+ '--type-check',
128
+ ...forwardedArgs,
129
+ ]);
130
+ }
131
+
132
+ if (require.main === module) {
133
+ main();
134
+ }
135
+
136
+ module.exports = {
137
+ findMissingSwcDependency,
138
+ resolveBuilder,
139
+ };
@@ -0,0 +1,61 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+ OUTPUT="$ROOT/dist/output"
6
+ OUTPUT_RESOURCE="$ROOT/dist/output_resource"
7
+ OUTPUT_STATIC="$ROOT/dist/output_static"
8
+
9
+ # 映射平台环境变量到 preset 期望的变量名
10
+ # MIAODA_APP_ID → /app/<appId> 作为客户端 base path
11
+ # MIAODA_RESOURCE_CDN_PREFIX → assets (JS/CSS) 的 CDN 前缀
12
+ # CLI 注入约定见 miaoda-cli src/services/deploy/modern/atoms/build.ts
13
+ export CLIENT_BASE_PATH="${MIAODA_APP_ID:+/app/$MIAODA_APP_ID}"
14
+ export ASSETS_CDN_PATH="${MIAODA_RESOURCE_CDN_PREFIX:-/}"
15
+ export STATIC_ASSETS_BASE_URL="${MIAODA_STATIC_CDN_PREFIX}"
16
+ export NODE_ENV="${NODE_ENV:-production}"
17
+
18
+ # 清理
19
+ rm -rf "$ROOT/dist"
20
+
21
+ # 1. Vite 构建 → dist/client/(相对于项目根目录输出)
22
+ npx vite build --outDir "$ROOT/dist/client" --emptyOutDir
23
+
24
+ # 2. public/ 静态资源 + HTML → dist/output/(模型 B:public = 应用根目录,同源 /app/<appId>/*)
25
+ mkdir -p "$OUTPUT"
26
+ # 2a. public/*:vite copyPublicDir 已把 public/* 平铺进 dist/client,但下面白名单只取
27
+ # *.html/routes.json,其余(favicon / 图片 / 运行时 fetch 的数据文件)会随 dist/client 被删。
28
+ # 从源 public/ 补拷到 output/(同源根),让 fetch('/x') / resolveAppUrl('/x') 线上可用。
29
+ # 先拷 public、再拷 HTML,保证构建产出的 index.html/routes.json 覆盖 public 里的同名文件(若有)。
30
+ if [ -d "$ROOT/public" ]; then
31
+ cp -R "$ROOT/public/." "$OUTPUT/"
32
+ fi
33
+ # 2b. HTML + routes.json(构建产物)
34
+ find "$ROOT/dist/client" -maxdepth 1 \( -name '*.html' -o -name 'routes.json' \) -exec cp {} "$OUTPUT/" \;
35
+
36
+ # 3. assets/ → dist/output_resource/(JS/CSS/字体,上传到 CDN)
37
+ if [ -d "$ROOT/dist/client/assets" ]; then
38
+ mkdir -p "$OUTPUT_RESOURCE"
39
+ cp -r "$ROOT/dist/client/assets" "$OUTPUT_RESOURCE/"
40
+ fi
41
+
42
+ # 4. 私有静态资源 → dist/output_static/(排除代码文件)
43
+ if [ -d "$ROOT/shared/static" ]; then
44
+ mkdir -p "$OUTPUT_STATIC"
45
+ rsync -a --exclude='*.ts' --exclude='*.tsx' --exclude='*.js' --exclude='*.jsx' "$ROOT/shared/static/" "$OUTPUT_STATIC/"
46
+ fi
47
+
48
+ # 5. capability 配置 → dist/output_capabilities/
49
+ if [ -d "$ROOT/shared/capabilities" ]; then
50
+ mkdir -p "$ROOT/dist/output_capabilities"
51
+ cp -r "$ROOT/shared/capabilities/." "$ROOT/dist/output_capabilities/"
52
+ fi
53
+
54
+ # 清理中间产物
55
+ rm -rf "$ROOT/dist/client"
56
+
57
+ echo "Build complete"
58
+ echo " HTML → dist/output/"
59
+ [ -d "$OUTPUT_RESOURCE" ] && echo " Resource → dist/output_resource/" || true
60
+ [ -d "$OUTPUT_STATIC" ] && echo " Static → dist/output_static/" || true
61
+ [ -d "$ROOT/dist/output_capabilities" ] && echo " Capabilities → dist/output_capabilities/" || true