@lark-apaas/miaoda-cli 0.1.16-alpha.ab80f52 → 0.1.16-alpha.c0b0ae2

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.
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.applyMigrateRules = applyMigrateRules;
7
7
  exports.rewriteViteConfigForFullstack = rewriteViteConfigForFullstack;
8
+ exports.rewriteAliasSrcPaths = rewriteAliasSrcPaths;
8
9
  const node_fs_1 = __importDefault(require("node:fs"));
9
10
  const node_path_1 = __importDefault(require("node:path"));
10
11
  const sync_rule_1 = require("./sync-rule");
@@ -182,75 +183,125 @@ function applyCodemodViteConfigFullstack(rule, opts) {
182
183
  return { rule, action: 'skipped', path: rel };
183
184
  }
184
185
  const before = node_fs_1.default.readFileSync(dest, 'utf-8');
185
- const { text, changed, alreadyDone } = rewriteViteConfigForFullstack(before);
186
- if (alreadyDone) {
187
- (0, logger_1.log)(logPrefix, ` ○ ${rel} (fullstack: true already present)`);
186
+ const { text, changed, alreadyDone, aliasRewriteCount } = rewriteViteConfigForFullstack(before);
187
+ if (alreadyDone && aliasRewriteCount === 0) {
188
+ (0, logger_1.log)(logPrefix, ` ○ ${rel} (fullstack: true already present, no alias to rewrite)`);
188
189
  return { rule, action: 'noop', path: rel };
189
190
  }
190
- if (!changed) {
191
+ if (!changed && aliasRewriteCount === 0) {
191
192
  (0, logger_1.log)(logPrefix, ` ⚠ ${rel} (defineConfig call not found; please add { fullstack: true } manually)`);
192
193
  return { rule, action: 'noop', path: rel, detail: 'defineConfig call not found' };
193
194
  }
194
195
  node_fs_1.default.writeFileSync(dest, text);
195
- (0, logger_1.log)(logPrefix, ` ✓ ${rel} (added { fullstack: true } to defineConfig)`);
196
- return { rule, action: 'patched', path: rel };
196
+ const parts = [];
197
+ if (changed && !alreadyDone)
198
+ parts.push('added { fullstack: true }');
199
+ if (aliasRewriteCount > 0)
200
+ parts.push(`rewrote ${aliasRewriteCount.toString()} alias path(s) src → client/src`);
201
+ (0, logger_1.log)(logPrefix, ` ✓ ${rel} (${parts.join(', ')})`);
202
+ return { rule, action: 'patched', path: rel, detail: parts.join('; ') };
197
203
  }
198
204
  /**
199
- * 给 vite.config.ts 的 `defineConfig(...)` 调用加上 `{ fullstack: true }` 第二参。
205
+ * 给 vite.config.ts 的 `defineConfig(...)` 调用加上 `{ fullstack: true }` 第二参,
206
+ * 顺便把 alias 字符串里 `src` 起头的路径重写到 `client/src`。
200
207
  *
201
- * 支持的形态:
208
+ * 1) defineConfig 改写支持的形态:
202
209
  * defineConfig({ ... }) → defineConfig({ ... }, { fullstack: true })
203
210
  * defineConfig({ ... }, { ... }) → 已有第二参,把 fullstack: true 注入对象字面量
204
211
  * defineConfig() → defineConfig({}, { fullstack: true })
212
+ * 跨多行 + 嵌套 arrow function / 字符串 / 模板字符串都要保留,所以用括号匹配而非正则。
213
+ * 已经含 `fullstack: true` 字面量则跳过(视为已迁移)。
205
214
  *
206
- * 跨多行 + 嵌套 arrow function / 字符串 / 模板字符串都要保留,所以用括号匹配而非正则。
207
- * 已经含 `fullstack: true` 字面量则跳过(视为已迁移)。
215
+ * 2) alias 路径重写:vite-react 时代 user 业务代码在 `src/`,迁移后搬到 `client/src/`,
216
+ * vite.config 里的 alias(典型如 `'@': path.resolve(__dirname, 'src')`)必须同步改:
217
+ * path.resolve(<args>, 'src') → 'src' 替换为 'client/src'
218
+ * path.resolve(<args>, 'src/<rest>') → 同上
219
+ * path.join(<args>, 'src') → 同上
220
+ * './src' / './src/<rest>' 字符串 → './client/src' / './client/src/<rest>'
221
+ * 边界:仅匹配整段 'src' 起头(避免 'sub/src' 误改),保留单/双引号原样。
208
222
  */
209
223
  function rewriteViteConfigForFullstack(source) {
224
+ // ── Step 1: 先处理 alias 路径(在 defineConfig 改写之前做,避免位置串行扰乱) ──
225
+ const { text: afterAlias, count: aliasRewriteCount } = rewriteAliasSrcPaths(source);
226
+ // ── Step 2: 加 fullstack: true ──
210
227
  const callRe = /defineConfig\s*\(/g;
211
- const m = callRe.exec(source);
228
+ const m = callRe.exec(afterAlias);
212
229
  if (m === null) {
213
- return { text: source, changed: false, alreadyDone: false };
230
+ return { text: afterAlias, changed: false, alreadyDone: false, aliasRewriteCount };
214
231
  }
215
232
  const openIdx = m.index + m[0].length - 1; // 指向 '('
216
- const closeIdx = findMatchingParen(source, openIdx);
233
+ const closeIdx = findMatchingParen(afterAlias, openIdx);
217
234
  if (closeIdx < 0) {
218
- return { text: source, changed: false, alreadyDone: false };
235
+ return { text: afterAlias, changed: false, alreadyDone: false, aliasRewriteCount };
219
236
  }
220
- const argsRaw = source.slice(openIdx + 1, closeIdx);
237
+ const argsRaw = afterAlias.slice(openIdx + 1, closeIdx);
221
238
  if (/fullstack\s*:\s*true/.test(argsRaw)) {
222
- return { text: source, changed: false, alreadyDone: true };
239
+ return { text: afterAlias, changed: false, alreadyDone: true, aliasRewriteCount };
223
240
  }
224
- const { firstArgEnd, secondArgRange } = splitFirstTwoArgs(source, openIdx + 1, closeIdx);
241
+ const { firstArgEnd, secondArgRange } = splitFirstTwoArgs(afterAlias, openIdx + 1, closeIdx);
225
242
  if (secondArgRange === null) {
226
243
  // 单参 / 无参:在第一参之后插 ", { fullstack: true }"
227
244
  const insertAt = firstArgEnd ?? openIdx + 1;
228
- const head = source.slice(0, insertAt);
229
- const tail = source.slice(insertAt, closeIdx);
245
+ const head = afterAlias.slice(0, insertAt);
246
+ const tail = afterAlias.slice(insertAt, closeIdx);
230
247
  // 第一参是否非空(去空白)
231
- const arg1 = source.slice(openIdx + 1, insertAt).trim();
248
+ const arg1 = afterAlias.slice(openIdx + 1, insertAt).trim();
232
249
  const sep = arg1.length === 0 ? '{}, { fullstack: true }' : ', { fullstack: true }';
233
250
  return {
234
- text: head + (arg1.length === 0 ? sep : sep) + tail + source.slice(closeIdx),
251
+ text: head + sep + tail + afterAlias.slice(closeIdx),
235
252
  changed: true,
236
253
  alreadyDone: false,
254
+ aliasRewriteCount,
237
255
  };
238
256
  }
239
257
  // 双参:在第二参对象 `{` 之后插 ` fullstack: true,`
240
- const arg2 = source.slice(secondArgRange.start, secondArgRange.end);
258
+ const arg2 = afterAlias.slice(secondArgRange.start, secondArgRange.end);
241
259
  const objOpen = arg2.indexOf('{');
242
260
  if (objOpen < 0) {
243
261
  // 第二参不是对象字面量(罕见),保守跳过
244
- return { text: source, changed: false, alreadyDone: false };
262
+ return { text: afterAlias, changed: false, alreadyDone: false, aliasRewriteCount };
245
263
  }
246
264
  const absObjOpen = secondArgRange.start + objOpen;
247
265
  const inserted = '\n fullstack: true,';
248
266
  return {
249
- text: source.slice(0, absObjOpen + 1) + inserted + source.slice(absObjOpen + 1),
267
+ text: afterAlias.slice(0, absObjOpen + 1) + inserted + afterAlias.slice(absObjOpen + 1),
250
268
  changed: true,
251
269
  alreadyDone: false,
270
+ aliasRewriteCount,
252
271
  };
253
272
  }
273
+ /**
274
+ * vite.config 里 alias 路径重写:`src` 起头的路径加 `client/` 前缀。
275
+ *
276
+ * 匹配的形态(不限制在 resolve.alias 块内 —— vite.config 顶层很少出现非 alias 用的
277
+ * `src` 字符串,业务 import 走 `@/...`,所以全文匹配是安全的):
278
+ *
279
+ * path.resolve(<args>, 'src') → path.resolve(<args>, 'client/src')
280
+ * path.resolve(<args>, 'src/<rest>') → path.resolve(<args>, 'client/src/<rest>')
281
+ * path.join(<args>, 'src') → 同上
282
+ * './src' / './src/<rest>'(独立字符串) → './client/src' / './client/src/<rest>'
283
+ *
284
+ * 已含 `client/src` 的不动(视为已迁移),`sub/src` 之类(src 不在词首)不动。
285
+ */
286
+ function rewriteAliasSrcPaths(source) {
287
+ let count = 0;
288
+ // path.resolve(...) / path.join(...) 的最后一个参数是 'src' 或 'src/...' 起头
289
+ // 匹配 ['"]src[/'"],保留引号
290
+ const pathCallRe = /(\bpath\s*\.\s*(?:resolve|join)\s*\([^)]*?,\s*)(['"`])(src)(\/[^'"`]*)?(['"`])/g;
291
+ let next = source.replace(pathCallRe, (_match, prefix, q1, _src, rest, q2) => {
292
+ count++;
293
+ return `${prefix}${q1}client/src${rest ?? ''}${q2}`;
294
+ });
295
+ // 独立的相对路径字符串字面量 './src' 或 './src/...'(不在 path.resolve 参数里)
296
+ // 注意:path.resolve / join 已被上一步处理,这里只匹配剩下的 './src...' 字面量。
297
+ // 这种用法在 vite.config 较少(典型是直接给 alias 字符串值),但兜底处理。
298
+ const relativeRe = /(['"`])\.\/(src)(\/[^'"`]*)?(['"`])/g;
299
+ next = next.replace(relativeRe, (_match, q1, _src, rest, q2) => {
300
+ count++;
301
+ return `${q1}./client/src${rest ?? ''}${q2}`;
302
+ });
303
+ return { text: next, count };
304
+ }
254
305
  /**
255
306
  * 从 openIdx(指向 `(`)开始扫到匹配的 `)`,跳过 string / template / 括号嵌套。
256
307
  * 找不到匹配返回 -1。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/miaoda-cli",
3
- "version": "0.1.16-alpha.ab80f52",
3
+ "version": "0.1.16-alpha.c0b0ae2",
4
4
  "description": "Miaoda 平台命令行工具,面向 Agent 调用",
5
5
  "type": "commonjs",
6
6
  "bin": {