@lark-apaas/fullstack-cli 1.1.63-alpha.20260826185602 → 1.1.63-alpha.20260826190313

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) {
@@ -2767,7 +2791,9 @@ async function run2(options) {
2767
2791
  }
2768
2792
  const stack = resolveStack(userProjectRoot);
2769
2793
  try {
2770
- console.log(`[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`);
2794
+ console.log(
2795
+ `[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`
2796
+ );
2771
2797
  const config = genSyncConfig(stack, {
2772
2798
  disableGenOpenapi: options.disableGenOpenapi ?? false
2773
2799
  });
@@ -2786,7 +2812,9 @@ async function run2(options) {
2786
2812
  activateGitHooks(userProjectRoot);
2787
2813
  } catch (error) {
2788
2814
  const message = error instanceof Error ? error.message : String(error);
2789
- 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
+ );
2790
2818
  }
2791
2819
  }
2792
2820
  console.log("[fullstack-cli] Sync completed successfully \u2705");
@@ -2806,7 +2834,9 @@ function resolveStack(userProjectRoot) {
2806
2834
  return typeof meta.stack === "string" ? meta.stack : void 0;
2807
2835
  } catch (error) {
2808
2836
  const message = error instanceof Error ? error.message : String(error);
2809
- 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
+ );
2810
2840
  return void 0;
2811
2841
  }
2812
2842
  }
@@ -2827,12 +2857,22 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2827
2857
  }
2828
2858
  if (rule.type === "add-script") {
2829
2859
  const packageJsonPath = path5.join(userProjectRoot, "package.json");
2830
- 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);
2831
2871
  return;
2832
2872
  }
2833
2873
  if (rule.type === "patch-script") {
2834
2874
  const packageJsonPath = path5.join(userProjectRoot, "package.json");
2835
- patchScript(packageJsonPath, rule.name, rule.to, rule.ifStartsWith);
2875
+ patchScript(packageJsonPath, rule);
2836
2876
  return;
2837
2877
  }
2838
2878
  if (rule.type === "add-line") {
@@ -2860,7 +2900,12 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2860
2900
  syncDirectory(srcPath, destPath, rule.overwrite ?? true);
2861
2901
  break;
2862
2902
  case "file":
2863
- syncFile(srcPath, destPath, rule.overwrite ?? true, rule.onlyIfExists ?? false);
2903
+ syncFile(
2904
+ srcPath,
2905
+ destPath,
2906
+ rule.overwrite ?? true,
2907
+ rule.onlyIfExists ?? false
2908
+ );
2864
2909
  break;
2865
2910
  case "append":
2866
2911
  appendToFile(srcPath, destPath);
@@ -2869,7 +2914,9 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2869
2914
  }
2870
2915
  function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2871
2916
  if (onlyIfExists && !fs7.existsSync(dest)) {
2872
- 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
+ );
2873
2920
  return;
2874
2921
  }
2875
2922
  const destDir = path5.dirname(dest);
@@ -2877,7 +2924,9 @@ function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2877
2924
  fs7.mkdirSync(destDir, { recursive: true });
2878
2925
  }
2879
2926
  if (fs7.existsSync(dest) && !overwrite) {
2880
- 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
+ );
2881
2930
  return;
2882
2931
  }
2883
2932
  fs7.copyFileSync(src, dest);
@@ -2904,7 +2953,9 @@ function syncDirectory(src, dest, overwrite = true) {
2904
2953
  }
2905
2954
  });
2906
2955
  if (count > 0) {
2907
- 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
+ );
2908
2959
  }
2909
2960
  }
2910
2961
  function appendToFile(src, dest) {
@@ -2914,7 +2965,9 @@ function appendToFile(src, dest) {
2914
2965
  existingContent = fs7.readFileSync(dest, "utf-8");
2915
2966
  }
2916
2967
  if (existingContent.includes(content.trim())) {
2917
- 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
+ );
2918
2971
  return;
2919
2972
  }
2920
2973
  fs7.appendFileSync(dest, content);
@@ -2972,7 +3025,34 @@ function addScript(packageJsonPath, name, command, overwrite) {
2972
3025
  fs7.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
2973
3026
  console.log(`[fullstack-cli] \u2713 scripts.${name}`);
2974
3027
  }
2975
- 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;
2976
3056
  if (!fs7.existsSync(packageJsonPath)) {
2977
3057
  console.log(`[fullstack-cli] \u25CB package.json (not found)`);
2978
3058
  return;
@@ -2989,7 +3069,8 @@ function patchScript(packageJsonPath, name, to, ifStartsWith) {
2989
3069
  console.log(`[fullstack-cli] \u25CB scripts.${name} (already patched)`);
2990
3070
  return;
2991
3071
  }
2992
- if (!current.startsWith(ifStartsWith)) {
3072
+ const matches = rule.ifEquals !== void 0 ? current === rule.ifEquals : current.startsWith(rule.ifStartsWith);
3073
+ if (!matches) {
2993
3074
  console.warn(
2994
3075
  `[fullstack-cli] \u26A0 Skipped patching scripts.${name} because it has been customized`
2995
3076
  );
@@ -3000,7 +3081,9 @@ function patchScript(packageJsonPath, name, to, ifStartsWith) {
3000
3081
  console.log(`[fullstack-cli] \u2713 Patched scripts.${name}`);
3001
3082
  } catch (error) {
3002
3083
  const message = error instanceof Error ? error.message : String(error);
3003
- 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
+ );
3004
3087
  }
3005
3088
  }
3006
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.20260826185602",
3
+ "version": "1.1.63-alpha.20260826190313",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -154,18 +154,6 @@ if [ -d "$DIST_DIR/client" ]; then
154
154
  find "$DIST_DIR/client" -maxdepth 1 -name "*.html" -exec mv {} "$DIST_DIR/dist/client/" \;
155
155
  fi
156
156
 
157
- # 拷贝 shared/static → dist/shared/static
158
- # Nest server 生产从 process.cwd() + shared/static 读 (nestjs-core static.controller.ts:100),
159
- # 生产 cwd = dist/, 所以目标是 dist/shared/static。coding-preset 的 static-assets plugin
160
- # 之前 closeBundle 拷贝, 现在删除后交给 build.sh 显式做 (跟 jsPage 的 output_static rsync 对齐)。
161
- # 排除 .ts/.tsx/.js/.jsx: 避免 shared/static/ 里若有开发脚本被误传到生产。
162
- if [ -d "$ROOT_DIR/shared/static" ]; then
163
- mkdir -p "$DIST_DIR/shared/static"
164
- rsync -a --exclude='*.ts' --exclude='*.tsx' --exclude='*.js' --exclude='*.jsx' \
165
- "$ROOT_DIR/shared/static/" "$DIST_DIR/shared/static/"
166
- echo " Static → dist/shared/static/"
167
- fi
168
-
169
157
  # server 相关产物准备(only_frontend_change=true 时跳过)
170
158
  if [[ "${only_frontend_change:-false}" == "true" ]]; then
171
159
  echo " [skip] 跳过 run.sh/.env 复制 (only_frontend_change=true)"
@@ -0,0 +1,285 @@
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 resolveInsideProject(projectRoot, configuredPath) {
46
+ const resolvedPath = path.resolve(projectRoot, configuredPath);
47
+ const relativePath = path.relative(projectRoot, resolvedPath);
48
+ if (
49
+ relativePath === '..' ||
50
+ relativePath.startsWith(`..${path.sep}`) ||
51
+ path.isAbsolute(relativePath)
52
+ ) {
53
+ return undefined;
54
+ }
55
+ return resolvedPath;
56
+ }
57
+
58
+ function readJsonStringProperty(filePath, propertyName, fallback) {
59
+ if (!fs.existsSync(filePath)) {
60
+ return fallback;
61
+ }
62
+ const content = fs.readFileSync(filePath, 'utf8');
63
+ const match = content.match(
64
+ new RegExp(`"${propertyName}"\\s*:\\s*"([^"\\r\\n]+)"`)
65
+ );
66
+ return match?.[1] ?? fallback;
67
+ }
68
+
69
+ function cleanStaleSwcOutputs(projectRoot) {
70
+ const nestConfigPath = path.join(projectRoot, 'nest-cli.json');
71
+ const nestConfig = JSON.parse(fs.readFileSync(nestConfigPath, 'utf8'));
72
+ const sourceRootName =
73
+ typeof nestConfig.sourceRoot === 'string' ? nestConfig.sourceRoot : 'src';
74
+ const tsConfigName =
75
+ typeof nestConfig.compilerOptions?.tsConfigPath === 'string'
76
+ ? nestConfig.compilerOptions.tsConfigPath
77
+ : 'tsconfig.json';
78
+ const tsConfigPath = resolveInsideProject(projectRoot, tsConfigName);
79
+ const sourceRoot = resolveInsideProject(projectRoot, sourceRootName);
80
+ if (!tsConfigPath || !sourceRoot || !fs.existsSync(sourceRoot)) {
81
+ return 0;
82
+ }
83
+
84
+ const outDirName = readJsonStringProperty(tsConfigPath, 'outDir', 'dist');
85
+ const rootDirName = readJsonStringProperty(
86
+ tsConfigPath,
87
+ 'rootDir',
88
+ sourceRootName
89
+ );
90
+ const outDir = resolveInsideProject(projectRoot, outDirName);
91
+ const rootDir = resolveInsideProject(projectRoot, rootDirName);
92
+ if (!outDir) {
93
+ return 0;
94
+ }
95
+ const emitRoots = [sourceRoot];
96
+ if (rootDir && rootDir !== sourceRoot) {
97
+ emitRoots.push(rootDir);
98
+ }
99
+
100
+ let removed = 0;
101
+ const pendingDirectories = [sourceRoot];
102
+ while (pendingDirectories.length > 0) {
103
+ const currentDirectory = pendingDirectories.pop();
104
+ for (const entry of fs.readdirSync(currentDirectory, {
105
+ withFileTypes: true,
106
+ })) {
107
+ const sourcePath = path.join(currentDirectory, entry.name);
108
+ if (entry.isDirectory()) {
109
+ pendingDirectories.push(sourcePath);
110
+ continue;
111
+ }
112
+ if (!entry.isFile() || entry.name.endsWith('.d.ts')) {
113
+ continue;
114
+ }
115
+
116
+ const sourceExtension = ['.tsx', '.mts', '.cts', '.ts'].find(extension =>
117
+ entry.name.endsWith(extension)
118
+ );
119
+ if (!sourceExtension) {
120
+ continue;
121
+ }
122
+ const outputExtension =
123
+ sourceExtension === '.mts'
124
+ ? '.mjs'
125
+ : sourceExtension === '.cts'
126
+ ? '.cjs'
127
+ : '.js';
128
+ const declarationExtension =
129
+ sourceExtension === '.mts'
130
+ ? '.d.mts'
131
+ : sourceExtension === '.cts'
132
+ ? '.d.cts'
133
+ : '.d.ts';
134
+ const sourceBasePath = sourcePath.slice(0, -sourceExtension.length);
135
+ for (const suffix of [
136
+ outputExtension,
137
+ `${outputExtension}.map`,
138
+ declarationExtension,
139
+ `${declarationExtension}.map`,
140
+ ]) {
141
+ const generatedSourceSibling = `${sourceBasePath}${suffix}`;
142
+ if (fs.existsSync(generatedSourceSibling)) {
143
+ fs.rmSync(generatedSourceSibling, { force: true });
144
+ removed += 1;
145
+ }
146
+ }
147
+
148
+ for (const emitRoot of emitRoots) {
149
+ const relativeSourcePath = path.relative(emitRoot, sourcePath);
150
+ if (
151
+ relativeSourcePath === '..' ||
152
+ relativeSourcePath.startsWith(`..${path.sep}`) ||
153
+ path.isAbsolute(relativeSourcePath)
154
+ ) {
155
+ continue;
156
+ }
157
+
158
+ const outputBasePath = path.join(
159
+ outDir,
160
+ relativeSourcePath.slice(0, -sourceExtension.length)
161
+ );
162
+ for (const suffix of [
163
+ outputExtension,
164
+ `${outputExtension}.map`,
165
+ declarationExtension,
166
+ `${declarationExtension}.map`,
167
+ ]) {
168
+ const outputPath = `${outputBasePath}${suffix}`;
169
+ if (fs.existsSync(outputPath)) {
170
+ fs.rmSync(outputPath, { force: true });
171
+ removed += 1;
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ return removed;
178
+ }
179
+
180
+ function startNest(projectRoot, nestArgs) {
181
+ const child = spawn(
182
+ process.execPath,
183
+ [resolveNestEntrypoint(projectRoot), ...nestArgs],
184
+ {
185
+ cwd: projectRoot,
186
+ env: process.env,
187
+ stdio: 'inherit',
188
+ }
189
+ );
190
+
191
+ const signalHandlers = new Map();
192
+ for (const signal of FORWARDED_SIGNALS) {
193
+ const handler = () => child.kill(signal);
194
+ signalHandlers.set(signal, handler);
195
+ process.once(signal, handler);
196
+ }
197
+
198
+ function removeSignalHandlers() {
199
+ for (const [signal, handler] of signalHandlers) {
200
+ process.removeListener(signal, handler);
201
+ }
202
+ }
203
+
204
+ child.once('error', error => {
205
+ removeSignalHandlers();
206
+ console.error(
207
+ `[miaoda:nest-compiler] failed to start Nest CLI: ${error.message}`
208
+ );
209
+ process.exitCode = 1;
210
+ });
211
+
212
+ child.once('exit', (code, signal) => {
213
+ removeSignalHandlers();
214
+ if (signal) {
215
+ process.kill(process.pid, signal);
216
+ return;
217
+ }
218
+ process.exitCode = code ?? 1;
219
+ });
220
+ }
221
+
222
+ function main() {
223
+ const projectRoot = process.cwd();
224
+ const forwardedArgs = process.argv.slice(2);
225
+ let explicitBuilder;
226
+
227
+ try {
228
+ explicitBuilder = resolveBuilder(projectRoot);
229
+ } catch (error) {
230
+ console.error(
231
+ `[miaoda:nest-compiler] MIAODA_NEST_SWC_FALLBACK reason=invalid_nest_cli_config message=${JSON.stringify(error.message)}`
232
+ );
233
+ console.log('[miaoda:nest-compiler] MIAODA_NEST_COMPILER=tsc');
234
+ startNest(projectRoot, ['start', '--watch', ...forwardedArgs]);
235
+ return;
236
+ }
237
+
238
+ if (explicitBuilder && explicitBuilder !== 'swc') {
239
+ console.log(
240
+ `[miaoda:nest-compiler] MIAODA_NEST_COMPILER=${explicitBuilder}`
241
+ );
242
+ startNest(projectRoot, ['start', '--watch', ...forwardedArgs]);
243
+ return;
244
+ }
245
+
246
+ const missingDependency = findMissingSwcDependency(projectRoot);
247
+ if (missingDependency) {
248
+ console.error(
249
+ `[miaoda:nest-compiler] MIAODA_NEST_SWC_FALLBACK reason=missing_dependency dependency=${missingDependency}`
250
+ );
251
+ console.log('[miaoda:nest-compiler] MIAODA_NEST_COMPILER=tsc');
252
+ startNest(projectRoot, ['start', '--watch', ...forwardedArgs]);
253
+ return;
254
+ }
255
+
256
+ try {
257
+ const removed = cleanStaleSwcOutputs(projectRoot);
258
+ console.log(
259
+ `[miaoda:nest-compiler] MIAODA_NEST_SWC_CLEANUP removed=${removed}`
260
+ );
261
+ } catch (error) {
262
+ console.error(
263
+ `[miaoda:nest-compiler] MIAODA_NEST_SWC_CLEANUP_FAILED message=${JSON.stringify(error.message)}`
264
+ );
265
+ }
266
+ console.log('[miaoda:nest-compiler] MIAODA_NEST_COMPILER=swc');
267
+ startNest(projectRoot, [
268
+ 'start',
269
+ '--watch',
270
+ '--builder',
271
+ 'swc',
272
+ '--type-check',
273
+ ...forwardedArgs,
274
+ ]);
275
+ }
276
+
277
+ if (require.main === module) {
278
+ main();
279
+ }
280
+
281
+ module.exports = {
282
+ cleanStaleSwcOutputs,
283
+ findMissingSwcDependency,
284
+ resolveBuilder,
285
+ };