@taole/deploy-helper 1.0.0-beta.6 → 1.0.1
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/README.md +8 -8
- package/index.mjs +557 -204
- package/lib/login.mjs +167 -0
- package/lib/offlinePkg.mjs +314 -0
- package/lib/pipelineApi.mjs +364 -365
- package/lib/project.mjs +346 -0
- package/lib/upload.js +49 -0
- package/lib/util.mjs +101 -16
- package/modules/alibabacloud-devops-mcp-server/dist/operations/flow/pipeline.js +8 -1
- package/package.json +36 -31
- package/deploy.mjs +0 -203
- package/deploy2.mjs +0 -275
- package/util.mjs +0 -22
package/lib/login.mjs
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { log } from "./util.mjs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { exec } from "child_process";
|
|
6
|
+
import { promisify } from "util";
|
|
7
|
+
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
const isDev = false;
|
|
10
|
+
/**
|
|
11
|
+
* 保存本地用户登录状态的缓存文件
|
|
12
|
+
*/
|
|
13
|
+
const cacheFilePath = path.join(os.homedir(), ".tw-cli-userinfo-cache.json");
|
|
14
|
+
let host =
|
|
15
|
+
"https://fapi.tuwan.com";
|
|
16
|
+
if (isDev) {
|
|
17
|
+
host = "https://local-h5-plat.tuwan.com";
|
|
18
|
+
}
|
|
19
|
+
const devHost = "http://localhost:9000";
|
|
20
|
+
let CREATE_SESSION_URL = `${host}/api/login/createLoginSession`;
|
|
21
|
+
|
|
22
|
+
export async function getCache() {
|
|
23
|
+
try {
|
|
24
|
+
if (fs.existsSync(cacheFilePath)) {
|
|
25
|
+
const cacheStr = fs.readFileSync(cacheFilePath, "utf8");
|
|
26
|
+
const cache = JSON.parse(cacheStr);
|
|
27
|
+
const Tuwan_Passport = cache.Tuwan_Passport;
|
|
28
|
+
if (Tuwan_Passport) {
|
|
29
|
+
// 检查Tuwan_Passport是否还有效
|
|
30
|
+
const res = await fetch(
|
|
31
|
+
"https://yapi.tuwan.com/User/getUid?format=json",
|
|
32
|
+
{
|
|
33
|
+
headers: {
|
|
34
|
+
Cookie: `Tuwan_Passport=${Tuwan_Passport}`,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
//log("getCache res", res);
|
|
39
|
+
const uidResp = await res.json();
|
|
40
|
+
//log("getCache uidResp", uidResp);
|
|
41
|
+
if (
|
|
42
|
+
uidResp &&
|
|
43
|
+
uidResp.error === 0 &&
|
|
44
|
+
uidResp.data &&
|
|
45
|
+
uidResp.data.uid === cache.userInfo.uid
|
|
46
|
+
) {
|
|
47
|
+
return cache;
|
|
48
|
+
} else {
|
|
49
|
+
log("验证本地缓存失败, 已清理本地缓存");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
clearCache();
|
|
54
|
+
return null;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("获取缓存失败:", error);
|
|
57
|
+
clearCache();
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function setCache(data) {
|
|
63
|
+
fs.writeFileSync(cacheFilePath, JSON.stringify(data));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function clearCache() {
|
|
67
|
+
if (fs.existsSync(cacheFilePath)) {
|
|
68
|
+
fs.unlinkSync(cacheFilePath);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function openBrowser(url) {
|
|
73
|
+
const platform = process.platform;
|
|
74
|
+
if (platform === "win32") {
|
|
75
|
+
await execAsync(`start "" "${url}"`);
|
|
76
|
+
} else if (platform === "darwin") {
|
|
77
|
+
await execAsync(`open "${url}"`);
|
|
78
|
+
} else {
|
|
79
|
+
await execAsync(`xdg-open "${url}"`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function createLoginSession() {
|
|
84
|
+
let url = CREATE_SESSION_URL;
|
|
85
|
+
if (isDev) {
|
|
86
|
+
url = url.replace(host, devHost);
|
|
87
|
+
}
|
|
88
|
+
const res = await fetch(url, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { "Content-Type": "application/json" },
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
throw new Error(`创建登录会话失败: ${res.status} ${res.statusText}`);
|
|
94
|
+
}
|
|
95
|
+
return res.json();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function queryLoginResult(
|
|
99
|
+
doneUrl,
|
|
100
|
+
intervalMs = 2000,
|
|
101
|
+
timeoutMs = 120000
|
|
102
|
+
) {
|
|
103
|
+
const start = Date.now();
|
|
104
|
+
if (isDev) {
|
|
105
|
+
doneUrl = doneUrl.replace(host, devHost);
|
|
106
|
+
}
|
|
107
|
+
while (Date.now() - start < timeoutMs) {
|
|
108
|
+
const res = await fetch(doneUrl, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/json" },
|
|
111
|
+
});
|
|
112
|
+
//log("queryLoginResult res", res);
|
|
113
|
+
if (!res.ok) {
|
|
114
|
+
throw new Error(`轮询登录结果失败: ${res.status} ${res.statusText}`);
|
|
115
|
+
}
|
|
116
|
+
const data = await res.json();
|
|
117
|
+
//log("queryLoginResult data", JSON.stringify(data, null, 2));
|
|
118
|
+
let loginResult = data.loginResult;
|
|
119
|
+
if (loginResult && data.status === "success") {
|
|
120
|
+
return JSON.parse(loginResult);
|
|
121
|
+
}
|
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
123
|
+
}
|
|
124
|
+
throw new Error("登录超时,请重试");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function cmdLogin() {
|
|
128
|
+
const cache = await getCache();
|
|
129
|
+
if (cache && cache.userInfo && cache.userInfo.uid) {
|
|
130
|
+
log(
|
|
131
|
+
`已登录账号: ${cache.userInfo.nickname}(${cache.userInfo.uid}), 若需登录其他账号请先登出`
|
|
132
|
+
);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
log("正在创建登录会话...");
|
|
136
|
+
const { openUrl, doneUrl } = await createLoginSession();
|
|
137
|
+
log("请在浏览器中完成登录:", openUrl);
|
|
138
|
+
|
|
139
|
+
log("正在打开浏览器...");
|
|
140
|
+
await openBrowser(openUrl);
|
|
141
|
+
|
|
142
|
+
log("等待登录结果...");
|
|
143
|
+
const result = await queryLoginResult(doneUrl);
|
|
144
|
+
log(`${result.userInfo.nickname}(${result.userInfo.uid})登录成功`);
|
|
145
|
+
setCache(result);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function cmdWhoami() {
|
|
149
|
+
const cache = await getCache();
|
|
150
|
+
const userInfo = cache?.userInfo;
|
|
151
|
+
if (!userInfo || !userInfo.uid) {
|
|
152
|
+
log("当前未登录");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
log(`当前登录账号: ${userInfo.nickname}(${userInfo.uid})`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function cmdLogout() {
|
|
159
|
+
const cache = await getCache();
|
|
160
|
+
const userInfo = cache?.userInfo;
|
|
161
|
+
if (!userInfo || !userInfo.uid) {
|
|
162
|
+
log("当前未登录");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
clearCache();
|
|
166
|
+
log("已退出登录");
|
|
167
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import archiver from 'archiver';
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { log, getOssToken, getUserDeployHelperConfig } from './util.mjs';
|
|
5
|
+
import { uploadFile } from './upload.js';
|
|
6
|
+
import md5File from 'md5-file';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function genArchive(outputPath, dir) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const output = fs.createWriteStream(outputPath);
|
|
13
|
+
const archive = archiver('zip', {
|
|
14
|
+
zlib: { level: 1 } // Sets the compression level.
|
|
15
|
+
});
|
|
16
|
+
archive.on('error', reject);
|
|
17
|
+
archive.on("finish", resolve);
|
|
18
|
+
archive.pipe(output);
|
|
19
|
+
archive.directory(dir, false);
|
|
20
|
+
archive.finalize();
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* json配置
|
|
26
|
+
* @param {string} workDir 项目工作目录
|
|
27
|
+
* @returns {Object|null} 配置
|
|
28
|
+
*/
|
|
29
|
+
export function getJsonConfig(workDir, name) {
|
|
30
|
+
const filePath = join(workDir, name);
|
|
31
|
+
if (fs.existsSync(filePath)) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
34
|
+
} catch (_error) {
|
|
35
|
+
// pass
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 同步离线包列表到api
|
|
43
|
+
* @param {{offlineApi: {get: string, set: string}}} userDeployHelperConfig 用户配置
|
|
44
|
+
* @param {"prod"|"test"} mode 部署模式 prod: 线上, test: 测试
|
|
45
|
+
* @param {{name: string, remove: boolean}} offlineConfig 离线包配置
|
|
46
|
+
* @param {any} packageDescJson 离线包描述json
|
|
47
|
+
*/
|
|
48
|
+
export async function syncApi(userDeployHelperConfig, mode, offlineConfig, packageDescJson) {
|
|
49
|
+
// 根据部署模式,决定更新调用哪个接口
|
|
50
|
+
const doAction = offlineConfig.remove ? "remove" : "add";
|
|
51
|
+
const apiDomain = mode === "prod" ? "https://yapi.tuwan.com" : "https://yapi-test.tuwan.com";
|
|
52
|
+
const getPath = userDeployHelperConfig.offlineApi.get;
|
|
53
|
+
const setPath = userDeployHelperConfig.offlineApi.set;
|
|
54
|
+
const getUrl = `${apiDomain}${getPath}`;
|
|
55
|
+
const setUrl = `${apiDomain}${setPath}`;
|
|
56
|
+
const envPrefix = `${mode === "prod" ? "线上环境" : "测试环境"}[platform=${offlineConfig.platform}]`;
|
|
57
|
+
const nowRes = await fetch(getUrl, {
|
|
58
|
+
headers: {
|
|
59
|
+
platform: String(offlineConfig.platform),
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const nowData = await nowRes.json();
|
|
63
|
+
let needSetData = true;
|
|
64
|
+
let finalJsonData = null;
|
|
65
|
+
if (!nowData || nowData.error !== 0) {
|
|
66
|
+
throw new Error(`获取离线包列表失败, 获取离线包当前配置失败: ${nowData.error_msg || "未知错误"}`);
|
|
67
|
+
}
|
|
68
|
+
const jsonData = JSON.parse(nowData.data.json || "{}");
|
|
69
|
+
finalJsonData = jsonData;
|
|
70
|
+
jsonData.packages = jsonData.packages || [];
|
|
71
|
+
if (doAction === "add") {
|
|
72
|
+
const idx = jsonData.packages.findIndex(item => item.name === offlineConfig.name);
|
|
73
|
+
if (idx !== -1) {
|
|
74
|
+
log(`离线包${offlineConfig.name}已存在, 更新离线包`);
|
|
75
|
+
jsonData.packages[idx] = packageDescJson;
|
|
76
|
+
} else {
|
|
77
|
+
log(`离线包${offlineConfig.name}不存在, 添加离线包`);
|
|
78
|
+
jsonData.packages.push(packageDescJson);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
const idx = jsonData.packages.findIndex(item => item.name === offlineConfig.name);
|
|
82
|
+
if (idx === -1) {
|
|
83
|
+
log(`离线包${offlineConfig.name}不存在, 无需删除`);
|
|
84
|
+
needSetData = false;
|
|
85
|
+
}
|
|
86
|
+
jsonData.packages = jsonData.packages.filter(item => item.name !== offlineConfig.name);
|
|
87
|
+
}
|
|
88
|
+
jsonData.packages = jsonData.packages.filter(item => item.name);
|
|
89
|
+
if (needSetData) {
|
|
90
|
+
// console.log('jsonData', jsonData);
|
|
91
|
+
var formdata = new FormData();
|
|
92
|
+
formdata.append("json", JSON.stringify(jsonData));
|
|
93
|
+
const setRes = await fetch(setUrl, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
body: formdata,
|
|
96
|
+
headers: {
|
|
97
|
+
platform: String(offlineConfig.platform),
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
const setData = await setRes.json();
|
|
101
|
+
if (!setData || setData.error !== 0) {
|
|
102
|
+
throw new Error(`更新离线包列表失败, 更新离线包当前配置失败: ${setData.error_msg || "未知错误"}`);
|
|
103
|
+
}
|
|
104
|
+
log(`${envPrefix}离线包列表更新完成: ${JSON.stringify(setData)}`);
|
|
105
|
+
// 再次获取列表,确保更新成功
|
|
106
|
+
const nowRes2 = await fetch(getUrl, {
|
|
107
|
+
headers: {
|
|
108
|
+
platform: String(offlineConfig.platform),
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const nowData2 = await nowRes2.json();
|
|
112
|
+
if (!nowData2 || nowData2.error !== 0) {
|
|
113
|
+
throw new Error(`再次获取离线包列表失败, 获取离线包当前配置失败: ${nowData2.error_msg || "未知错误"}`);
|
|
114
|
+
}
|
|
115
|
+
finalJsonData = JSON.parse(nowData2.data.json || "{}");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const packages = finalJsonData.packages || [];
|
|
119
|
+
console.log(`${envPrefix}当前离线包列表:`, packages.map(item => ({ ...item, items: '省略' + item.items.length + '个条目' })));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @deprecated 不再需要这个函数, 如果插件使用的是@taole/vite-plugin-dynamic-base, 则不需要这个函数
|
|
124
|
+
* hack
|
|
125
|
+
* 由于vite-plugin-dynamic-base插件和legacy插件同时使用, 生存的入口文件中代码有点问题,这里直接修改有问题的代码
|
|
126
|
+
*/
|
|
127
|
+
function fixEntryHtml(config, offlineConfig) {
|
|
128
|
+
if (!offlineConfig || !config) return;
|
|
129
|
+
const entryHtmlPath = join(config.workDir, offlineConfig.distDir, "index.html");
|
|
130
|
+
if (!fs.existsSync(entryHtmlPath)) {
|
|
131
|
+
throw new Error(`构建产物${entryHtmlPath}不存在`);
|
|
132
|
+
}
|
|
133
|
+
const markLine = "} else if (item.tagName == 'script') {";
|
|
134
|
+
const replaceLine = `} else if (item.tagName == 'script')/*injected by deploy-helper*/{
|
|
135
|
+
if(item.attrs.id=='vite-legacy-polyfill')childNode.onload=function(){System.import(window.__dynamic_base__ + document.getElementById('vite-legacy-entry').getAttribute('data-src'))};
|
|
136
|
+
`;
|
|
137
|
+
let entryHtml = fs.readFileSync(entryHtmlPath, "utf-8");
|
|
138
|
+
entryHtml = entryHtml.replace(markLine, replaceLine);
|
|
139
|
+
fs.writeFileSync(entryHtmlPath, entryHtml);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @typedef {Object} CheckOfflinePkgResult
|
|
144
|
+
* @property {string} workDir 工作目录
|
|
145
|
+
* @property {boolean} canBuild 是否可以构建离线包
|
|
146
|
+
* @property {string} errorMsg 错误信息
|
|
147
|
+
* @property {function} hookPostBuild 构建后钩子
|
|
148
|
+
* @property {function} hookPostDeployTest 部署到测试环境后钩子
|
|
149
|
+
* @property {function} hookPostDeployProd 部署到生产环境后钩子
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 在项目构建好之后,将项目打包成离线包,并上传到指定服务器
|
|
154
|
+
* @param {Object} config 配置
|
|
155
|
+
* @param {string} config.workDir 工作目录
|
|
156
|
+
* @param {"prod"|"test"} config.mode 部署模式
|
|
157
|
+
* @returns {CheckOfflinePkgResult} 结果
|
|
158
|
+
*/
|
|
159
|
+
export function checkOfflinePkg(config) {
|
|
160
|
+
let canBuildOfflinePkg = true;
|
|
161
|
+
let errorMsg = "";
|
|
162
|
+
const offlineConfig = getJsonConfig(config.workDir, "offline.config.json");
|
|
163
|
+
if (offlineConfig) {
|
|
164
|
+
offlineConfig.distDir = offlineConfig.distDir || "dist";
|
|
165
|
+
offlineConfig.platform = offlineConfig.platform || 3; // 3,4是点点
|
|
166
|
+
}
|
|
167
|
+
const packageJson = getJsonConfig(config.workDir, "package.json");
|
|
168
|
+
const userDeployHelperConfig = getUserDeployHelperConfig();
|
|
169
|
+
const distPath = join(config.workDir, offlineConfig && offlineConfig.distDir ? offlineConfig.distDir : "dist");
|
|
170
|
+
const allDeps = {
|
|
171
|
+
...packageJson.devDependencies,
|
|
172
|
+
...packageJson.dependencies,
|
|
173
|
+
};
|
|
174
|
+
const useOldPDB = !!allDeps['vite-plugin-dynamic-base'];
|
|
175
|
+
|
|
176
|
+
// 如果offlineConfig.remove为true,则不构建离线包
|
|
177
|
+
const willRemovePkg = offlineConfig && offlineConfig.name && offlineConfig.remove === true && offlineConfig.skip !== true;
|
|
178
|
+
const willSkip = !offlineConfig || !offlineConfig.name || offlineConfig.skip === true || (offlineConfig.onlyTest === true && config.mode === "prod");
|
|
179
|
+
if (willRemovePkg) {
|
|
180
|
+
return {
|
|
181
|
+
canBuild: true,
|
|
182
|
+
errorMsg: "",
|
|
183
|
+
hookPostBuild: async () => {
|
|
184
|
+
if (useOldPDB) {
|
|
185
|
+
fixEntryHtml(config, offlineConfig);
|
|
186
|
+
}
|
|
187
|
+
log(`开始删除离线包${offlineConfig.name}`);
|
|
188
|
+
await syncApi(userDeployHelperConfig, config.mode, offlineConfig, null);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!fs.existsSync(distPath)) {
|
|
193
|
+
errorMsg = `构建产物${distPath}不存在, 请先构建项目`;
|
|
194
|
+
canBuildOfflinePkg = false;
|
|
195
|
+
} else if (offlineConfig && !offlineConfig.name) {
|
|
196
|
+
errorMsg = "离线包配置文件中未配置name, 请先配置";
|
|
197
|
+
canBuildOfflinePkg = false;
|
|
198
|
+
} else if (!userDeployHelperConfig || !userDeployHelperConfig.offlineApi || !userDeployHelperConfig.offlineApi.get || !userDeployHelperConfig.offlineApi.set) {
|
|
199
|
+
errorMsg = "未配置离线包接口, 请移步看配置文档: https://alidocs.dingtalk.com/i/nodes/R1zknDm0WRkbz13oHn0LDz3ZVBQEx5rG?doc_type=wiki_doc";
|
|
200
|
+
canBuildOfflinePkg = false;
|
|
201
|
+
} else if (!packageJson) {
|
|
202
|
+
errorMsg = "啊没有package.json?";
|
|
203
|
+
canBuildOfflinePkg = false;
|
|
204
|
+
} else {
|
|
205
|
+
if (!allDeps['vite-plugin-dynamic-base'] && !allDeps['@taole/vite-plugin-dynamic-base']) {
|
|
206
|
+
errorMsg = "检查到项目中未安装依赖@taole/vite-plugin-dynamic-base, 请先安装依赖并调整构建代码以支持离线化,相关文档:https://alidocs.dingtalk.com/i/nodes/ydxXB52LJqexwD71F9K5XNMrJqjMp697?doc_type=wiki_doc";
|
|
207
|
+
canBuildOfflinePkg = false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (willSkip) {
|
|
211
|
+
if (!offlineConfig) {
|
|
212
|
+
return {
|
|
213
|
+
canBuild: false,
|
|
214
|
+
errorMsg: ""
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (offlineConfig.onlyTest === true && config.mode === "prod") {
|
|
218
|
+
console.log(`离线包${offlineConfig.name}只用于测试环境, 跳过构建线上环境的离线包构建`);
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
canBuild: true,
|
|
222
|
+
errorMsg: "",
|
|
223
|
+
hookPostBuild: async () => {
|
|
224
|
+
if (useOldPDB) {
|
|
225
|
+
fixEntryHtml(config, offlineConfig);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
let ossToken = "";
|
|
231
|
+
if (canBuildOfflinePkg) {
|
|
232
|
+
ossToken = getOssToken();
|
|
233
|
+
if (!ossToken) {
|
|
234
|
+
errorMsg = "获取ossToken失败";
|
|
235
|
+
canBuildOfflinePkg = false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!canBuildOfflinePkg) {
|
|
239
|
+
return {
|
|
240
|
+
canBuild: false,
|
|
241
|
+
errorMsg: errorMsg
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const hookPostBuild = async () => {
|
|
245
|
+
if (!offlineConfig || !config) return;
|
|
246
|
+
// 在构建完成后,执行一些操作
|
|
247
|
+
if (useOldPDB) {
|
|
248
|
+
fixEntryHtml(config, offlineConfig);
|
|
249
|
+
}
|
|
250
|
+
// 开始进行打包流程
|
|
251
|
+
const offlinePkgDir = join(config.workDir, "./offlinepkgDist");
|
|
252
|
+
if (fs.existsSync(offlinePkgDir)) {
|
|
253
|
+
fs.rmSync(offlinePkgDir, { recursive: true });
|
|
254
|
+
} else {
|
|
255
|
+
fs.mkdirSync(offlinePkgDir, { recursive: true });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const packageDir = join(offlinePkgDir, "./package");
|
|
259
|
+
fs.mkdirSync(packageDir, { recursive: true });
|
|
260
|
+
// cp dist到offlinepkgDist目录下
|
|
261
|
+
fs.cpSync(join(config.workDir, offlineConfig.distDir), join(packageDir, offlineConfig.distDir), { recursive: true });
|
|
262
|
+
|
|
263
|
+
const targetArchive = join(offlinePkgDir, `./${packageJson.version}.zip`);
|
|
264
|
+
await genArchive(targetArchive, packageDir);
|
|
265
|
+
log(`离线包zip包打包完成: ${targetArchive}`);
|
|
266
|
+
|
|
267
|
+
const packageDescJson = { // 包描述json,之后会通过接口更新
|
|
268
|
+
name: offlineConfig.name,
|
|
269
|
+
version: packageJson.version,
|
|
270
|
+
md5: "",
|
|
271
|
+
packageUrl: "",
|
|
272
|
+
items: [],
|
|
273
|
+
updateTime: String(Math.floor(Date.now() / 1000)),
|
|
274
|
+
cacheTime: offlineConfig.cacheTime || "7200",
|
|
275
|
+
isLazy: offlineConfig.isLazy || 0,
|
|
276
|
+
openType: offlineConfig.openType || "file",
|
|
277
|
+
};
|
|
278
|
+
Object.keys(offlineConfig.entry || {}).forEach(key => {
|
|
279
|
+
const val = offlineConfig.entry[key];
|
|
280
|
+
let vals = [];
|
|
281
|
+
if (typeof val === 'string') {
|
|
282
|
+
vals.push(val);
|
|
283
|
+
} else if (Array.isArray(val)) {
|
|
284
|
+
vals = val;
|
|
285
|
+
}
|
|
286
|
+
vals.forEach(v => {
|
|
287
|
+
packageDescJson.items.push({
|
|
288
|
+
"path": key,
|
|
289
|
+
"mimeType": "text/html",
|
|
290
|
+
"remoteUrl": v,
|
|
291
|
+
"isEntrance": 1
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
packageDescJson.md5 = await md5File(targetArchive);
|
|
296
|
+
const uploadResult = await uploadFile(ossToken, targetArchive, packageDescJson.md5);
|
|
297
|
+
packageDescJson.packageUrl = uploadResult.url;
|
|
298
|
+
fs.writeFileSync(join(offlinePkgDir, "./offline.package.json"), JSON.stringify(packageDescJson, null, 2));
|
|
299
|
+
log(`离线包描述json写入完成: ${join(offlinePkgDir, "./offline.package.json")}`);
|
|
300
|
+
|
|
301
|
+
if (config.mode !== "prod") {
|
|
302
|
+
packageDescJson.items.forEach(item => {
|
|
303
|
+
item.remoteUrl = item.remoteUrl.replace("https://y.tuwan.com", "https://y-test.tuwan.com");
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
await syncApi(userDeployHelperConfig, config.mode, offlineConfig, packageDescJson);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
canBuild: canBuildOfflinePkg,
|
|
310
|
+
errorMsg: errorMsg,
|
|
311
|
+
hookPostBuild: hookPostBuild,
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|