flex-dfe 1.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +301 -0
  2. package/package.json +34 -0
package/dist/index.js ADDED
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env node
2
+ import { resolve, basename } from "path";
3
+ import { execSync, exec } from "child_process";
4
+ import { cpSync, mkdirSync, rmdirSync, existsSync } from "fs";
5
+ import minimist from "minimist";
6
+ import AdmZip from "adm-zip";
7
+ import ora from "ora";
8
+ import pLimit from "p-limit";
9
+ // -p 命令选择产品栈 参数 可选值为 portal, admin
10
+ // -g 命令只执行 git 操作
11
+ // -e 命令排除项目,在全量打包的时候可以指定排除项目,用逗号分隔
12
+ // -l 命令限制并发数,可选值为数字,默认值为 3
13
+ // 全局 pLimit 实例对象,用于限制并发数
14
+ let limit;
15
+ // --- 1. CONFIGURATION & SETUP ---
16
+ const paths = {
17
+ curPath: process.cwd(),
18
+ // PORTAL 栈
19
+ portalFront: resolve(process.cwd(), "flexitdl-portal-front"),
20
+ portalComposite: resolve(process.cwd(), "flexitdl-portal-composite"),
21
+ portalDiscoverdata: resolve(process.cwd(), "flexitdl-portal-discoverdata"),
22
+ portalSdk: resolve(process.cwd(), "flexitdl-portal-sdk"),
23
+ portalCooperate: resolve(process.cwd(), "flexitdl-portal-cooperate"),
24
+ portalUsageControl: resolve(process.cwd(), "flexitdl-portal-usagecontrol"),
25
+ portalUsedData: resolve(process.cwd(), "flexitdl-portal-usedata"),
26
+ portalProvidData: resolve(process.cwd(), "flexitdl-portal-providedata"),
27
+ portalDatasource: resolve(process.cwd(), "flexitdl-portal-datasource"),
28
+ portalLoginFront: resolve(process.cwd(), "flexitdl-portal-login-front"),
29
+ // ADMIN 栈
30
+ adminFront: resolve(process.cwd(), "flexitdl-admin-front"),
31
+ adminSdk: resolve(process.cwd(), "flexitdl-admin-sdk"),
32
+ adminLoginFront: resolve(process.cwd(), "flexitdl-portal-login-front"),
33
+ buildOutput: resolve(process.cwd(), "build"),
34
+ };
35
+ // 根据产品栈获取配置
36
+ function getStackConfig(stack) {
37
+ if (stack === "portal") {
38
+ // Portal 栈:所有非 front 非 sdk 的项目都是子应用(不存在时会被自动过滤)
39
+ const allPortalSlaves = [
40
+ paths.portalComposite,
41
+ paths.portalDiscoverdata,
42
+ paths.portalCooperate,
43
+ paths.portalUsageControl,
44
+ paths.portalUsedData,
45
+ paths.portalProvidData,
46
+ paths.portalDatasource,
47
+ paths.portalLoginFront,
48
+ ];
49
+ return {
50
+ front: paths.portalFront,
51
+ sdk: paths.portalSdk,
52
+ sdkFilename: "flexitdl-portal-sdk.js",
53
+ slaves: allPortalSlaves,
54
+ frontDistName: "flexitdl-portal-front",
55
+ };
56
+ }
57
+ // admin (default)
58
+ return {
59
+ front: paths.adminFront,
60
+ sdk: paths.adminSdk,
61
+ sdkFilename: "flexitdl-admin-sdk.js",
62
+ slaves: [paths.adminLoginFront].filter(p => existsSync(p)),
63
+ frontDistName: "flexitdl-admin-front",
64
+ };
65
+ }
66
+ const zip = new AdmZip();
67
+ // --- 2. UTILITY FUNCTIONS ---
68
+ function formatDuration(ms) {
69
+ const seconds = Math.floor((ms / 1000) % 60);
70
+ const minutes = Math.floor((ms / (1000 * 60)) % 60);
71
+ const hours = Math.floor(ms / (1000 * 60 * 60));
72
+ return [hours, minutes, seconds]
73
+ .map(v => String(v).padStart(2, "0"))
74
+ .join(":");
75
+ }
76
+ // 获取项目的实际构建输出目录(处理嵌套 dist)
77
+ function getProjectDistDir(projectPath) {
78
+ const projectName = basename(projectPath);
79
+ const distRoot = resolve(projectPath, "dist");
80
+ const nestedDist = resolve(distRoot, projectName);
81
+ // 如果嵌套目录存在,说明是设置了 outputPath 的子应用/主应用
82
+ if (existsSync(nestedDist)) {
83
+ return nestedDist;
84
+ }
85
+ // 否则直接使用 dist/
86
+ return distRoot;
87
+ }
88
+ // 从短名称解析项目路径(如 composite -> flexitdl-portal-composite)
89
+ function resolveProjectName(name, stack) {
90
+ const config = getStackConfig(stack);
91
+ const nameMap = {
92
+ // 通用
93
+ front: config.front,
94
+ sdk: config.sdk,
95
+ // Portal 子应用
96
+ composite: paths.portalComposite,
97
+ discoverdata: paths.portalDiscoverdata,
98
+ cooperate: paths.portalCooperate,
99
+ usagecontrol: paths.portalUsageControl,
100
+ usedata: paths.portalUsedData,
101
+ providedata: paths.portalProvidData,
102
+ datasource: paths.portalDatasource,
103
+ loginfront: paths.portalLoginFront,
104
+ // Admin
105
+ adminloginfront: paths.adminLoginFront,
106
+ };
107
+ return nameMap[name] || null;
108
+ }
109
+ function parseArguments() {
110
+ const argv = minimist(process.argv.slice(2), {
111
+ alias: { g: "gitonly" },
112
+ boolean: ["g"],
113
+ string: ["e"],
114
+ });
115
+ const stack = argv.p === "portal" ? "portal" : "admin";
116
+ const config = getStackConfig(stack);
117
+ const selectedProject = argv._ || [];
118
+ const gitOnly = argv.g;
119
+ const branch = argv.b;
120
+ const maxLimit = argv.l ? parseInt(argv.l) : 3;
121
+ limit = pLimit(maxLimit);
122
+ let projectsToProcess = [];
123
+ const excludeProject = argv.e ? argv.e.split(",") : [];
124
+ if (excludeProject.length)
125
+ console.log(`\n🚫 排除项目: ${excludeProject.join(", ")}`);
126
+ let packageType = "single";
127
+ if (!selectedProject.length) {
128
+ // 全量模式:front + slaves + sdk
129
+ const allProjects = [config.front, ...config.slaves, config.sdk];
130
+ projectsToProcess = allProjects.filter(p => {
131
+ const isExist = existsSync(p);
132
+ if (!isExist) {
133
+ console.log(`\n⚠️ ${p} 路径不存在`);
134
+ }
135
+ const shortName = basename(p).replace(/^flexitdl-/, "").replace(/^(portal|admin)-/, "");
136
+ return !excludeProject.includes(shortName) && !excludeProject.includes(basename(p)) && isExist;
137
+ });
138
+ packageType = "all";
139
+ }
140
+ else {
141
+ // 单独模式:按名称解析
142
+ for (const name of selectedProject) {
143
+ const resolved = resolveProjectName(name, stack);
144
+ if (resolved && existsSync(resolved)) {
145
+ projectsToProcess.push(resolved);
146
+ // 指定 front 时自动包含 sdk
147
+ if (name === "front" && !projectsToProcess.includes(config.sdk)) {
148
+ projectsToProcess.push(config.sdk);
149
+ }
150
+ }
151
+ else {
152
+ console.log(`\n⚠️ 项目 "${name}" 未找到或路径不存在`);
153
+ }
154
+ }
155
+ packageType = "single";
156
+ }
157
+ return { projectsToProcess, packageType, gitOnly, branch, stack };
158
+ }
159
+ function performGitOperations(projectPath, sdkPath) {
160
+ // 非 SDK 项目:拉取 commonComponents 子模块
161
+ if (projectPath !== sdkPath) {
162
+ const submodulePath = resolve(projectPath, "src/commonComponents");
163
+ if (existsSync(submodulePath)) {
164
+ try {
165
+ const cbranch = execSync("git rev-parse --abbrev-ref HEAD", {
166
+ encoding: "utf-8",
167
+ cwd: submodulePath,
168
+ }).trim();
169
+ console.log(`\n🚚 拉取 commonComponents ${cbranch}分支...`);
170
+ execSync("git pull", { cwd: submodulePath, stdio: "ignore" });
171
+ }
172
+ catch (e) {
173
+ console.log(`\n⚠️ ${basename(projectPath)} 的 commonComponents 子模块拉取失败,跳过`);
174
+ }
175
+ }
176
+ }
177
+ const bname = execSync("git rev-parse --abbrev-ref HEAD", {
178
+ encoding: "utf-8",
179
+ cwd: projectPath,
180
+ }).trim();
181
+ console.log(`\n🚚 拉取 ${basename(projectPath)} ${bname}分支...`);
182
+ // 拉取主仓库
183
+ execSync("git pull", { cwd: projectPath, stdio: "ignore" });
184
+ // 检查冲突
185
+ const status = execSync("git status --porcelain", {
186
+ cwd: projectPath,
187
+ encoding: "utf-8",
188
+ });
189
+ if (/(UU|AA|DD|AU|UD|UA|DU)/.test(status)) {
190
+ throw new Error(`❌ Git 冲突于: ${basename(projectPath)},请手动解决后重试`);
191
+ }
192
+ console.log(`✅ ${basename(projectPath)} 拉取并检查完毕。`);
193
+ }
194
+ function runBuildProcess(projectPath) {
195
+ const pathResolve = resolve(projectPath, "dist");
196
+ return limit(() => new Promise((resolvePromise, reject) => {
197
+ // SDK 项目先清理旧 dist
198
+ if (existsSync(pathResolve)) {
199
+ rmdirSync(pathResolve, { recursive: true });
200
+ }
201
+ console.log(`\n📦 开始构建 ${basename(projectPath)}...`);
202
+ exec("npm run build", { cwd: projectPath }, (error, stdout, stderr) => {
203
+ if (error) {
204
+ console.error(`\n❌ 构建失败: ${basename(projectPath)}`, stderr);
205
+ return reject(error);
206
+ }
207
+ console.log(`✅ ${basename(projectPath)} 构建成功。`);
208
+ resolvePromise(void 0);
209
+ });
210
+ }));
211
+ }
212
+ function createZipArchives(type, projects, stackConfig) {
213
+ console.log("\nዚ 开始打包...");
214
+ const { front, sdk, sdkFilename, slaves, frontDistName } = stackConfig;
215
+ if (type === "all") {
216
+ // ===== 全量打包模式 =====
217
+ // 1. 复制 SDK 到 front 的 dist 目录
218
+ const sdkSource = resolve(sdk, "dist", sdkFilename);
219
+ const frontDistDir = getProjectDistDir(front);
220
+ const sdkDest = resolve(frontDistDir, sdkFilename);
221
+ cpSync(sdkSource, sdkDest);
222
+ // 2. 创建 child 目录并复制子应用
223
+ const childDir = resolve(frontDistDir, "child");
224
+ mkdirSync(childDir, { recursive: true });
225
+ slaves
226
+ .filter(p => p !== front && p !== sdk && existsSync(p))
227
+ .forEach(p => {
228
+ const slaveDistDir = getProjectDistDir(p);
229
+ const slaveName = basename(p);
230
+ cpSync(slaveDistDir, resolve(childDir, slaveName), { recursive: true });
231
+ });
232
+ // 3. 打包整个 front dist 目录
233
+ const zip = new AdmZip();
234
+ zip.addLocalFolder(frontDistDir, frontDistName);
235
+ const finalZipPath = resolve(front, "dist", `${frontDistName}.zip`);
236
+ zip.writeZip(finalZipPath);
237
+ console.log(`🎉 全量包完成: ${finalZipPath}`);
238
+ }
239
+ else {
240
+ // ===== 单独打包模式 =====
241
+ for (const projectPath of projects) {
242
+ if (projectPath === front || projectPath === sdk)
243
+ continue;
244
+ const distDir = getProjectDistDir(projectPath);
245
+ const projectName = basename(projectPath);
246
+ const zip = new AdmZip();
247
+ zip.addLocalFolder(distDir, projectName);
248
+ zip.writeZip(`${distDir}.zip`);
249
+ console.log(`🎉 单独打包完成: ${distDir}.zip`);
250
+ }
251
+ // front 单独打包(包含 SDK)
252
+ if (projects.includes(front)) {
253
+ const frontDistDir = getProjectDistDir(front);
254
+ const sdkSource = resolve(sdk, "dist", sdkFilename);
255
+ const sdkDest = resolve(frontDistDir, sdkFilename);
256
+ cpSync(sdkSource, sdkDest);
257
+ const zip = new AdmZip();
258
+ zip.addLocalFolder(frontDistDir, frontDistName);
259
+ const zipPath = `${resolve(front, "dist")}/${frontDistName}.zip`;
260
+ zip.writeZip(zipPath);
261
+ console.log(`🎉 Front 包完成: ${zipPath}`);
262
+ }
263
+ }
264
+ }
265
+ // --- 4. MAIN ORCHESTRATOR ---
266
+ async function main() {
267
+ const startTime = Date.now();
268
+ const spinner = ora("🚀 开始执行自动化任务...").start();
269
+ const timer = setInterval(() => {
270
+ spinner.text = `执行中... 耗时: ${formatDuration(Date.now() - startTime)}`;
271
+ }, 1000);
272
+ try {
273
+ const { projectsToProcess, packageType, gitOnly, branch, stack } = parseArguments();
274
+ const config = getStackConfig(stack);
275
+ spinner.info(`产品栈: ${stack.toUpperCase()} | 将要处理的项目: ${projectsToProcess.map(p => basename(p)).join(", ")}`);
276
+ for (const projectPath of projectsToProcess) {
277
+ performGitOperations(projectPath, config.sdk);
278
+ }
279
+ spinner.succeed("✅ 所有仓库拉取/检查完毕。");
280
+ if (gitOnly) {
281
+ spinner.warn("🚩 检测到 -g (--gitonly) 参数,仅执行 Git 操作。");
282
+ return;
283
+ }
284
+ spinner.start("📦 并行构建所有项目...");
285
+ await Promise.all(projectsToProcess.map(runBuildProcess));
286
+ spinner.succeed("✅ 所有项目构建完成。");
287
+ createZipArchives(packageType, projectsToProcess, config);
288
+ spinner.succeed("🎉 所有任务成功完成!");
289
+ }
290
+ catch (error) {
291
+ spinner.fail("❌ 任务执行失败。");
292
+ console.error("\n[错误详情]:", error instanceof Error ? error.message : error);
293
+ process.exit(1);
294
+ }
295
+ finally {
296
+ clearInterval(timer);
297
+ spinner.stop();
298
+ console.log(`\n⏱️ 总耗时: ${formatDuration(Date.now() - startTime)}`);
299
+ }
300
+ }
301
+ main();
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "flex-dfe",
3
+ "version": "1.0.2",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "dependencies": {
8
+ "@types/node": "^24.0.12",
9
+ "adm-zip": "^0.5.17",
10
+ "minimist": "^1.2.8",
11
+ "ora": "^8.2.0",
12
+ "p-limit": "^7.3.0",
13
+ "rimraf": "^6.1.3",
14
+ "undici-types": "^7.8.0"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "test": "echo \"Error: no test specified\" && exit 1"
22
+ },
23
+ "bin": {
24
+ "dsep": "./dist/index.js"
25
+ },
26
+ "keywords": [],
27
+ "author": "",
28
+ "license": "ISC",
29
+ "devDependencies": {
30
+ "@types/adm-zip": "^0.5.7",
31
+ "@types/minimist": "^1.2.5",
32
+ "typescript": "^5.8.3"
33
+ }
34
+ }