@lark-apaas/miaoda-cli 0.1.16-alpha.0 → 0.1.16-alpha.f6825fa

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.
@@ -16,7 +16,6 @@ function registerDeployCommandsModern(program) {
16
16
  .description('触发 modern 应用发布:本地构建 + 本地部署(preRelease + localPublish)')
17
17
  .option('--dir <path>', '项目目录', '.')
18
18
  .option('--skip-build', '跳过 build 步骤(已构建好时使用)', false)
19
- .option('--conf <json>', '关联元信息 JSON,仅识别 checkPointVersion / commitID 两字段')
20
19
  .addHelpText('after', `
21
20
  不要用异步模式或后台模式调用 deploy,否则调用可能提前结束,Agent 会误判发布已完成。
22
21
 
@@ -33,12 +32,6 @@ function registerDeployCommandsModern(program) {
33
32
  6. savePluginInstances(扫描 dist/output_capabilities/*.json 批量注册)
34
33
  7. finalizeLocalRelease(Finished|Failed)
35
34
 
36
- --conf(关联元信息透传)
37
- 值为 JSON string,只识别两个字段,其余 key 忽略:
38
- checkPointVersion 关联的 checkpoint 版本
39
- commitID 关联的代码 commit ID
40
- 两字段均可选;非法 JSON / 非对象 / 字段值非字符串会报错。
41
-
42
35
  JSON 输出(stdout)
43
36
  {"data": {"appId": "...", "version": <n>, "url": "...", "releaseID": "...", "preReleaseID": "..."}}
44
37
 
@@ -46,14 +39,12 @@ JSON 输出(stdout)
46
39
  $ miaoda deploy
47
40
  $ miaoda deploy --dir ./my-app
48
41
  $ miaoda deploy --skip-build
49
- $ miaoda deploy --conf '{"checkPointVersion":"v3","commitID":"a1b2c3d"}'
50
42
  `);
51
43
  deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
52
44
  await (0, modern_1.handleDeployModern)({
53
45
  dir: rawOpts.dir ?? '.',
54
46
  appId: (0, shared_1.resolveAppId)({}),
55
47
  skipBuild: rawOpts.skipBuild,
56
- conf: rawOpts.conf,
57
48
  });
58
49
  }));
59
50
  }
@@ -3,46 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.parseDeployConf = parseDeployConf;
7
6
  exports.handleDeployModern = handleDeployModern;
8
7
  const node_path_1 = __importDefault(require("node:path"));
9
8
  const output_1 = require("../../../utils/output");
10
- const error_1 = require("../../../utils/error");
11
9
  const index_1 = require("../../../services/deploy/modern/index");
12
- const CONF_EXPECTED = '期望形如 {"checkPointVersion":"...","commitID":"..."} 的 JSON 对象';
13
- /**
14
- * 解析 `--conf` JSON string,只取 checkPointVersion / commitID 两个白名单字段。
15
- * - 未传 → 返回 {}(行为不变)。
16
- * - 非法 JSON / 非 object / 字段值非 string → 抛 DEPLOY_CONF_INVALID。
17
- * - 空串字段视为未提供(不进 body,保持可选语义);未知 key 忽略。
18
- */
19
- function parseDeployConf(raw) {
20
- if (raw === undefined || raw === '')
21
- return {};
22
- let parsed;
23
- try {
24
- parsed = JSON.parse(raw);
25
- }
26
- catch {
27
- throw new error_1.AppError('DEPLOY_CONF_INVALID', `--conf 不是合法 JSON,${CONF_EXPECTED}`);
28
- }
29
- if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
30
- throw new error_1.AppError('DEPLOY_CONF_INVALID', `--conf 必须是 JSON 对象,${CONF_EXPECTED}`);
31
- }
32
- const source = parsed;
33
- const conf = {};
34
- for (const key of ['checkPointVersion', 'commitID']) {
35
- const value = source[key];
36
- if (value === undefined)
37
- continue;
38
- if (typeof value !== 'string') {
39
- throw new error_1.AppError('DEPLOY_CONF_INVALID', `--conf.${key} 必须是字符串,${CONF_EXPECTED}`);
40
- }
41
- if (value !== '')
42
- conf[key] = value;
43
- }
44
- return conf;
45
- }
46
10
  /**
47
11
  * miaoda deploy(modern scene 专用,CLI 表面对齐 openclaw-cli)
48
12
  *
@@ -50,13 +14,10 @@ function parseDeployConf(raw) {
50
14
  */
51
15
  async function handleDeployModern(opts) {
52
16
  const projectDir = node_path_1.default.resolve(opts.dir);
53
- const conf = parseDeployConf(opts.conf);
54
17
  const result = await (0, index_1.runModernDeploy)({
55
18
  projectDir,
56
19
  appId: opts.appId,
57
20
  skipBuild: opts.skipBuild ?? false,
58
- checkPointVersion: conf.checkPointVersion,
59
- commitID: conf.commitID,
60
21
  });
61
22
  (0, output_1.emit)({
62
23
  data: {
@@ -33,28 +33,25 @@ exports.MIGRATE_CONFIG = {
33
33
  // 依赖集合变了,lockfile 重新生成(首次 npm install 时)
34
34
  { type: 'delete-file', to: 'package-lock.json' },
35
35
  // ===== 3. 从 user package.json 删除 vite-react 专属字段 =====
36
- // 这步在 merge-json 之前,确保 vite-react preset 不残留。
37
- // merge-json 是"template 覆盖同名 key + user 多出 key 保留",所以 user 的 vite-react
38
- // 专属字段不会被自动清理 —— 必须显式删。
36
+ // 这步在 merge-json 之前,确保 vite-react 的残留不会被 merge-json 兜底"保留 user 多出
37
+ // key"逻辑留下来。
39
38
  //
40
- // 注意:不删 dependencies.@lark-apaas/client-toolkit-lite —— lite 是 vite-react 时代的
41
- // 入门 wrapper(顶层 38 export),完整版 @lark-apaas/client-toolkit 只暴露 ~20
42
- // 对等出口(components/* / hooks/* / utils/* / tools/* / 顶层 alias),剩下 ~18 个 lite
43
- // symbolcopyToClipboard / getAppId / clsxWithTw / isMobile / avatarImages 等)在 full
44
- // 公共 API 里没有对等物。lite 跟 full 不是简单替换关系,而是不同抽象层级。强行替换
45
- // 会让 build 失败;让两个包共存(lite 很轻量)是当前的现实选择。后续若 client-toolkit
46
- // 补全 utility 出口,可启用 src/utils/codemod-client-toolkit-lite.ts 里已实现的 codemod
47
- // rule(在 MigrateRule 联合里有 CodemodClientToolkitLiteRule 类型)。
39
+ // dependencies.@lark-apaas/client-toolkit-lite 也删 —— client-toolkit@1.2.48+ 顶层
40
+ // single-entry 完整 re-export lite 全部 48 export,包名替换 codemod 后业务代码
41
+ // 全部 import full 顶层,lite dep 不再被引用。详见
42
+ // fullstack-plugin#1124client-toolkit 兼容 lite API 形态)。
48
43
  {
49
44
  type: 'delete-json-keys',
50
45
  to: 'package.json',
51
46
  keys: [
52
47
  // 顶层 "type": "module" —— vite-react ESM 入口约定,nestjs CommonJS 不需要
53
48
  'type',
54
- // vite-react 的 vite preset,nestjs 用 fullstack-vite-preset
49
+ // vite-react 的 vite preset
55
50
  'devDependencies.@lark-apaas/coding-preset-vite-react',
56
51
  // vite-react 的 ESLint preset
57
52
  'devDependencies.@lark-apaas/coding-presets-react',
53
+ // lite SDK:codemod 后 import 走 client-toolkit 顶层,lite dep 移除
54
+ 'dependencies.@lark-apaas/client-toolkit-lite',
58
55
  ],
59
56
  },
60
57
  // ===== 4. 模板资产覆盖 / 新增 =====
@@ -74,10 +71,13 @@ exports.MIGRATE_CONFIG = {
74
71
  // 所以 from 也写 dot-form
75
72
  { type: 'file', from: '.gitignore', to: '.gitignore', overwrite: true },
76
73
  { type: 'file', from: '.npmrc', to: '.npmrc', overwrite: true },
77
- // client 入口:用 nestjs 模板的 index.tsx 覆盖(含 Toaster / defaultTheme),原 vite-react
78
- // 入口在前面 move-directory 时已搬到 client/src/index.tsx
74
+ // client 入口:原 vite-react 入口在前面 move-directory 时已搬到 client/src/index.tsx
75
+ // 用户可能在入口加了 logger 初始化 / 自定义 Provider / 第三方 SDK init 等,强制覆盖
76
+ // 会丢用户改动。改为 overwrite:false(仅 user 没有时用 template 兜底)。codemod 会
77
+ // 把入口里的 lite import 包名换成 client-toolkit,让 AppContainer / QueryProvider /
78
+ // ErrorRender 等走 client-toolkit 顶层,业务行为不变。
79
79
  { type: 'file', from: 'client/index.html', to: 'client/index.html', overwrite: true },
80
- { type: 'file', from: 'client/src/index.tsx', to: 'client/src/index.tsx', overwrite: true },
80
+ { type: 'file', from: 'client/src/index.tsx', to: 'client/src/index.tsx', overwrite: false },
81
81
  // 整目录复制(不存在则创建,存在则递归覆盖同名文件)
82
82
  // server/:NestJS SSR runtime,固定模板,不需要业务定制
83
83
  { type: 'directory', from: 'server', to: 'server', overwrite: true },
@@ -103,7 +103,15 @@ exports.MIGRATE_CONFIG = {
103
103
  from: 'package.json',
104
104
  to: 'package.json',
105
105
  },
106
- // ===== 6. stack =====
106
+ // ===== 6. 业务代码 codemod:lite → full 包名替换 =====
107
+ // client-toolkit@1.2.48+ 顶层 single-entry 完整覆盖 lite 全部 48 项;
108
+ // 扫两个目录:客户端业务代码(client/src)+ 客户端服务端共享代码(shared)—
109
+ // shared 也可能引 lite 的类型 / utils(如 IUserProfile / IFileAttachment / logger),
110
+ // lite dep 已被 delete-json-keys 删,shared 里残留 lite import 会导致 typecheck/build 报错。
111
+ // codemod 覆盖 named import / dynamic import / require / export ... from / CSS @import。
112
+ { type: 'codemod-client-toolkit-lite', root: 'client/src' },
113
+ { type: 'codemod-client-toolkit-lite', root: 'shared' },
114
+ // ===== 7. 切 stack =====
107
115
  // 收尾:把 .spark/meta.json 的 stack 切到 nestjs-react-fullstack,后续 miaoda app sync
108
116
  // 会按新 stack 的 SyncConfig 跑
109
117
  { type: 'set-stack', stack: 'nestjs-react-fullstack' },
@@ -42,7 +42,7 @@ exports.SYNC_CONFIG = {
42
42
  command: 'node scripts/hooks/run-precommit.js',
43
43
  overwrite: false,
44
44
  },
45
- // 2. .gitignore 卫生:移除 package-lock.json + 加 .agent/
45
+ // 2. .gitignore 卫生:移除 package-lock.json + 加 .agent/ / .npm_cache
46
46
  {
47
47
  type: 'remove-line',
48
48
  to: '.gitignore',
@@ -53,6 +53,11 @@ exports.SYNC_CONFIG = {
53
53
  to: '.gitignore',
54
54
  line: '.agent/',
55
55
  },
56
+ {
57
+ type: 'add-line',
58
+ to: '.gitignore',
59
+ line: '.npm_cache',
60
+ },
56
61
  // 3. 老 npm scripts 迁移(裸 fullstack-cli → 钉版 npx)。两条 ifStartsWith 分别覆盖
57
62
  // "裸 fullstack-cli"(最老形态)和 "npx 未钉版" 形态,统一升到 FULLSTACK_CLI_PIN_SPEC。
58
63
  {
@@ -113,6 +113,12 @@ exports.SYNC_CONFIG = {
113
113
  to: '.gitignore',
114
114
  line: '.agent/',
115
115
  },
116
+ // 7a. 确保 .gitignore 包含 .npm_cache 目录
117
+ {
118
+ type: 'add-line',
119
+ to: '.gitignore',
120
+ line: '.npm_cache',
121
+ },
116
122
  // 8. 同步 .spark_project 配置文件(总是覆盖)
117
123
  {
118
124
  type: 'file',
@@ -10,13 +10,8 @@ const logger_1 = require("../../../../utils/logger");
10
10
  * 创建本地发布单(加锁;不挂 pipeline)。
11
11
  * 返回的 releaseId 必须由 finalizeLocalRelease 翻为终态,否则发布单一直挂着。
12
12
  */
13
- async function createLocalRelease(appId, version, extra) {
14
- return (0, index_1.createLocalRelease)({
15
- appID: appId,
16
- version,
17
- checkPointVersion: extra?.checkPointVersion,
18
- commitID: extra?.commitID,
19
- });
13
+ async function createLocalRelease(appId, version) {
14
+ return (0, index_1.createLocalRelease)({ appID: appId, version });
20
15
  }
21
16
  /**
22
17
  * 把本地发布单翻为终态。Finished / Failed 由 pipeline 视上下游结果决定。
@@ -43,10 +43,7 @@ async function localBuildLocalPublishPipeline(opts) {
43
43
  });
44
44
  }
45
45
  (0, logger_1.log)('deploy', 'Creating local release...');
46
- const release = await (0, index_1.createLocalRelease)(ctx.appId, pre.version, {
47
- checkPointVersion: opts.checkPointVersion,
48
- commitID: opts.commitID,
49
- });
46
+ const release = await (0, index_1.createLocalRelease)(ctx.appId, pre.version);
50
47
  try {
51
48
  (0, logger_1.log)('deploy', 'Uploading artifacts...');
52
49
  await (0, index_1.uploadArtifacts)({ projectDir: ctx.projectDir, appId: ctx.appId, data });
@@ -1,21 +1,22 @@
1
1
  "use strict";
2
2
  /**
3
- * Codemod:把 `@lark-apaas/client-toolkit-lite` 的 import 改写到 `@lark-apaas/client-toolkit`
4
- * 的对应子路径。
3
+ * Codemod:把 `@lark-apaas/client-toolkit-lite` 的 import 改写到 `@lark-apaas/client-toolkit`。
5
4
  *
6
5
  * 用于 `miaoda app migrate vite-react → nestjs-react-fullstack`:迁移后让两个包共存属于
7
- * lib 冗余(两份 react / 两套主题 / 两套 axios),所以扫 client/src lite import 改写到
8
- * full 等价子路径,然后用 delete-json-keys 移除 lite dep。
6
+ * lib 冗余(两份 react / 两套主题 / 两套 axios),所以扫 user 代码把 lite import 改写到
7
+ * full,然后用 delete-json-keys 移除 lite dep。
9
8
  *
10
- * 表的来源:grep client-toolkit lib/ 目录定位每个 lite symbol 的 d.ts 路径,subpath 取相对
11
- * 路径。同 symbol 多个 d.ts 时按优先级选最合理的——例如 AppContainer 优先 `components/` 而
12
- * `apis/components/`(跟 fullstack template 自带 client/src/index.tsx 的约定一致)。
9
+ * 实现策略:单行包名替换。
10
+ *
11
+ * client-toolkit v1.2.48 起在顶层 single-entry 完整 re-export 了 client-toolkit-lite
12
+ * 的全部 48 项(components / hooks / utils / integrations / logger / trace / types /
13
+ * constants),跟 lite 同名同签 —— 所以 codemod 只需把 import 来源包名从 `-lite` 换掉,
14
+ * specifier 列表完全不动。CSS `@import` 同理(client-toolkit 顶层加了 ./styles.css 别名)。
13
15
  *
14
16
  * 边界:
15
17
  * - 仅处理形如 `import [type] { A, B as C } from '@lark-apaas/client-toolkit-lite'` 的
16
- * 单行 named importdefault import / namespace import / 跨多行 specifier 表当作 unmapped。
17
- * - 表里没有的 symbol:保留原 import + 加一行 `// TODO[migrate]: ...` 注释;后续 build
18
- * 会因为 lite dep 被删而失败,提示用户手动处理。
18
+ * named importdefault import / namespace import / 跨多行 specifier 当前不识别,
19
+ * 但只要 `from '...lite'` 在同一行,包名替换仍有效(regex from 段)。
19
20
  */
20
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
21
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -26,68 +27,10 @@ exports.rewriteCssText = rewriteCssText;
26
27
  exports.rewriteText = rewriteText;
27
28
  const node_fs_1 = __importDefault(require("node:fs"));
28
29
  const node_path_1 = __importDefault(require("node:path"));
29
- /** lite 38 个 export → full 子路径的映射表 */
30
- const SYMBOL_MAP = {
31
- // === 组件(apis/components 是 lite 兼容层,components 是基础层) ===
32
- ActiveLink: { subpath: 'apis/components/ActiveLink' },
33
- AppContainer: { subpath: 'components/AppContainer' },
34
- ErrorRender: { subpath: 'components/ErrorRender' },
35
- NavLink: { subpath: 'apis/components/NavLink' },
36
- PagePlaceholder: { subpath: 'components/PagePlaceholder' },
37
- QueryProvider: { subpath: 'components/QueryProvider' },
38
- UniversalLink: { subpath: 'apis/components/UniversalLink' },
39
- Welcome: { subpath: 'apis/components/Welcome' },
40
- // === 类型 ===
41
- AppInfoPayload: { subpath: 'types/common' },
42
- IFileAttachment: { subpath: 'apis/udt-types' },
43
- IJson: { subpath: 'apis/udt-types' },
44
- IUserProfile: { subpath: 'apis/udt-types' },
45
- LogLevel: { subpath: 'logger/log-types' },
46
- LogWithMeta: { subpath: 'logger/log-types' },
47
- TrackKey: { subpath: 'types/tea' },
48
- TrackParams: { subpath: 'types/tea' },
49
- // === 图片资源(lite 端用了 alias:avatar→avatarImages 等) ===
50
- avatarImages: { subpath: 'apis/constants/img-resources/avatar', sourceName: 'avatar' },
51
- bannerImages: { subpath: 'apis/constants/img-resources/banner', sourceName: 'banner' },
52
- coverImages: { subpath: 'apis/constants/img-resources/cover', sourceName: 'cover' },
53
- // === Hooks ===
54
- useAppInfo: { subpath: 'hooks/useAppInfo' },
55
- useCurrentUserProfile: { subpath: 'hooks/useCurrentUserProfile' },
56
- useIsMobile: { subpath: 'hooks/useIsMobile' },
57
- useLogout: { subpath: 'hooks/useLogout' },
58
- // === Utils / 工具函数 ===
59
- axiosForBackend: { subpath: 'utils/getAxiosForBackend' },
60
- capabilityClient: { subpath: '' }, // 在 full 顶层 export
61
- clsxWithTw: { subpath: 'utils/utils' },
62
- copyToClipboard: { subpath: 'utils/copyToClipboard' },
63
- getAppId: { subpath: 'utils/getAppId' },
64
- getAppInfo: { subpath: 'integrations/getAppInfo' },
65
- getAxiosForBackend: { subpath: 'utils/getAxiosForBackend' },
66
- getCsrfToken: { subpath: 'utils/getCsrfToken' },
67
- getCurrentUserProfile: { subpath: 'integrations/getCurrentUserProfile' },
68
- getDataloom: { subpath: 'apis/dataloom' },
69
- getEnvPath: { subpath: 'utils/getEnvPath' },
70
- getWsPath: { subpath: 'utils/utils' },
71
- initAxiosConfig: { subpath: 'utils/axiosConfig' },
72
- initObservable: { subpath: 'runtime/observable' },
73
- isIOS: { subpath: 'utils/deviceType' },
74
- isIpad: { subpath: 'utils/deviceType' },
75
- isMobile: { subpath: 'utils/deviceType' },
76
- isPreview: { subpath: 'utils/utils' },
77
- logger: { subpath: 'apis/logger' },
78
- normalizeBasePath: { subpath: 'utils/utils' },
79
- reportTeaEvent: { subpath: 'components/AppContainer/utils/tea' },
80
- resolveAppUrl: { subpath: 'utils/resolveAppUrl' },
81
- safeStringify: { subpath: 'utils/safeStringify' },
82
- scopedStorage: { subpath: 'apis/utils/scopedStorage' },
83
- trace: { subpath: 'trace/index' },
84
- };
85
30
  const LITE_PKG = '@lark-apaas/client-toolkit-lite';
86
31
  const FULL_PKG = '@lark-apaas/client-toolkit';
87
32
  /**
88
33
  * 递归扫描 rootDir 下的 .ts/.tsx/.js/.jsx/.css 文件,把 lite import 改写到 full。
89
- * css 里 `@import "@lark-apaas/client-toolkit-lite/styles.css"` 同样要重写到
90
- * `@lark-apaas/client-toolkit/lib/index.css`,否则 css build 会因 lite dep 被删而 ENOENT。
91
34
  */
92
35
  function rewriteLiteImports(rootDir) {
93
36
  const result = {
@@ -108,8 +51,7 @@ function rewriteLiteImports(rootDir) {
108
51
  if (!before.includes(LITE_PKG))
109
52
  continue;
110
53
  const rel = node_path_1.default.relative(rootDir, file);
111
- const { text, count, unmapped } = isJs ? rewriteText(before, rel) : rewriteCssText(before, rel);
112
- result.unmapped.push(...unmapped);
54
+ const { text, count } = isJs ? rewriteText(before, rel) : rewriteCssText(before, rel);
113
55
  if (count > 0) {
114
56
  node_fs_1.default.writeFileSync(file, text);
115
57
  result.filesChanged.push(rel);
@@ -119,128 +61,35 @@ function rewriteLiteImports(rootDir) {
119
61
  return result;
120
62
  }
121
63
  /**
122
- * css 改写器:把 `@import "@lark-apaas/client-toolkit-lite/styles.css"` 重写到 full 的入口
123
- * `@lark-apaas/client-toolkit/lib/index.css`;其他 lite styles 子路径(如 `styles/*`)
124
- * full 里没有对等物,留下 TODO 注释让用户手动处理。
64
+ * css 改写器:单行包名替换。`@import "@lark-apaas/client-toolkit-lite/<path>"`
65
+ * `@import "@lark-apaas/client-toolkit/<path>"`。其中 styles.css full 端已加同名别名
66
+ * 指向 ./lib/index.css。
125
67
  */
126
- function rewriteCssText(source, fileLabel) {
127
- const unmapped = [];
68
+ function rewriteCssText(source, _fileLabel) {
128
69
  let count = 0;
129
- const lines = source.split('\n');
130
- const out = [];
131
- // 匹配 `@import "@lark-apaas/client-toolkit-lite/<path>";` 或 single quote
132
- const re = /^(\s*)@import\s+['"]@lark-apaas\/client-toolkit-lite\/([^'"]+)['"]\s*;?\s*$/;
133
- for (let i = 0; i < lines.length; i++) {
134
- const line = lines[i];
135
- const m = re.exec(line);
136
- if (!m) {
137
- out.push(line);
138
- continue;
139
- }
140
- const captures = m;
141
- const indent = captures[1] ?? '';
142
- const subpath = captures[2] ?? '';
143
- if (subpath === 'styles.css') {
144
- out.push(`${indent}@import "${FULL_PKG}/lib/index.css";`);
145
- count++;
146
- }
147
- else {
148
- // styles/* 等子路径 full 没对等物,保留原行 + TODO(build 会失败)
149
- out.push(line);
150
- out.push(`${indent}/* TODO[migrate]: @lark-apaas/client-toolkit-lite/${subpath} has no known mapping in @lark-apaas/client-toolkit; replace manually. */`);
151
- unmapped.push({ file: fileLabel, symbol: subpath, line: i + 1 });
152
- }
153
- }
154
- return { text: out.join('\n'), count, unmapped };
70
+ const re = /(@import\s+['"])@lark-apaas\/client-toolkit-lite(\/[^'"]*)?(['"])/g;
71
+ const text = source.replace(re, (_match, p1, sub, p3) => {
72
+ count++;
73
+ return `${p1}${FULL_PKG}${sub ?? ''}${p3}`;
74
+ });
75
+ return { text, count, unmapped: [] };
155
76
  }
156
- /** 对单个文件文本做改写。导出供单测直接验证。 */
157
- function rewriteText(source, fileLabel) {
158
- const unmapped = [];
77
+ /**
78
+ * JS/TS 文件改写器:把任何 `from '@lark-apaas/client-toolkit-lite'` 改成 full 包名。
79
+ * specifier 列表完全保留 —— full 顶层 single-entry 已经兼容 lite 全部 48 项 export。
80
+ *
81
+ * 同样兼容 dynamic import / require / `export ... from` 等其他形式(只要包名字符串在)。
82
+ */
83
+ function rewriteText(source, _fileLabel) {
159
84
  let count = 0;
160
- const lines = source.split('\n');
161
- const out = [];
162
- // 匹配单行 named import:捕获 indent / `type ` 修饰符 / specifier 段
163
- const reImport = /^(\s*)import\s+(type\s+)?\{([^}]+)\}\s+from\s+['"]@lark-apaas\/client-toolkit-lite['"]\s*;?\s*$/;
164
- for (let i = 0; i < lines.length; i++) {
165
- const line = lines[i];
166
- const m = reImport.exec(line);
167
- if (!m) {
168
- out.push(line);
169
- continue;
170
- }
171
- // RegExp.exec 返回 string[],但 optional capture group 实际可能是 undefined。
172
- // 显式 cast 让 TS narrow 配合 eslint no-unnecessary-condition 不报"永远 truthy"。
173
- const captures = m;
174
- const indent = captures[1] ?? '';
175
- const wholeIsType = captures[2] !== undefined;
176
- const specifiers = parseSpecifiers(captures[3] ?? '');
177
- // 按 target subpath 分组(同 subpath 的 specifier 合并到一条 import)
178
- const groups = new Map();
179
- const localUnmapped = [];
180
- for (const spec of specifiers) {
181
- // SYMBOL_MAP 是 Record,TS 默认推断索引返回非 undefined;用 hasOwnProperty 做 narrow
182
- if (!Object.prototype.hasOwnProperty.call(SYMBOL_MAP, spec.imported)) {
183
- localUnmapped.push(spec.imported);
184
- unmapped.push({ file: fileLabel, symbol: spec.imported, line: i + 1 });
185
- continue;
186
- }
187
- const info = SYMBOL_MAP[spec.imported];
188
- const target = info.subpath ? `${FULL_PKG}/${info.subpath}` : FULL_PKG;
189
- const sourceName = info.sourceName ?? spec.imported;
190
- const isType = wholeIsType || spec.isType;
191
- const entry = groups.get(target) ?? { items: [], allTypes: true };
192
- entry.items.push({ source: sourceName, local: spec.local, isType });
193
- entry.allTypes = entry.allTypes && isType;
194
- groups.set(target, entry);
195
- count++;
196
- }
197
- if (groups.size === 0) {
198
- // 全部 unmapped:保留原行 + TODO 注释
199
- out.push(line);
200
- out.push(`${indent}// TODO[migrate]: '${localUnmapped.join(', ')}' has no known mapping from '${LITE_PKG}' to '${FULL_PKG}'; replace manually before building.`);
201
- continue;
202
- }
203
- // 输出 group 化的新 import 行(同 target 下区分 type-only / 混合)
204
- for (const [target, { items, allTypes }] of groups) {
205
- if (allTypes) {
206
- const inner = items.map(fmtSpec).join(', ');
207
- out.push(`${indent}import type { ${inner} } from '${target}';`);
208
- }
209
- else {
210
- // 把 type 和非 type 分两条输出更稳:避免 `import { type X, Y }` 在某些工具链下不被支持
211
- const typeItems = items.filter((it) => it.isType);
212
- const valItems = items.filter((it) => !it.isType);
213
- if (typeItems.length > 0) {
214
- out.push(`${indent}import type { ${typeItems.map(fmtSpec).join(', ')} } from '${target}';`);
215
- }
216
- out.push(`${indent}import { ${valItems.map(fmtSpec).join(', ')} } from '${target}';`);
217
- }
218
- }
219
- if (localUnmapped.length > 0) {
220
- out.push(`${indent}// TODO[migrate]: '${localUnmapped.join(', ')}' has no known mapping from '${LITE_PKG}' to '${FULL_PKG}'; replace manually before building.`);
221
- }
222
- }
223
- return { text: out.join('\n'), count, unmapped };
224
- }
225
- function fmtSpec(s) {
226
- return s.local === s.source ? s.source : `${s.source} as ${s.local}`;
227
- }
228
- function parseSpecifiers(raw) {
229
- const out = [];
230
- for (const part of raw.split(',')) {
231
- const t = part.trim();
232
- if (!t)
233
- continue;
234
- const m = /^(type\s+)?([A-Za-z_$][A-Za-z0-9_$]*)(?:\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*))?$/.exec(t);
235
- if (!m)
236
- continue;
237
- const captures = m;
238
- const imported = captures[2];
239
- if (imported === undefined)
240
- continue;
241
- out.push({ isType: captures[1] !== undefined, imported, local: captures[3] ?? imported });
242
- }
243
- return out;
85
+ // 既匹配 `from 'pkg'` 也匹配 `from "pkg"`,单双引号都管。
86
+ // 同时覆盖 `import('pkg')` / `require('pkg')` 等其他常见形态。
87
+ const re = /(['"])@lark-apaas\/client-toolkit-lite(['"])/g;
88
+ const text = source.replace(re, (_match, p1, p2) => {
89
+ count++;
90
+ return `${p1}${FULL_PKG}${p2}`;
91
+ });
92
+ return { text, count, unmapped: [] };
244
93
  }
245
94
  function* walk(dir) {
246
95
  for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/miaoda-cli",
3
- "version": "0.1.16-alpha.0",
3
+ "version": "0.1.16-alpha.f6825fa",
4
4
  "description": "Miaoda 平台命令行工具,面向 Agent 调用",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -17,20 +17,6 @@
17
17
  "dist",
18
18
  "upgrade"
19
19
  ],
20
- "scripts": {
21
- "build": "bash scripts/build.sh",
22
- "typecheck": "tsc --noEmit -p tsconfig.json",
23
- "lint": "eslint src/ --max-warnings 0",
24
- "format": "prettier --write src/",
25
- "format:check": "prettier --check src/",
26
- "test": "vitest run --project=unit",
27
- "test:watch": "vitest --project=unit",
28
- "test:integration": "vitest run --project=integration",
29
- "dev": "node --import tsx src/main.ts",
30
- "cli": "node --env-file-if-exists=integration/.env --import tsx src/main.ts",
31
- "prepare": "husky",
32
- "prepublishOnly": "pnpm format:check && pnpm lint && pnpm build && pnpm test"
33
- },
34
20
  "keywords": [
35
21
  "miaoda",
36
22
  "cli",
@@ -65,5 +51,16 @@
65
51
  "vitest": "^4.1.4",
66
52
  "xml2js": "^0.6.2"
67
53
  },
68
- "packageManager": "pnpm@10.16.1+sha512.0e155aa2629db8672b49e8475da6226aa4bdea85fdcdfdc15350874946d4f3c91faaf64cbdc4a5d1ab8002f473d5c3fcedcd197989cf0390f9badd3c04678706"
69
- }
54
+ "scripts": {
55
+ "build": "bash scripts/build.sh",
56
+ "typecheck": "tsc --noEmit -p tsconfig.json",
57
+ "lint": "eslint src/ --max-warnings 0",
58
+ "format": "prettier --write src/",
59
+ "format:check": "prettier --check src/",
60
+ "test": "vitest run --project=unit",
61
+ "test:watch": "vitest --project=unit",
62
+ "test:integration": "vitest run --project=integration",
63
+ "dev": "node --import tsx src/main.ts",
64
+ "cli": "node --env-file-if-exists=integration/.env --import tsx src/main.ts"
65
+ }
66
+ }