@lark-apaas/miaoda-cli 0.1.16-alpha.f013f1c → 0.1.16-beta.6cd807b
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/cli/commands/app/index.js +9 -15
- package/dist/cli/commands/deploy/modern.js +9 -0
- package/dist/cli/handlers/app/migrate.js +44 -3
- package/dist/cli/handlers/deploy/modern.js +39 -0
- package/dist/config/migrate-configs/vite-react-to-nestjs-react-fullstack.js +24 -5
- package/dist/services/app/init/index.js +2 -1
- package/dist/services/deploy/modern/atoms/local-release.js +7 -2
- package/dist/services/deploy/modern/pipelines/local.js +4 -1
- package/dist/utils/migrate-rule.js +54 -242
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerAppCommands = registerAppCommands;
|
|
4
|
+
const commander_1 = require("commander");
|
|
4
5
|
const shared_1 = require("../../../cli/commands/shared");
|
|
5
6
|
const index_1 = require("../../../cli/handlers/app/index");
|
|
6
7
|
const index_2 = require("../../../config/migrate-configs/index");
|
|
@@ -30,8 +31,13 @@ function registerAppMigrate(parent) {
|
|
|
30
31
|
const supportedDesc = (0, index_2.listSupportedMigrations)()
|
|
31
32
|
.map((m) => `${m.from} → ${m.to}`)
|
|
32
33
|
.join(', ');
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
// 渐进式披露:migrate 是给妙搭平台 / 用户主动触发的命令,**不暴露给 Agent**。
|
|
35
|
+
// - `miaoda app -h` / `miaoda app help` 都不列出此命令
|
|
36
|
+
// - 但 `miaoda app migrate --to xxx` 仍能直接调用(仅命令注册时打 hidden 标记,行为不变)
|
|
37
|
+
// - `--help` 也不显示 ——commander hidden 命令仅显式 invoke 时才暴露
|
|
38
|
+
// 用 addCommand(cmd, { hidden: true }) 而非 parent.command(...),因为 commander 13
|
|
39
|
+
// 的 fluent `.command()` 不支持设置 hidden。
|
|
40
|
+
const cmd = new commander_1.Command('migrate')
|
|
35
41
|
.description('跨 stack 原地迁移:按 MigrateConfig 全套 apply(move + delete + 模板覆盖 + 字段 merge + set-stack)')
|
|
36
42
|
.requiredOption('--to <stack>', '目标 stack 短名')
|
|
37
43
|
.option('--from <stack>', '源 stack;默认读 .spark/meta.json 当前 stack')
|
|
@@ -40,19 +46,6 @@ function registerAppMigrate(parent) {
|
|
|
40
46
|
已支持的迁移路径
|
|
41
47
|
${supportedDesc || '(none)'}
|
|
42
48
|
|
|
43
|
-
执行流
|
|
44
|
-
1. 校验 .spark/meta.json 存在且当前 stack 匹配 --from(或自动读 meta)
|
|
45
|
-
2. 按 (from, to) 在 src/config/migrate-configs/ 取 MigrateConfig
|
|
46
|
-
3. 顺序执行 rules:
|
|
47
|
-
move-directory / move-file → 用户领地搬到新布局(src → client/src 等)
|
|
48
|
-
delete-file / delete-directory → 清理老 stack 的残留文件
|
|
49
|
-
delete-json-keys → 从 user package.json 删 vite-react 专属字段
|
|
50
|
-
file / directory → 拷新 stack 的模板资产覆盖
|
|
51
|
-
merge-json → package.json 等字段级深合并
|
|
52
|
-
add-script / patch-script / add-line / remove-line → 局部修补
|
|
53
|
-
set-stack → 收尾切 .spark/meta.json 的 stack 字段
|
|
54
|
-
4. 不做 npm install —— 让用户自行 review 改动后再装依赖(package-lock.json 已删)
|
|
55
|
-
|
|
56
49
|
JSON 输出
|
|
57
50
|
{"data": {"from": "...", "to": "...",
|
|
58
51
|
"appliedRules": [{"type": "...", "action": "...", "path": "...", "detail": "..."}],
|
|
@@ -72,6 +65,7 @@ JSON 输出
|
|
|
72
65
|
to: rawOpts.to,
|
|
73
66
|
});
|
|
74
67
|
}));
|
|
68
|
+
parent.addCommand(cmd, { hidden: true });
|
|
75
69
|
}
|
|
76
70
|
function registerAppSync(parent) {
|
|
77
71
|
const cmd = parent
|
|
@@ -16,6 +16,7 @@ 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 两字段')
|
|
19
20
|
.addHelpText('after', `
|
|
20
21
|
不要用异步模式或后台模式调用 deploy,否则调用可能提前结束,Agent 会误判发布已完成。
|
|
21
22
|
|
|
@@ -32,6 +33,12 @@ function registerDeployCommandsModern(program) {
|
|
|
32
33
|
6. savePluginInstances(扫描 dist/output_capabilities/*.json 批量注册)
|
|
33
34
|
7. finalizeLocalRelease(Finished|Failed)
|
|
34
35
|
|
|
36
|
+
--conf(关联元信息透传)
|
|
37
|
+
值为 JSON string,只识别两个字段,其余 key 忽略:
|
|
38
|
+
checkPointVersion 关联的 checkpoint 版本
|
|
39
|
+
commitID 关联的代码 commit ID
|
|
40
|
+
两字段均可选;非法 JSON / 非对象 / 字段值非字符串会报错。
|
|
41
|
+
|
|
35
42
|
JSON 输出(stdout)
|
|
36
43
|
{"data": {"appId": "...", "version": <n>, "url": "...", "releaseID": "...", "preReleaseID": "..."}}
|
|
37
44
|
|
|
@@ -39,12 +46,14 @@ JSON 输出(stdout)
|
|
|
39
46
|
$ miaoda deploy
|
|
40
47
|
$ miaoda deploy --dir ./my-app
|
|
41
48
|
$ miaoda deploy --skip-build
|
|
49
|
+
$ miaoda deploy --conf '{"checkPointVersion":"v3","commitID":"a1b2c3d"}'
|
|
42
50
|
`);
|
|
43
51
|
deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
|
|
44
52
|
await (0, modern_1.handleDeployModern)({
|
|
45
53
|
dir: rawOpts.dir ?? '.',
|
|
46
54
|
appId: (0, shared_1.resolveAppId)({}),
|
|
47
55
|
skipBuild: rawOpts.skipBuild,
|
|
56
|
+
conf: rawOpts.conf,
|
|
48
57
|
});
|
|
49
58
|
}));
|
|
50
59
|
}
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.handleAppMigrate = handleAppMigrate;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
9
|
const node_os_1 = __importDefault(require("node:os"));
|
|
9
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -30,11 +31,14 @@ const logger_1 = require("../../../utils/logger");
|
|
|
30
31
|
* 2. 拿到 (from, to) 对应的 MigrateConfig 与模板源根目录
|
|
31
32
|
* 3. 顺序执行 rules:move-* → delete-* → delete-json-keys → file/directory →
|
|
32
33
|
* merge-json → set-stack
|
|
33
|
-
* 4.
|
|
34
|
+
* 4. 清掉 node_modules(package-lock.json 已经在 rules 里 delete),跑 npm install
|
|
35
|
+
* 物化新依赖集合 —— migrate 改了 dependencies / devDependencies 集合(删 lite +
|
|
36
|
+
* 加 NestJS 全套),不重装就跑不起来。
|
|
37
|
+
* 同一条 npm install 顺手把 config.followLatestPackages 钉 `@latest`,覆盖
|
|
38
|
+
* template caret range 拉不到 pre-release 的盲区(详见 MigrateConfig 注释)。
|
|
34
39
|
*
|
|
35
40
|
* 不做事:
|
|
36
41
|
* - 不自动 commit / stash —— 让用户自己用 git review 改动
|
|
37
|
-
* - 不跑 npm install —— 留给后续 sync 或用户手动
|
|
38
42
|
* - 不动 .agent/skills/(那是 miaoda skills sync 的事,迁移后用户应该单独跑一次切到新 stack 的 skills)
|
|
39
43
|
*/
|
|
40
44
|
async function handleAppMigrate(opts) {
|
|
@@ -94,6 +98,42 @@ async function handleAppMigrate(opts) {
|
|
|
94
98
|
// 的 miaodaTemplate.archType 拿到的动态值,rule 配置时不知道,由 handler 在拿到
|
|
95
99
|
// renderTemplate 结果后单独写。version 也一并写回方便 sync 后续诊断。
|
|
96
100
|
(0, spark_meta_1.writeSparkMeta)(targetDir, { archType: templateArchType, version: templateVersion });
|
|
101
|
+
// 清掉 user app 的 node_modules —— migrate 改了 dependencies 集合(删 lite + 加 NestJS
|
|
102
|
+
// 全套)+ rule 已经把 package-lock.json 删了。如果 node_modules 留着,npm install 时旧
|
|
103
|
+
// 包还在 node_modules 顶层 hoist 树里,可能跟新 package.json spec 撞 peer 冲突
|
|
104
|
+
// (比如旧 coding-preset-vite-react@1.0.8 peer vite@^8 跟新 vite@^7 撞)。
|
|
105
|
+
// 软失败:node_modules 不存在不报错。
|
|
106
|
+
const nodeModulesPath = node_path_1.default.join(targetDir, 'node_modules');
|
|
107
|
+
if (node_fs_1.default.existsSync(nodeModulesPath)) {
|
|
108
|
+
(0, logger_1.log)('migrate', '清理 node_modules(dependencies 集合已变)...');
|
|
109
|
+
node_fs_1.default.rmSync(nodeModulesPath, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
// 跑 npm install 物化新依赖集合 + 把 followLatestPackages 钉 latest
|
|
112
|
+
// --ignore-scripts 绕开 action-plugin postinstall 在缺平台 env 时的 ENOENT
|
|
113
|
+
// --registry 钉同一份 npmmirror(跟 init / sync 行为一致)
|
|
114
|
+
// 软失败:install 挂了不阻断 emit;用户拿到详细 error,自行 npm install 兜底
|
|
115
|
+
const followLatest = config.followLatestPackages ?? [];
|
|
116
|
+
const installArgs = [
|
|
117
|
+
'install',
|
|
118
|
+
'--no-audit',
|
|
119
|
+
'--no-fund',
|
|
120
|
+
'--ignore-scripts',
|
|
121
|
+
'--registry',
|
|
122
|
+
(0, index_2.resolveNpmInstallRegistry)(),
|
|
123
|
+
...followLatest.map((pkg) => `${pkg}@latest`),
|
|
124
|
+
];
|
|
125
|
+
(0, logger_1.log)('migrate', `Running npm ${installArgs.join(' ')}...`);
|
|
126
|
+
let installError;
|
|
127
|
+
try {
|
|
128
|
+
(0, node_child_process_1.execFileSync)('npm', installArgs, {
|
|
129
|
+
cwd: targetDir,
|
|
130
|
+
stdio: (0, output_1.isJsonMode)() ? ['ignore', 'ignore', 'inherit'] : 'inherit',
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
installError = err instanceof Error ? err.message : String(err);
|
|
135
|
+
(0, logger_1.log)('migrate', `⚠ npm install failed (continuing): ${installError}`);
|
|
136
|
+
}
|
|
97
137
|
(0, output_1.emit)({
|
|
98
138
|
data: {
|
|
99
139
|
from,
|
|
@@ -106,9 +146,10 @@ async function handleAppMigrate(opts) {
|
|
|
106
146
|
detail: r.detail,
|
|
107
147
|
})),
|
|
108
148
|
...summarizeResults(results),
|
|
149
|
+
followLatestPackages: followLatest,
|
|
150
|
+
installError,
|
|
109
151
|
nextActions: [
|
|
110
152
|
'git status / git diff 评估改动并 commit',
|
|
111
|
-
'npm install 装新依赖(package-lock.json 已被删除,将重新生成)',
|
|
112
153
|
'miaoda skills sync 同步到新 stack 的 agent skills',
|
|
113
154
|
],
|
|
114
155
|
},
|
|
@@ -3,10 +3,46 @@ 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;
|
|
6
7
|
exports.handleDeployModern = handleDeployModern;
|
|
7
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
9
|
const output_1 = require("../../../utils/output");
|
|
10
|
+
const error_1 = require("../../../utils/error");
|
|
9
11
|
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
|
+
}
|
|
10
46
|
/**
|
|
11
47
|
* miaoda deploy(modern scene 专用,CLI 表面对齐 openclaw-cli)
|
|
12
48
|
*
|
|
@@ -14,10 +50,13 @@ const index_1 = require("../../../services/deploy/modern/index");
|
|
|
14
50
|
*/
|
|
15
51
|
async function handleDeployModern(opts) {
|
|
16
52
|
const projectDir = node_path_1.default.resolve(opts.dir);
|
|
53
|
+
const conf = parseDeployConf(opts.conf);
|
|
17
54
|
const result = await (0, index_1.runModernDeploy)({
|
|
18
55
|
projectDir,
|
|
19
56
|
appId: opts.appId,
|
|
20
57
|
skipBuild: opts.skipBuild ?? false,
|
|
58
|
+
checkPointVersion: conf.checkPointVersion,
|
|
59
|
+
commitID: conf.commitID,
|
|
21
60
|
});
|
|
22
61
|
(0, output_1.emit)({
|
|
23
62
|
data: {
|
|
@@ -55,18 +55,28 @@ exports.MIGRATE_CONFIG = {
|
|
|
55
55
|
'dependencies.@lark-apaas/client-toolkit-lite',
|
|
56
56
|
],
|
|
57
57
|
},
|
|
58
|
-
// ===== 4.
|
|
58
|
+
// ===== 4. fullstack 形态必需资产(覆盖 user 已有同名文件) =====
|
|
59
59
|
// server/ 整目录:main.ts / app.module.ts / common/ / modules/view/ 等(fullstack 必需)
|
|
60
60
|
{ type: 'directory', from: 'server', to: 'server', overwrite: true },
|
|
61
61
|
// nest-cli.json:nest build 入口
|
|
62
62
|
{ type: 'file', from: 'nest-cli.json', to: 'nest-cli.json', overwrite: true },
|
|
63
|
-
// tsconfig
|
|
63
|
+
// tsconfig.* —— vite-react 时代 tsconfig 配 include: ["src"],迁完代码搬到 client/src,
|
|
64
|
+
// include 路径不对 + 老 tsconfig.app 还 extends @lark-apaas/coding-presets-react;
|
|
65
|
+
// 整体覆盖到 fullstack 版本(include 改 client/** + shared/**、extends fullstack-presets)。
|
|
66
|
+
// user 自己加的 paths / strict 等设置如有需要,按 git diff 手动加回。
|
|
67
|
+
{ type: 'file', from: 'tsconfig.json', to: 'tsconfig.json', overwrite: true },
|
|
68
|
+
{ type: 'file', from: 'tsconfig.app.json', to: 'tsconfig.app.json', overwrite: true },
|
|
64
69
|
{ type: 'file', from: 'tsconfig.node.json', to: 'tsconfig.node.json', overwrite: true },
|
|
70
|
+
// ESLint 9 flat config —— vite-react 用 eslint.config.mjs 已删,fullstack 用
|
|
71
|
+
// eslint.config.js (commonjs) + extends @lark-apaas/fullstack-presets;user 通常不改
|
|
72
|
+
{ type: 'file', from: 'eslint.config.js', to: 'eslint.config.js', overwrite: true },
|
|
65
73
|
// client/index.html:HBS 模板(fullstack 用,vite-react 用根 index.html 已删)
|
|
66
74
|
{ type: 'file', from: 'client/index.html', to: 'client/index.html', overwrite: true },
|
|
67
|
-
// scripts/
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
// scripts/ 整目录:fullstack 形态启动 / 构建 / lint 所需的 8 个平台脚本(dev.sh /
|
|
76
|
+
// dev.js / dev-local.js / build.sh / lint.js / prune-smart.js / run.sh / hooks/)。
|
|
77
|
+
// dev.sh exec node dev.js,dev.js 才是并发起 nest + vite 的核心 —— 漏一个 dev 跑不起来。
|
|
78
|
+
// overwrite: true:这些脚本由平台 cli 维护,user 一般不改;按 fullstack 版本覆盖。
|
|
79
|
+
{ type: 'directory', from: 'scripts', to: 'scripts', overwrite: true },
|
|
70
80
|
// .githooks(fallback,user 已有则保留)
|
|
71
81
|
{ type: 'directory', from: '.githooks', to: '.githooks', overwrite: false },
|
|
72
82
|
// shared/ fallback(user 已有同名文件保留,template 新增文件加入)
|
|
@@ -183,6 +193,10 @@ exports.MIGRATE_CONFIG = {
|
|
|
183
193
|
'@types/node',
|
|
184
194
|
// 并发跑 nest + vite(scripts/dev.sh 用 concurrently)
|
|
185
195
|
'concurrently',
|
|
196
|
+
// fullstack server 端 tsconfig.node.json 通过 extends 引这个包
|
|
197
|
+
// (@lark-apaas/fullstack-presets/lib/simple/tsconfig/tsconfig.node.json);
|
|
198
|
+
// 不装会导致 nest build / type:check:server / vite dev 解析 tsconfig 时 ENOENT
|
|
199
|
+
'@lark-apaas/fullstack-presets',
|
|
186
200
|
],
|
|
187
201
|
},
|
|
188
202
|
// ===== 7. 业务代码 codemod:lite → client-toolkit 包名替换 =====
|
|
@@ -198,5 +212,10 @@ exports.MIGRATE_CONFIG = {
|
|
|
198
212
|
// ===== 9. 切 stack =====
|
|
199
213
|
{ type: 'set-stack', stack: 'nestjs-react-fullstack' },
|
|
200
214
|
],
|
|
215
|
+
// install 收尾要钉 latest 的关键平台包 —— 见 MigrateConfig.followLatestPackages 注释。
|
|
216
|
+
// - client-toolkit:codemod 后业务代码依赖 lite 兼容 API(getCurrentUserProfile 等),
|
|
217
|
+
// 这些 API 当前只在 alpha 版本里。template caret range 不匹配 pre-release,必须显式 latest。
|
|
218
|
+
// - coding-preset-vite-react:本次切到 MIAODA_APP_TYPE='3' env 检测,老版本 preset 不认 env。
|
|
219
|
+
followLatestPackages: ['@lark-apaas/client-toolkit', '@lark-apaas/coding-preset-vite-react'],
|
|
201
220
|
};
|
|
202
221
|
exports.default = exports.MIGRATE_CONFIG;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.installDependencies = exports.writeSparkMeta = exports.readSparkMeta = exports.TEMPLATE_PACKAGE_BY_STACK = exports.SUPPORTED_STACKS = exports.renderTemplate = void 0;
|
|
3
|
+
exports.resolveNpmInstallRegistry = exports.installDependencies = exports.writeSparkMeta = exports.readSparkMeta = exports.TEMPLATE_PACKAGE_BY_STACK = exports.SUPPORTED_STACKS = exports.renderTemplate = void 0;
|
|
4
4
|
var template_1 = require("./template");
|
|
5
5
|
Object.defineProperty(exports, "renderTemplate", { enumerable: true, get: function () { return template_1.renderTemplate; } });
|
|
6
6
|
Object.defineProperty(exports, "SUPPORTED_STACKS", { enumerable: true, get: function () { return template_1.SUPPORTED_STACKS; } });
|
|
@@ -10,3 +10,4 @@ Object.defineProperty(exports, "readSparkMeta", { enumerable: true, get: functio
|
|
|
10
10
|
Object.defineProperty(exports, "writeSparkMeta", { enumerable: true, get: function () { return spark_meta_1.writeSparkMeta; } });
|
|
11
11
|
var install_1 = require("./install");
|
|
12
12
|
Object.defineProperty(exports, "installDependencies", { enumerable: true, get: function () { return install_1.installDependencies; } });
|
|
13
|
+
Object.defineProperty(exports, "resolveNpmInstallRegistry", { enumerable: true, get: function () { return install_1.resolveNpmInstallRegistry; } });
|
|
@@ -10,8 +10,13 @@ const logger_1 = require("../../../../utils/logger");
|
|
|
10
10
|
* 创建本地发布单(加锁;不挂 pipeline)。
|
|
11
11
|
* 返回的 releaseId 必须由 finalizeLocalRelease 翻为终态,否则发布单一直挂着。
|
|
12
12
|
*/
|
|
13
|
-
async function createLocalRelease(appId, version) {
|
|
14
|
-
return (0, index_1.createLocalRelease)({
|
|
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
|
+
});
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
17
22
|
* 把本地发布单翻为终态。Finished / Failed 由 pipeline 视上下游结果决定。
|
|
@@ -43,7 +43,10 @@ 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
|
|
46
|
+
const release = await (0, index_1.createLocalRelease)(ctx.appId, pre.version, {
|
|
47
|
+
checkPointVersion: opts.checkPointVersion,
|
|
48
|
+
commitID: opts.commitID,
|
|
49
|
+
});
|
|
47
50
|
try {
|
|
48
51
|
(0, logger_1.log)('deploy', 'Uploading artifacts...');
|
|
49
52
|
await (0, index_1.uploadArtifacts)({ projectDir: ctx.projectDir, appId: ctx.appId, data });
|
|
@@ -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,260 +183,71 @@ 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,
|
|
186
|
-
if (
|
|
187
|
-
(0, logger_1.log)(logPrefix, ` ○ ${rel} (
|
|
186
|
+
const { text, aliasRewriteCount } = rewriteViteConfigForFullstack(before);
|
|
187
|
+
if (aliasRewriteCount === 0) {
|
|
188
|
+
(0, logger_1.log)(logPrefix, ` ○ ${rel} (no src alias path to rewrite)`);
|
|
188
189
|
return { rule, action: 'noop', path: rel };
|
|
189
190
|
}
|
|
190
|
-
if (!changed) {
|
|
191
|
-
(0, logger_1.log)(logPrefix, ` ⚠ ${rel} (defineConfig call not found; please add { fullstack: true } manually)`);
|
|
192
|
-
return { rule, action: 'noop', path: rel, detail: 'defineConfig call not found' };
|
|
193
|
-
}
|
|
194
191
|
node_fs_1.default.writeFileSync(dest, text);
|
|
195
|
-
(0, logger_1.log)(logPrefix, ` ✓ ${rel} (
|
|
196
|
-
return {
|
|
192
|
+
(0, logger_1.log)(logPrefix, ` ✓ ${rel} (rewrote ${aliasRewriteCount.toString()} alias path(s) src → client/src)`);
|
|
193
|
+
return {
|
|
194
|
+
rule,
|
|
195
|
+
action: 'patched',
|
|
196
|
+
path: rel,
|
|
197
|
+
detail: `${aliasRewriteCount.toString()} alias path(s)`,
|
|
198
|
+
};
|
|
197
199
|
}
|
|
198
200
|
/**
|
|
199
|
-
*
|
|
201
|
+
* 把 vite.config.ts 里 alias 字符串里 `src` 起头的路径重写到 `client/src`。
|
|
200
202
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
203
|
+
* vite-react 时代 user 业务代码在 `src/`,迁移后搬到 `client/src/`,vite.config
|
|
204
|
+
* 里的 alias(典型如 `'@': path.resolve(__dirname, 'src')`)必须同步改:
|
|
205
|
+
* path.resolve(<args>, 'src') → 'src' 替换为 'client/src'
|
|
206
|
+
* path.resolve(<args>, 'src/<rest>') → 同上
|
|
207
|
+
* path.join(<args>, 'src') → 同上
|
|
208
|
+
* './src' / './src/<rest>' 字符串 → './client/src' / './client/src/<rest>'
|
|
209
|
+
* 边界:仅匹配整段 'src' 起头(避免 'sub/src' 误改),保留单/双引号原样。
|
|
205
210
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
211
|
+
* 注:fullstack 形态识别 **不再** 通过 `defineConfig` 的 option 第二参,而是
|
|
212
|
+
* 由 `coding-preset-vite-react` 内部读 `process.env.MIAODA_APP_TYPE === '3'`
|
|
213
|
+
* 自动判断(fullstack stack 的 archType 是 3)—— 所以 vite.config 完全不需要
|
|
214
|
+
* 加 option,只需 alias 路径同步即可。
|
|
208
215
|
*/
|
|
209
216
|
function rewriteViteConfigForFullstack(source) {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
if (m === null) {
|
|
213
|
-
return { text: source, changed: false, alreadyDone: false };
|
|
214
|
-
}
|
|
215
|
-
const openIdx = m.index + m[0].length - 1; // 指向 '('
|
|
216
|
-
const closeIdx = findMatchingParen(source, openIdx);
|
|
217
|
-
if (closeIdx < 0) {
|
|
218
|
-
return { text: source, changed: false, alreadyDone: false };
|
|
219
|
-
}
|
|
220
|
-
const argsRaw = source.slice(openIdx + 1, closeIdx);
|
|
221
|
-
if (/fullstack\s*:\s*true/.test(argsRaw)) {
|
|
222
|
-
return { text: source, changed: false, alreadyDone: true };
|
|
223
|
-
}
|
|
224
|
-
const { firstArgEnd, secondArgRange } = splitFirstTwoArgs(source, openIdx + 1, closeIdx);
|
|
225
|
-
if (secondArgRange === null) {
|
|
226
|
-
// 单参 / 无参:在第一参之后插 ", { fullstack: true }"
|
|
227
|
-
const insertAt = firstArgEnd ?? openIdx + 1;
|
|
228
|
-
const head = source.slice(0, insertAt);
|
|
229
|
-
const tail = source.slice(insertAt, closeIdx);
|
|
230
|
-
// 第一参是否非空(去空白)
|
|
231
|
-
const arg1 = source.slice(openIdx + 1, insertAt).trim();
|
|
232
|
-
const sep = arg1.length === 0 ? '{}, { fullstack: true }' : ', { fullstack: true }';
|
|
233
|
-
return {
|
|
234
|
-
text: head + (arg1.length === 0 ? sep : sep) + tail + source.slice(closeIdx),
|
|
235
|
-
changed: true,
|
|
236
|
-
alreadyDone: false,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
// 双参:在第二参对象 `{` 之后插 ` fullstack: true,`
|
|
240
|
-
const arg2 = source.slice(secondArgRange.start, secondArgRange.end);
|
|
241
|
-
const objOpen = arg2.indexOf('{');
|
|
242
|
-
if (objOpen < 0) {
|
|
243
|
-
// 第二参不是对象字面量(罕见),保守跳过
|
|
244
|
-
return { text: source, changed: false, alreadyDone: false };
|
|
245
|
-
}
|
|
246
|
-
const absObjOpen = secondArgRange.start + objOpen;
|
|
247
|
-
const inserted = '\n fullstack: true,';
|
|
248
|
-
return {
|
|
249
|
-
text: source.slice(0, absObjOpen + 1) + inserted + source.slice(absObjOpen + 1),
|
|
250
|
-
changed: true,
|
|
251
|
-
alreadyDone: false,
|
|
252
|
-
};
|
|
217
|
+
const { text, count } = rewriteAliasSrcPaths(source);
|
|
218
|
+
return { text, aliasRewriteCount: count };
|
|
253
219
|
}
|
|
254
220
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
while (i < text.length) {
|
|
267
|
-
const c = text[i];
|
|
268
|
-
const next = i + 1 < text.length ? text[i + 1] : '';
|
|
269
|
-
if (inLineComment) {
|
|
270
|
-
if (c === '\n')
|
|
271
|
-
inLineComment = false;
|
|
272
|
-
i++;
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
if (inBlockComment) {
|
|
276
|
-
if (c === '*' && next === '/') {
|
|
277
|
-
inBlockComment = false;
|
|
278
|
-
i += 2;
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
i++;
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
if (inSingle) {
|
|
285
|
-
if (c === '\\')
|
|
286
|
-
i += 2;
|
|
287
|
-
else if (c === "'") {
|
|
288
|
-
inSingle = false;
|
|
289
|
-
i++;
|
|
290
|
-
}
|
|
291
|
-
else
|
|
292
|
-
i++;
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
if (inDouble) {
|
|
296
|
-
if (c === '\\')
|
|
297
|
-
i += 2;
|
|
298
|
-
else if (c === '"') {
|
|
299
|
-
inDouble = false;
|
|
300
|
-
i++;
|
|
301
|
-
}
|
|
302
|
-
else
|
|
303
|
-
i++;
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
|
-
if (inTpl) {
|
|
307
|
-
if (c === '\\')
|
|
308
|
-
i += 2;
|
|
309
|
-
else if (c === '`') {
|
|
310
|
-
inTpl = false;
|
|
311
|
-
i++;
|
|
312
|
-
}
|
|
313
|
-
else
|
|
314
|
-
i++;
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
if (c === '/' && next === '/') {
|
|
318
|
-
inLineComment = true;
|
|
319
|
-
i += 2;
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
if (c === '/' && next === '*') {
|
|
323
|
-
inBlockComment = true;
|
|
324
|
-
i += 2;
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
if (c === "'") {
|
|
328
|
-
inSingle = true;
|
|
329
|
-
i++;
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
if (c === '"') {
|
|
333
|
-
inDouble = true;
|
|
334
|
-
i++;
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
if (c === '`') {
|
|
338
|
-
inTpl = true;
|
|
339
|
-
i++;
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
if (c === '(' || c === '{' || c === '[') {
|
|
343
|
-
depth++;
|
|
344
|
-
i++;
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (c === ')' || c === '}' || c === ']') {
|
|
348
|
-
depth--;
|
|
349
|
-
if (depth === 0 && c === ')')
|
|
350
|
-
return i;
|
|
351
|
-
i++;
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
i++;
|
|
355
|
-
}
|
|
356
|
-
return -1;
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* 在 `argsStart`(`(` 之后第一字符)到 `argsEnd`(`)` 位置)范围内识别顶层 `,`,
|
|
360
|
-
* 拿到第一参结束位置和(如果存在)第二参的范围。
|
|
221
|
+
* vite.config 里 alias 路径重写:`src` 起头的路径加 `client/` 前缀。
|
|
222
|
+
*
|
|
223
|
+
* 匹配的形态(不限制在 resolve.alias 块内 —— vite.config 顶层很少出现非 alias 用的
|
|
224
|
+
* `src` 字符串,业务 import 走 `@/...`,所以全文匹配是安全的):
|
|
225
|
+
*
|
|
226
|
+
* path.resolve(<args>, 'src') → path.resolve(<args>, 'client/src')
|
|
227
|
+
* path.resolve(<args>, 'src/<rest>') → path.resolve(<args>, 'client/src/<rest>')
|
|
228
|
+
* path.join(<args>, 'src') → 同上
|
|
229
|
+
* './src' / './src/<rest>'(独立字符串) → './client/src' / './client/src/<rest>'
|
|
230
|
+
*
|
|
231
|
+
* 已含 `client/src` 的不动(视为已迁移),`sub/src` 之类(src 不在词首)不动。
|
|
361
232
|
*/
|
|
362
|
-
function
|
|
363
|
-
let
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
let
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
if (inDouble) {
|
|
382
|
-
if (c === '\\') {
|
|
383
|
-
i += 2;
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (c === '"')
|
|
387
|
-
inDouble = false;
|
|
388
|
-
i++;
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
if (inTpl) {
|
|
392
|
-
if (c === '\\') {
|
|
393
|
-
i += 2;
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
if (c === '`')
|
|
397
|
-
inTpl = false;
|
|
398
|
-
i++;
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
if (c === "'") {
|
|
402
|
-
inSingle = true;
|
|
403
|
-
i++;
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
|
-
if (c === '"') {
|
|
407
|
-
inDouble = true;
|
|
408
|
-
i++;
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
if (c === '`') {
|
|
412
|
-
inTpl = true;
|
|
413
|
-
i++;
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
if (c === '(' || c === '{' || c === '[') {
|
|
417
|
-
depth++;
|
|
418
|
-
i++;
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
if (c === ')' || c === '}' || c === ']') {
|
|
422
|
-
depth--;
|
|
423
|
-
i++;
|
|
424
|
-
continue;
|
|
425
|
-
}
|
|
426
|
-
if (c === ',' && depth === 0) {
|
|
427
|
-
firstComma = i;
|
|
428
|
-
break;
|
|
429
|
-
}
|
|
430
|
-
i++;
|
|
431
|
-
}
|
|
432
|
-
if (firstComma < 0) {
|
|
433
|
-
return { firstArgEnd: argsEnd, secondArgRange: null };
|
|
434
|
-
}
|
|
435
|
-
return {
|
|
436
|
-
firstArgEnd: firstComma,
|
|
437
|
-
secondArgRange: { start: firstComma + 1, end: argsEnd },
|
|
438
|
-
};
|
|
233
|
+
function rewriteAliasSrcPaths(source) {
|
|
234
|
+
let count = 0;
|
|
235
|
+
// path.resolve(...) / path.join(...) 的最后一个参数是 'src' 或 'src/...' 起头
|
|
236
|
+
// 匹配 ['"]src[/'"],保留引号
|
|
237
|
+
const pathCallRe = /(\bpath\s*\.\s*(?:resolve|join)\s*\([^)]*?,\s*)(['"`])(src)(\/[^'"`]*)?(['"`])/g;
|
|
238
|
+
let next = source.replace(pathCallRe, (_match, prefix, q1, _src, rest, q2) => {
|
|
239
|
+
count++;
|
|
240
|
+
return `${prefix}${q1}client/src${rest ?? ''}${q2}`;
|
|
241
|
+
});
|
|
242
|
+
// 独立的相对路径字符串字面量 './src' 或 './src/...'(不在 path.resolve 参数里)
|
|
243
|
+
// 注意:path.resolve / join 已被上一步处理,这里只匹配剩下的 './src...' 字面量。
|
|
244
|
+
// 这种用法在 vite.config 较少(典型是直接给 alias 字符串值),但兜底处理。
|
|
245
|
+
const relativeRe = /(['"`])\.\/(src)(\/[^'"`]*)?(['"`])/g;
|
|
246
|
+
next = next.replace(relativeRe, (_match, q1, _src, rest, q2) => {
|
|
247
|
+
count++;
|
|
248
|
+
return `${q1}./client/src${rest ?? ''}${q2}`;
|
|
249
|
+
});
|
|
250
|
+
return { text: next, count };
|
|
439
251
|
}
|
|
440
252
|
function applyAddDepsFromTemplate(rule, opts) {
|
|
441
253
|
const logPrefix = opts.logPrefix ?? 'migrate';
|