@lark-apaas/miaoda-cli 0.1.16 → 0.1.17-alpha.ddef3e9
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 +59 -0
- package/dist/cli/commands/deploy/modern.js +9 -0
- package/dist/cli/handlers/app/index.js +3 -1
- package/dist/cli/handlers/app/init.js +41 -12
- package/dist/cli/handlers/app/migrate.js +251 -0
- package/dist/cli/handlers/deploy/modern.js +39 -0
- package/dist/config/migrate-configs/index.js +35 -0
- package/dist/config/migrate-configs/vite-react-to-nestjs-react-fullstack.js +298 -0
- package/dist/config/migrate.js +15 -0
- package/dist/services/app/init/async-install.js +116 -0
- package/dist/services/app/init/index.js +7 -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/codemod-client-toolkit-lite.js +106 -0
- package/dist/utils/migrate-rule.js +319 -0
- package/package.json +1 -1
- package/upgrade/templates/design-stack/templates/scripts/dev-local.js +16 -7
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/dev-local.js +19 -10
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* vite-react → nestjs-react-fullstack 的跨 stack 迁移规则。
|
|
4
|
+
*
|
|
5
|
+
* 设计原则:**最小改动**。只动 fullstack 形态必须的文件 / package.json key,user 已有
|
|
6
|
+
* 的 vite.config / tsconfig / eslint / prettier / tailwind / postcss / components.json
|
|
7
|
+
* 等业务配置一律**保留**,不被 template 整体覆盖。
|
|
8
|
+
*
|
|
9
|
+
* 步骤:
|
|
10
|
+
* 1. src/ public/ 搬到 client/ 下(fullstack 布局)
|
|
11
|
+
* 2. 清掉 vite-react 时代的根 index.html / eslint.config.mjs / package-lock.json
|
|
12
|
+
* 3. 删 user package.json 里 lite SDK 相关 key(保留 coding-preset-vite-react)
|
|
13
|
+
* 4. 把 NestJS server runtime 拷进来(server/ + nest-cli.json + tsconfig.node.json
|
|
14
|
+
* + client/index.html 等 fullstack 形态新增的资产)
|
|
15
|
+
* 5. 把 fullstack 必需的 scripts 加到 package.json(dev / build / type:check 等)
|
|
16
|
+
* 6. 从 fullstack template/package.json 取版本号,精确加 NestJS 强依赖白名单
|
|
17
|
+
* (client-toolkit + 几个 @nestjs/* + fullstack-nestjs-core + hbs 等;不全量 merge)
|
|
18
|
+
* 7. codemod 业务代码:@lark-apaas/client-toolkit-lite → @lark-apaas/client-toolkit
|
|
19
|
+
* 8. codemod vite.config:defineConfig(...) → defineConfig(..., { fullstack: true }),
|
|
20
|
+
* 让 coding-preset-vite-react 启用 fullstack dev-proxy / html-output / basename /
|
|
21
|
+
* server-log 4 件套,vite 8080 反代 HTML / API 到 nest 3000
|
|
22
|
+
* 9. set-stack 切 .spark/meta.json
|
|
23
|
+
*
|
|
24
|
+
* 跟 SDK 的协作:client-toolkit@1.2.52+ 顶层 single-entry 完整 re-export 了
|
|
25
|
+
* client-toolkit-lite 全部 48 项 API(fullstack-plugin#1124),所以 codemod 只换
|
|
26
|
+
* 包名、specifier 列表不动就够。
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.MIGRATE_CONFIG = void 0;
|
|
30
|
+
exports.MIGRATE_CONFIG = {
|
|
31
|
+
from: 'vite-react',
|
|
32
|
+
to: 'nestjs-react-fullstack',
|
|
33
|
+
rules: [
|
|
34
|
+
// ===== 1. 用户领地搬迁 =====
|
|
35
|
+
{ type: 'move-directory', from: 'src', to: 'client/src' },
|
|
36
|
+
{ type: 'move-directory', from: 'public', to: 'client/public' },
|
|
37
|
+
// shared/ 不动(两边布局相同);.env / .spark_project 同理。
|
|
38
|
+
// ===== 2. 清理 vite-react 残留 =====
|
|
39
|
+
// 根 index.html 被 client/index.html 取代(fullstack 用 HBS 渲染)
|
|
40
|
+
{ type: 'delete-file', to: 'index.html' },
|
|
41
|
+
// ESLint 9 flat config 文件名变了(user eslint.config.mjs → eslint.config.js)
|
|
42
|
+
{ type: 'delete-file', to: 'eslint.config.mjs' },
|
|
43
|
+
// 依赖集合变了,lockfile 重新生成
|
|
44
|
+
{ type: 'delete-file', to: 'package-lock.json' },
|
|
45
|
+
// ===== 3. 从 user package.json 删 vite-react 专属字段 =====
|
|
46
|
+
// 注意保留 devDependencies.@lark-apaas/coding-preset-vite-react —— user 迁移后
|
|
47
|
+
// vite.config 仍用它(开 fullstack 模式即可),不切到 fullstack-vite-preset。
|
|
48
|
+
{
|
|
49
|
+
type: 'delete-json-keys',
|
|
50
|
+
to: 'package.json',
|
|
51
|
+
keys: [
|
|
52
|
+
// 顶层 "type": "module" —— vite-react ESM 入口约定,nestjs CommonJS 不需要
|
|
53
|
+
'type',
|
|
54
|
+
// lite SDK:codemod 后 import 走 client-toolkit 顶层
|
|
55
|
+
'dependencies.@lark-apaas/client-toolkit-lite',
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
// ===== 4. fullstack 形态必需资产(覆盖 user 已有同名文件) =====
|
|
59
|
+
// server/ 整目录:main.ts / app.module.ts / common/ / modules/view/ 等(fullstack 必需)
|
|
60
|
+
{ type: 'directory', from: 'server', to: 'server', overwrite: true },
|
|
61
|
+
// nest-cli.json:nest build 入口
|
|
62
|
+
{ type: 'file', from: 'nest-cli.json', to: 'nest-cli.json', overwrite: true },
|
|
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 },
|
|
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 },
|
|
73
|
+
// client/index.html:HBS 模板(fullstack 用,vite-react 用根 index.html 已删)
|
|
74
|
+
{ type: 'file', from: 'client/index.html', to: 'client/index.html', overwrite: true },
|
|
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 },
|
|
80
|
+
// .githooks(fallback,user 已有则保留)
|
|
81
|
+
{ type: 'directory', from: '.githooks', to: '.githooks', overwrite: false },
|
|
82
|
+
// shared/ fallback(user 已有同名文件保留,template 新增文件加入)
|
|
83
|
+
{ type: 'directory', from: 'shared', to: 'shared', overwrite: false },
|
|
84
|
+
// .env fallback(user 没有时用 template 默认)
|
|
85
|
+
{ type: 'file', from: '.env', to: '.env', overwrite: false },
|
|
86
|
+
// ===== 4.5. plugin 实例从 shared/capabilities 搬到 server/capabilities =====
|
|
87
|
+
// vite-react (jsPage) 时期 plugin 实例 json 放在 shared/capabilities/<id>.json,
|
|
88
|
+
// fullstack 形态走 server 侧装载,路径约定改成 server/capabilities/<id>.json。
|
|
89
|
+
// 顺序约束:必须在 server/ 覆盖之后 —— server/ 是 overwrite:true,放前面会被擦掉。
|
|
90
|
+
{ type: 'move-directory', from: 'shared/capabilities', to: 'server/capabilities' },
|
|
91
|
+
// ===== 5. 加 fullstack 形态必需的 scripts =====
|
|
92
|
+
// 这些 scripts 用 user 现有的可能性低(vite-react 没有 nest 相关 script),直接覆盖
|
|
93
|
+
{
|
|
94
|
+
type: 'add-script',
|
|
95
|
+
name: 'dev',
|
|
96
|
+
command: './scripts/dev.sh',
|
|
97
|
+
overwrite: true,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'add-script',
|
|
101
|
+
name: 'dev:server',
|
|
102
|
+
command: 'NODE_ENV=development nest start --watch',
|
|
103
|
+
overwrite: true,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: 'add-script',
|
|
107
|
+
name: 'dev:client',
|
|
108
|
+
command: 'NODE_ENV=development vite --config vite.config.ts',
|
|
109
|
+
overwrite: true,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
type: 'add-script',
|
|
113
|
+
name: 'build',
|
|
114
|
+
command: './scripts/build.sh',
|
|
115
|
+
overwrite: true,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
type: 'add-script',
|
|
119
|
+
name: 'build:server',
|
|
120
|
+
command: 'NODE_ENV=production nest build',
|
|
121
|
+
overwrite: true,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
type: 'add-script',
|
|
125
|
+
name: 'build:client',
|
|
126
|
+
command: 'NODE_ENV=production vite build --config vite.config.ts',
|
|
127
|
+
overwrite: true,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
type: 'add-script',
|
|
131
|
+
name: 'build:prod',
|
|
132
|
+
command: 'npm run build:server && npm run build:client',
|
|
133
|
+
overwrite: true,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
type: 'add-script',
|
|
137
|
+
name: 'start',
|
|
138
|
+
command: 'NODE_ENV=production node main.js',
|
|
139
|
+
overwrite: true,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
type: 'add-script',
|
|
143
|
+
name: 'type:check:server',
|
|
144
|
+
command: 'tsc --noEmit --project tsconfig.node.json',
|
|
145
|
+
overwrite: true,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
type: 'add-script',
|
|
149
|
+
name: 'type:check:client',
|
|
150
|
+
command: 'tsc --noEmit --project tsconfig.app.json',
|
|
151
|
+
overwrite: true,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
type: 'add-script',
|
|
155
|
+
name: 'type:check',
|
|
156
|
+
command: 'concurrently -n "server,client" -c "blue,green" "npm run type:check:server" "npm run type:check:client"',
|
|
157
|
+
overwrite: true,
|
|
158
|
+
},
|
|
159
|
+
// ----- 平台脚本调用链 -----
|
|
160
|
+
// build.sh 步骤 1 跑 `npm run gen:openapi`,缺这条 npm ERR missing script,build 直接挂。
|
|
161
|
+
// scripts/lint.js 内部 spawn `npm run eslint` 和 `npm run stylelint`,缺这两条 lint 挂。
|
|
162
|
+
// 这些都是 fullstack 平台脚本自洽必需的桥接 script,user 不会自定义,直接覆盖。
|
|
163
|
+
{
|
|
164
|
+
type: 'add-script',
|
|
165
|
+
name: 'gen:openapi',
|
|
166
|
+
command: "echo 'UNSUPPORTED, SKIP'",
|
|
167
|
+
overwrite: true,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: 'add-script',
|
|
171
|
+
name: 'gen:db-schema',
|
|
172
|
+
command: 'npx -y @lark-apaas/db-schema-sync@latest --output server/database/schema.ts --export-custom-types',
|
|
173
|
+
overwrite: true,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'add-script',
|
|
177
|
+
name: 'eslint',
|
|
178
|
+
command: 'eslint . --quiet',
|
|
179
|
+
overwrite: true,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
type: 'add-script',
|
|
183
|
+
name: 'stylelint',
|
|
184
|
+
command: 'stylelint client/src/**/*.css --quiet',
|
|
185
|
+
overwrite: true,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: 'add-script',
|
|
189
|
+
name: 'lint',
|
|
190
|
+
command: 'node ./scripts/lint.js',
|
|
191
|
+
overwrite: true,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
type: 'add-script',
|
|
195
|
+
name: 'precommit',
|
|
196
|
+
command: 'node scripts/hooks/run-precommit.js',
|
|
197
|
+
overwrite: true,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: 'add-script',
|
|
201
|
+
name: 'upgrade',
|
|
202
|
+
command: 'npx -y @lark-apaas/fullstack-cli sync --disable-gen-openapi',
|
|
203
|
+
overwrite: true,
|
|
204
|
+
},
|
|
205
|
+
// ----- user 可能自定义的 scripts -----
|
|
206
|
+
// format/test 多半 user 自己改过(prettier 配 / jest 配),保留 user 自有值。
|
|
207
|
+
{
|
|
208
|
+
type: 'add-script',
|
|
209
|
+
name: 'format',
|
|
210
|
+
command: 'prettier --write "{server,client,test}/**/*.{js,ts,tsx,json,md}"',
|
|
211
|
+
overwrite: false,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
type: 'add-script',
|
|
215
|
+
name: 'test',
|
|
216
|
+
command: 'jest',
|
|
217
|
+
overwrite: false,
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
type: 'add-script',
|
|
221
|
+
name: 'test:watch',
|
|
222
|
+
command: 'jest --watch',
|
|
223
|
+
overwrite: false,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
type: 'add-script',
|
|
227
|
+
name: 'test:e2e',
|
|
228
|
+
command: 'jest --config test/e2e/jest.config.js',
|
|
229
|
+
overwrite: false,
|
|
230
|
+
},
|
|
231
|
+
// ===== 6. 从 fullstack template 取版本号加 NestJS 强依赖(精确白名单) =====
|
|
232
|
+
// 不走 merge-json(template 130+ devDeps 全 merge 会污染 user 业务依赖集合);
|
|
233
|
+
// 白名单只列 fullstack 启动能跑起来必需的最小集。
|
|
234
|
+
{
|
|
235
|
+
type: 'add-deps-from-template',
|
|
236
|
+
from: 'package.json',
|
|
237
|
+
to: 'package.json',
|
|
238
|
+
dependencies: [
|
|
239
|
+
// NestJS server 核心
|
|
240
|
+
'@nestjs/core',
|
|
241
|
+
'@nestjs/common',
|
|
242
|
+
'@nestjs/platform-express',
|
|
243
|
+
'@nestjs/config',
|
|
244
|
+
'@nestjs/axios',
|
|
245
|
+
'@nestjs/swagger',
|
|
246
|
+
// 妙搭 fullstack server SDK(main.ts / view module 等都依赖)
|
|
247
|
+
'@lark-apaas/fullstack-nestjs-core',
|
|
248
|
+
// HBS 模板引擎(client/index.html 渲染)
|
|
249
|
+
'hbs',
|
|
250
|
+
// NestJS runtime 必需
|
|
251
|
+
'reflect-metadata',
|
|
252
|
+
'rxjs',
|
|
253
|
+
'tslib',
|
|
254
|
+
// DTO 校验 / 转换
|
|
255
|
+
'class-transformer',
|
|
256
|
+
'class-validator',
|
|
257
|
+
// .env 加载(scripts/dev.sh 用到 dotenv-cli 风格)
|
|
258
|
+
'dotenv',
|
|
259
|
+
// client-toolkit:替代 lite,业务代码 codemod 后走它
|
|
260
|
+
'@lark-apaas/client-toolkit',
|
|
261
|
+
],
|
|
262
|
+
devDependencies: [
|
|
263
|
+
// nest start / nest build 必需
|
|
264
|
+
'@nestjs/cli',
|
|
265
|
+
'@nestjs/testing',
|
|
266
|
+
// server ts 编译
|
|
267
|
+
'ts-node',
|
|
268
|
+
'@types/express',
|
|
269
|
+
'@types/hbs',
|
|
270
|
+
'@types/node',
|
|
271
|
+
// 并发跑 nest + vite(scripts/dev.sh 用 concurrently)
|
|
272
|
+
'concurrently',
|
|
273
|
+
// fullstack server 端 tsconfig.node.json 通过 extends 引这个包
|
|
274
|
+
// (@lark-apaas/fullstack-presets/lib/simple/tsconfig/tsconfig.node.json);
|
|
275
|
+
// 不装会导致 nest build / type:check:server / vite dev 解析 tsconfig 时 ENOENT
|
|
276
|
+
'@lark-apaas/fullstack-presets',
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
// ===== 7. 业务代码 codemod:lite → client-toolkit 包名替换 =====
|
|
280
|
+
// client-toolkit@1.2.52+ 顶层完整 re-export lite 全部 48 项,specifier 列表不动。
|
|
281
|
+
// 同时扫 shared/(client/server 共享代码也可能引 lite 类型 / utils)。
|
|
282
|
+
{ type: 'codemod-client-toolkit-lite', root: 'client/src' },
|
|
283
|
+
{ type: 'codemod-client-toolkit-lite', root: 'shared' },
|
|
284
|
+
// ===== 8. vite.config codemod:加 { fullstack: true } 第二参 =====
|
|
285
|
+
// user vite.config 现在是 defineConfig({...}) 单参;fullstack 形态需要 vite 8080
|
|
286
|
+
// 反代 HTML / API 到 nest 3000,coding-preset-vite-react 用 fullstack option 启用
|
|
287
|
+
// 这一行为(详见 fullstack-plugin coding-preset-vite-react#fullstack)。
|
|
288
|
+
{ type: 'codemod-vite-config-fullstack', to: 'vite.config.ts' },
|
|
289
|
+
// ===== 9. 切 stack =====
|
|
290
|
+
{ type: 'set-stack', stack: 'nestjs-react-fullstack' },
|
|
291
|
+
],
|
|
292
|
+
// install 收尾要钉 latest 的关键平台包 —— 见 MigrateConfig.followLatestPackages 注释。
|
|
293
|
+
// - client-toolkit:codemod 后业务代码依赖 lite 兼容 API(getCurrentUserProfile 等),
|
|
294
|
+
// 这些 API 当前只在 alpha 版本里。template caret range 不匹配 pre-release,必须显式 latest。
|
|
295
|
+
// - coding-preset-vite-react:本次切到 MIAODA_APP_TYPE='3' env 检测,老版本 preset 不认 env。
|
|
296
|
+
followLatestPackages: ['@lark-apaas/client-toolkit', '@lark-apaas/coding-preset-vite-react'],
|
|
297
|
+
};
|
|
298
|
+
exports.default = exports.MIGRATE_CONFIG;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MigrateRule 类型定义 —— miaoda app migrate(跨 stack 迁移)的核心抽象。
|
|
4
|
+
*
|
|
5
|
+
* 跟 sync 的关系:sync 是**同 stack 内**的版本同步(脚手架更新、平台依赖钉版本);
|
|
6
|
+
* migrate 是**跨 stack 切换**(如 vite-react → nestjs-react-fullstack)。
|
|
7
|
+
*
|
|
8
|
+
* 复用:MigrateRule 把 SyncRule 全部纳入联合类型(10 种)—— migrate 也要做覆盖 / 删除 /
|
|
9
|
+
* merge-json / patch-script 等动作,没必要重新定义。
|
|
10
|
+
*
|
|
11
|
+
* 新增:MoveRule 和 SetStackRule 是 migrate 专有的语义,sync 用不上:
|
|
12
|
+
* - MoveRule:把用户领地从老布局搬到新布局(如 src/ → client/src/),完成后源消失。
|
|
13
|
+
* - SetStackRule:把 .spark/meta.json 的 stack 字段切到新 stack,migrate 必跑的收尾。
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ASYNC_INSTALL_LOG = void 0;
|
|
7
|
+
exports.isSandboxEnv = isSandboxEnv;
|
|
8
|
+
exports.runAsyncInstallWorker = runAsyncInstallWorker;
|
|
9
|
+
exports.dispatchAsyncInstall = dispatchAsyncInstall;
|
|
10
|
+
const node_child_process_1 = require("node:child_process");
|
|
11
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
const install_1 = require("./install");
|
|
14
|
+
/** event marker 目录,写死;写前 mkdir -p。沙箱里 code server 轮询此目录。 */
|
|
15
|
+
const EVENT_DIR = '/tmp/event';
|
|
16
|
+
/** 安装成功 marker 名 */
|
|
17
|
+
const MARKER_READY = 'WORKSPACE_READY';
|
|
18
|
+
/** 安装失败 marker 名 */
|
|
19
|
+
const MARKER_FAILED = 'WORKSPACE_FAILED';
|
|
20
|
+
/** 后台 worker stdout/stderr 落盘路径 */
|
|
21
|
+
exports.ASYNC_INSTALL_LOG = '/tmp/async_install_dep.std.log';
|
|
22
|
+
/** 沙箱判定:SANDBOX_ID 非空。与 init handler 的 outputLayout 分流口径一致。 */
|
|
23
|
+
function isSandboxEnv() {
|
|
24
|
+
return process.env.SANDBOX_ID !== undefined && process.env.SANDBOX_ID !== '';
|
|
25
|
+
}
|
|
26
|
+
function nowIso() {
|
|
27
|
+
return new Date().toISOString();
|
|
28
|
+
}
|
|
29
|
+
function clearStaleMarkers(eventDir) {
|
|
30
|
+
for (const name of [MARKER_READY, MARKER_FAILED]) {
|
|
31
|
+
const p = node_path_1.default.join(eventDir, name);
|
|
32
|
+
if (node_fs_1.default.existsSync(p))
|
|
33
|
+
node_fs_1.default.rmSync(p, { force: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function writeMarker(eventDir, name, content) {
|
|
37
|
+
node_fs_1.default.mkdirSync(eventDir, { recursive: true });
|
|
38
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(eventDir, name), JSON.stringify(content) + '\n', 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 后台 install worker 体:跑 installDependencies,按结果写 marker(仅沙箱)。
|
|
42
|
+
* 由 dispatchAsyncInstall 通过 `node -e` 在 detached 子进程里调用(见下)。
|
|
43
|
+
* presence 定成败:成功写 WORKSPACE_READY,失败写 WORKSPACE_FAILED;content 给排障。
|
|
44
|
+
*/
|
|
45
|
+
function runAsyncInstallWorker(opts) {
|
|
46
|
+
const eventDir = opts.eventDir ?? EVENT_DIR;
|
|
47
|
+
const sandbox = isSandboxEnv();
|
|
48
|
+
if (sandbox)
|
|
49
|
+
clearStaleMarkers(eventDir);
|
|
50
|
+
const start = Date.now();
|
|
51
|
+
let result;
|
|
52
|
+
try {
|
|
53
|
+
result = (0, install_1.installDependencies)({ targetDir: opts.targetDir, quietStdout: false });
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
57
|
+
if (sandbox) {
|
|
58
|
+
writeMarker(eventDir, MARKER_FAILED, {
|
|
59
|
+
ts: nowIso(),
|
|
60
|
+
error: msg,
|
|
61
|
+
logPath: exports.ASYNC_INSTALL_LOG,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!sandbox)
|
|
67
|
+
return;
|
|
68
|
+
if (result.source === 'failed') {
|
|
69
|
+
writeMarker(eventDir, MARKER_FAILED, {
|
|
70
|
+
ts: nowIso(),
|
|
71
|
+
error: result.error ?? 'npm install failed',
|
|
72
|
+
logPath: exports.ASYNC_INSTALL_LOG,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
writeMarker(eventDir, MARKER_READY, {
|
|
77
|
+
ts: nowIso(),
|
|
78
|
+
durationMs: Date.now() - start,
|
|
79
|
+
source: result.source,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 派发 detached 后台进程跑依赖安装并立即返回。
|
|
85
|
+
* 用 `node -e` 直接 require 本模块(编译产物)调 runAsyncInstallWorker,
|
|
86
|
+
* 不挂任何 CLI 子命令、对外不暴露;worker 内复用 installDependencies(cache / registry / fallback)。
|
|
87
|
+
* 沙箱下派发前清旧 marker;stdout/stderr 重定向到 logPath。
|
|
88
|
+
*/
|
|
89
|
+
function dispatchAsyncInstall(opts) {
|
|
90
|
+
const eventDir = opts.eventDir ?? EVENT_DIR;
|
|
91
|
+
const logPath = opts.logPath ?? exports.ASYNC_INSTALL_LOG;
|
|
92
|
+
const sandbox = isSandboxEnv();
|
|
93
|
+
if (sandbox)
|
|
94
|
+
clearStaleMarkers(eventDir);
|
|
95
|
+
// targetDir 走 argv 传,不拼进代码串(免转义);只把本模块路径 JSON 内联进去。
|
|
96
|
+
const workerCode = `require(${JSON.stringify(__filename)}).runAsyncInstallWorker({ targetDir: process.argv[1] })`;
|
|
97
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(logPath), { recursive: true });
|
|
98
|
+
const fd = node_fs_1.default.openSync(logPath, 'w');
|
|
99
|
+
try {
|
|
100
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, ['-e', workerCode, opts.targetDir], {
|
|
101
|
+
detached: true,
|
|
102
|
+
stdio: ['ignore', fd, fd],
|
|
103
|
+
});
|
|
104
|
+
child.unref();
|
|
105
|
+
return {
|
|
106
|
+
pid: child.pid,
|
|
107
|
+
logPath,
|
|
108
|
+
eventReadyPath: sandbox ? node_path_1.default.join(eventDir, MARKER_READY) : undefined,
|
|
109
|
+
eventFailedPath: sandbox ? node_path_1.default.join(eventDir, MARKER_FAILED) : undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
// 父进程关掉自己那份 fd;子进程已通过 stdio dup 持有独立副本。
|
|
114
|
+
node_fs_1.default.closeSync(fd);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -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.ASYNC_INSTALL_LOG = exports.isSandboxEnv = exports.runAsyncInstallWorker = exports.dispatchAsyncInstall = 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,9 @@ 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; } });
|
|
14
|
+
var async_install_1 = require("./async-install");
|
|
15
|
+
Object.defineProperty(exports, "dispatchAsyncInstall", { enumerable: true, get: function () { return async_install_1.dispatchAsyncInstall; } });
|
|
16
|
+
Object.defineProperty(exports, "runAsyncInstallWorker", { enumerable: true, get: function () { return async_install_1.runAsyncInstallWorker; } });
|
|
17
|
+
Object.defineProperty(exports, "isSandboxEnv", { enumerable: true, get: function () { return async_install_1.isSandboxEnv; } });
|
|
18
|
+
Object.defineProperty(exports, "ASYNC_INSTALL_LOG", { enumerable: true, get: function () { return async_install_1.ASYNC_INSTALL_LOG; } });
|
|
@@ -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 });
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Codemod:把 `@lark-apaas/client-toolkit-lite` 的 import 改写到 `@lark-apaas/client-toolkit`。
|
|
4
|
+
*
|
|
5
|
+
* 用于 `miaoda app migrate vite-react → nestjs-react-fullstack`:迁移后让两个包共存属于
|
|
6
|
+
* lib 冗余(两份 react / 两套主题 / 两套 axios),所以扫 user 代码把 lite import 改写到
|
|
7
|
+
* full,然后用 delete-json-keys 移除 lite dep。
|
|
8
|
+
*
|
|
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 别名)。
|
|
15
|
+
*
|
|
16
|
+
* 边界:
|
|
17
|
+
* - 仅处理形如 `import [type] { A, B as C } from '@lark-apaas/client-toolkit-lite'` 的
|
|
18
|
+
* named import;default import / namespace import / 跨多行 specifier 当前不识别,
|
|
19
|
+
* 但只要 `from '...lite'` 在同一行,包名替换仍有效(regex 抓 from 段)。
|
|
20
|
+
*/
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.rewriteLiteImports = rewriteLiteImports;
|
|
26
|
+
exports.rewriteCssText = rewriteCssText;
|
|
27
|
+
exports.rewriteText = rewriteText;
|
|
28
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
29
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
30
|
+
const LITE_PKG = '@lark-apaas/client-toolkit-lite';
|
|
31
|
+
const FULL_PKG = '@lark-apaas/client-toolkit';
|
|
32
|
+
/**
|
|
33
|
+
* 递归扫描 rootDir 下的 .ts/.tsx/.js/.jsx/.css 文件,把 lite import 改写到 full。
|
|
34
|
+
*/
|
|
35
|
+
function rewriteLiteImports(rootDir) {
|
|
36
|
+
const result = {
|
|
37
|
+
filesScanned: 0,
|
|
38
|
+
filesChanged: [],
|
|
39
|
+
rewrittenSymbols: 0,
|
|
40
|
+
unmapped: [],
|
|
41
|
+
};
|
|
42
|
+
if (!node_fs_1.default.existsSync(rootDir))
|
|
43
|
+
return result;
|
|
44
|
+
for (const file of walk(rootDir)) {
|
|
45
|
+
const isJs = /\.(ts|tsx|js|jsx)$/.test(file);
|
|
46
|
+
const isCss = file.endsWith('.css');
|
|
47
|
+
if (!isJs && !isCss)
|
|
48
|
+
continue;
|
|
49
|
+
result.filesScanned++;
|
|
50
|
+
const before = node_fs_1.default.readFileSync(file, 'utf-8');
|
|
51
|
+
if (!before.includes(LITE_PKG))
|
|
52
|
+
continue;
|
|
53
|
+
const rel = node_path_1.default.relative(rootDir, file);
|
|
54
|
+
const { text, count } = isJs ? rewriteText(before, rel) : rewriteCssText(before, rel);
|
|
55
|
+
if (count > 0) {
|
|
56
|
+
node_fs_1.default.writeFileSync(file, text);
|
|
57
|
+
result.filesChanged.push(rel);
|
|
58
|
+
result.rewrittenSymbols += count;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* css 改写器:单行包名替换。`@import "@lark-apaas/client-toolkit-lite/<path>"` →
|
|
65
|
+
* `@import "@lark-apaas/client-toolkit/<path>"`。其中 styles.css 在 full 端已加同名别名
|
|
66
|
+
* 指向 ./lib/index.css。
|
|
67
|
+
*/
|
|
68
|
+
function rewriteCssText(source, _fileLabel) {
|
|
69
|
+
let count = 0;
|
|
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: [] };
|
|
76
|
+
}
|
|
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) {
|
|
84
|
+
let count = 0;
|
|
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: [] };
|
|
93
|
+
}
|
|
94
|
+
function* walk(dir) {
|
|
95
|
+
for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
96
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.'))
|
|
97
|
+
continue;
|
|
98
|
+
const full = node_path_1.default.join(dir, entry.name);
|
|
99
|
+
if (entry.isDirectory()) {
|
|
100
|
+
yield* walk(full);
|
|
101
|
+
}
|
|
102
|
+
else if (entry.isFile()) {
|
|
103
|
+
yield full;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|