@seayoo-web/finder 2.2.1 → 2.2.3
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/index.js +314 -340
- package/package.json +14 -14
- package/types/src/core.d.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -1,376 +1,350 @@
|
|
|
1
|
-
import fs, {
|
|
2
|
-
import path, {
|
|
1
|
+
import fs, { existsSync, lstatSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
2
|
+
import path, { basename, dirname, join, normalize, relative, resolve, sep } from "path";
|
|
3
3
|
import open from "open";
|
|
4
|
-
import
|
|
4
|
+
import colors from "picocolors";
|
|
5
5
|
import { zip } from "compressing";
|
|
6
6
|
import os from "os";
|
|
7
|
-
|
|
7
|
+
//#region src/compress.ts
|
|
8
|
+
var presetIgnores = [
|
|
9
|
+
"node_modules/",
|
|
10
|
+
".git/",
|
|
11
|
+
".vscode/",
|
|
12
|
+
"__MACOSX/",
|
|
13
|
+
".DS_Store",
|
|
14
|
+
".gitkeep"
|
|
15
|
+
];
|
|
16
|
+
/** 代码压缩 */
|
|
8
17
|
function compressToBuffer(sourceDir, ignoreFiles, debug) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
zipStream.on("data", (chunk) => chunks.push(chunk)).on("end", () => resolve2(Buffer.concat(chunks))).on("error", reject);
|
|
26
|
-
});
|
|
18
|
+
const ignoreFileList = [...presetIgnores, ...ignoreFiles || []];
|
|
19
|
+
const filesToCompress = getAllFiles(sourceDir, ignoreFileList);
|
|
20
|
+
const zipStream = new zip.Stream();
|
|
21
|
+
filesToCompress.forEach((file) => {
|
|
22
|
+
zipStream.addEntry(file, { relativePath: relative(sourceDir, file) });
|
|
23
|
+
});
|
|
24
|
+
if (debug) console.log({
|
|
25
|
+
method: "compressToBuffer",
|
|
26
|
+
sourceDir,
|
|
27
|
+
ignores: ignoreFileList,
|
|
28
|
+
filesCount: filesToCompress.length
|
|
29
|
+
});
|
|
30
|
+
const chunks = [];
|
|
31
|
+
return new Promise(function(resolve, reject) {
|
|
32
|
+
zipStream.on("data", (chunk) => chunks.push(chunk)).on("end", () => resolve(Buffer.concat(chunks))).on("error", reject);
|
|
33
|
+
});
|
|
27
34
|
}
|
|
28
35
|
function getAllFiles(dir, ignores = []) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
list.push(filePath);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
return list;
|
|
36
|
+
const list = [];
|
|
37
|
+
readdirSync(dir).forEach((file) => {
|
|
38
|
+
const filePath = join(dir, file);
|
|
39
|
+
if (lstatSync(filePath).isDirectory()) list.push(...getAllFiles(filePath, ignores));
|
|
40
|
+
else if (!isIgnoreFile(filePath, ignores)) list.push(filePath);
|
|
41
|
+
});
|
|
42
|
+
return list;
|
|
40
43
|
}
|
|
41
44
|
function isIgnoreFile(filePath, ignores) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return pattern.includes("*") ? getRegexp(pattern).test(filename) : filename === pattern;
|
|
49
|
-
});
|
|
45
|
+
const filename = basename(filePath);
|
|
46
|
+
const dirs = normalize(filePath).split(sep);
|
|
47
|
+
return ignores.some((pattern) => {
|
|
48
|
+
if (pattern.endsWith("/")) return pattern.includes("*") ? dirs.some((dir) => getRegexp(pattern.slice(0, -1)).test(dir)) : dirs.includes(pattern.slice(0, -1));
|
|
49
|
+
return pattern.includes("*") ? getRegexp(pattern).test(filename) : filename === pattern;
|
|
50
|
+
});
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const fixRegStr = path2.replace(/([\\(){}\[\]\^\$\+\-\?\.|])/g, "\\$1").replace(/\*{1,}/g, ".*");
|
|
57
|
-
return pathRegCache[path2] = new RegExp("^" + fixRegStr + "$");
|
|
52
|
+
var pathRegCache = {};
|
|
53
|
+
var getRegexp = function(path) {
|
|
54
|
+
if (path && pathRegCache[path]) return pathRegCache[path];
|
|
55
|
+
const fixRegStr = path.replace(/([\\(){}[\]^$+\-?.|])/g, "\\$1").replace(/\*{1,}/g, ".*");
|
|
56
|
+
return pathRegCache[path] = new RegExp("^" + fixRegStr + "$");
|
|
58
57
|
};
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/utils.ts
|
|
59
60
|
function pure(url) {
|
|
60
|
-
|
|
61
|
+
return url.replace(/(?:^https?:\/\/|\/*$)/gi, "");
|
|
61
62
|
}
|
|
62
63
|
function getSystemTempDir() {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
return dir;
|
|
64
|
+
const dir = path.resolve(os.tmpdir(), "webfinder");
|
|
65
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
66
|
+
return dir;
|
|
68
67
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
if (+process.version.replace(/\..+/, "").slice(1) < 20) process.emitWarning = function() {};
|
|
69
|
+
async function request({ url, method, headers, data }) {
|
|
70
|
+
const hasFileUpload = method === "POST" && data && Object.values(data).some((value) => typeof value === "object" && "buffer" in value);
|
|
71
|
+
const reqHeaders = new Headers();
|
|
72
|
+
if (headers) Object.entries(headers).forEach(([key, value]) => {
|
|
73
|
+
reqHeaders.set(key, value);
|
|
74
|
+
});
|
|
75
|
+
const requestInit = {
|
|
76
|
+
method,
|
|
77
|
+
headers: reqHeaders
|
|
78
|
+
};
|
|
79
|
+
try {
|
|
80
|
+
if (data) if (hasFileUpload) {
|
|
81
|
+
const formData = new FormData();
|
|
82
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
83
|
+
if (typeof value === "object" && "buffer" in value) {
|
|
84
|
+
const { buffer, filename, contentType } = value;
|
|
85
|
+
const blob = new Blob([new Uint8Array(buffer)], { type: contentType });
|
|
86
|
+
formData.append(key, blob, filename);
|
|
87
|
+
} else formData.append(key, String(value));
|
|
88
|
+
});
|
|
89
|
+
requestInit.headers = reqHeaders;
|
|
90
|
+
requestInit.body = formData;
|
|
91
|
+
} else {
|
|
92
|
+
reqHeaders.set("content-type", reqHeaders.get("content-type") || "application/json");
|
|
93
|
+
requestInit.headers = reqHeaders;
|
|
94
|
+
requestInit.body = JSON.stringify(data);
|
|
95
|
+
}
|
|
96
|
+
const response = await fetch(url, requestInit);
|
|
97
|
+
let responseData;
|
|
98
|
+
if (response.headers.get("content-type")?.includes("application/json")) responseData = await response.json();
|
|
99
|
+
else responseData = await response.text();
|
|
100
|
+
return {
|
|
101
|
+
status: response.status,
|
|
102
|
+
message: response.statusText,
|
|
103
|
+
data: responseData
|
|
104
|
+
};
|
|
105
|
+
} catch (err) {
|
|
106
|
+
return {
|
|
107
|
+
status: 500,
|
|
108
|
+
message: err instanceof Error ? err.message : String(err),
|
|
109
|
+
data: null
|
|
110
|
+
};
|
|
111
|
+
}
|
|
73
112
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
84
|
-
reqHeaders.set(key, value);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
const requestInit = { method, headers: reqHeaders };
|
|
88
|
-
try {
|
|
89
|
-
if (data) {
|
|
90
|
-
if (hasFileUpload) {
|
|
91
|
-
const formData = new FormData();
|
|
92
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
93
|
-
if (typeof value === "object" && "buffer" in value) {
|
|
94
|
-
const { buffer, filename, contentType: contentType2 } = value;
|
|
95
|
-
const blob = new Blob([new Uint8Array(buffer)], { type: contentType2 });
|
|
96
|
-
formData.append(key, blob, filename);
|
|
97
|
-
} else {
|
|
98
|
-
formData.append(key, String(value));
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
requestInit.headers = reqHeaders;
|
|
102
|
-
requestInit.body = formData;
|
|
103
|
-
} else {
|
|
104
|
-
reqHeaders.set("content-type", reqHeaders.get("content-type") || "application/json");
|
|
105
|
-
requestInit.headers = reqHeaders;
|
|
106
|
-
requestInit.body = JSON.stringify(data);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const response = await fetch(url, requestInit);
|
|
110
|
-
let responseData;
|
|
111
|
-
const contentType = response.headers.get("content-type");
|
|
112
|
-
if (contentType?.includes("application/json")) {
|
|
113
|
-
responseData = await response.json();
|
|
114
|
-
} else {
|
|
115
|
-
responseData = await response.text();
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
status: response.status,
|
|
119
|
-
message: response.statusText,
|
|
120
|
-
data: responseData
|
|
121
|
-
};
|
|
122
|
-
} catch (err) {
|
|
123
|
-
return {
|
|
124
|
-
status: 500,
|
|
125
|
-
message: err instanceof Error ? err.message : String(err),
|
|
126
|
-
data: null
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
const FinderServers = {
|
|
131
|
-
"finder.seayoo.io": [],
|
|
132
|
-
"finder.seayoo.com": [],
|
|
133
|
-
"finder.seayoo.internal": [],
|
|
134
|
-
"finder.dev.seayoo.com": [],
|
|
135
|
-
"finder.dev.seayoo.io": []
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/service.ts
|
|
115
|
+
/** finder 服务器列表以及所支持的域名 */
|
|
116
|
+
var FinderServers = {
|
|
117
|
+
"finder.seayoo.io": [],
|
|
118
|
+
"finder.seayoo.com": [],
|
|
119
|
+
"finder.seayoo.internal": [],
|
|
120
|
+
"finder.dev.seayoo.com": [],
|
|
121
|
+
"finder.dev.seayoo.io": []
|
|
136
122
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
123
|
+
/** finder 的 api path */
|
|
124
|
+
var FinderApiPaths = {
|
|
125
|
+
deploy: "/service/deploy",
|
|
126
|
+
inspect: "/inspect/supported/projects",
|
|
127
|
+
upload: "/service/upload"
|
|
141
128
|
};
|
|
129
|
+
/** 将指定的 zip buffer 部署到指定目录 */
|
|
142
130
|
async function deploy(option) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
console.log("部署完毕,接口返回内容", data);
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
previewUrl: url.endsWith("/") ? url.replace(/\/*$/, "/") + "index.html?" + Math.random().toString(16).slice(2) : url.startsWith("http") ? url : ""
|
|
174
|
-
};
|
|
131
|
+
const { debug, target, buffer, user, key, payload, ignoreCache } = option;
|
|
132
|
+
const targetServer = await findTargetServer(target, debug, ignoreCache);
|
|
133
|
+
if (!targetServer) throw colors.bgRed(`finder不支持该域名部署,请检查 ${target}`);
|
|
134
|
+
if (!user || !key) throw colors.bgRed("部署缺少认证信息(user & key)");
|
|
135
|
+
const zipMockName = `${Date.now()}${Math.random().toString(16).slice(-3)}.zip`;
|
|
136
|
+
const { status, data } = await request({
|
|
137
|
+
url: `${getFinderServerFullPath(targetServer)}${FinderApiPaths.deploy}?target=${encodeURIComponent(pure(target))}`,
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: {
|
|
140
|
+
user,
|
|
141
|
+
key
|
|
142
|
+
},
|
|
143
|
+
data: {
|
|
144
|
+
path: zipMockName,
|
|
145
|
+
file: {
|
|
146
|
+
buffer,
|
|
147
|
+
filename: zipMockName,
|
|
148
|
+
contentType: "application/octet-stream"
|
|
149
|
+
},
|
|
150
|
+
payload: payload ? JSON.stringify(payload) : ""
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
if (status !== 200) throw colors.bgRed(`部署接口错误,Server: ${targetServer},Status: ${status},Response: ${JSON.stringify(data)}`);
|
|
154
|
+
if (!data || typeof data !== "object" || "err" in data && data.err || !("data" in data) || typeof data.data !== "string") throw colors.bgRed(`部署接口响应错误。Server: ${targetServer},Response: ${JSON.stringify(data)}`);
|
|
155
|
+
const url = data.data;
|
|
156
|
+
if (debug) console.log("部署完毕,接口返回内容", data);
|
|
157
|
+
return { previewUrl: url.endsWith("/") ? url.replace(/\/*$/, "/") + "index.html?" + Math.random().toString(16).slice(2) : url.startsWith("http") ? url : "" };
|
|
175
158
|
}
|
|
159
|
+
/** 将文件上传到指定位置 */
|
|
176
160
|
async function upload(option) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return { previewUrl: `https://${pure(target)}` };
|
|
161
|
+
const { debug, target, buffer, user, key, ignoreCache } = option;
|
|
162
|
+
const targetServer = await findTargetServer(target, debug, ignoreCache);
|
|
163
|
+
if (!targetServer) throw colors.bgRed(`finder不支持该域名部署,请检查 ${target}`);
|
|
164
|
+
if (!user || !key) throw colors.bgRed("部署缺少认证信息(user & key)");
|
|
165
|
+
const filename = basename(target);
|
|
166
|
+
const deployTarget = dirname(pure(target));
|
|
167
|
+
const { status, data } = await request({
|
|
168
|
+
url: `${getFinderServerFullPath(targetServer)}${FinderApiPaths.upload}?target=${encodeURIComponent(deployTarget)}`,
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: {
|
|
171
|
+
user,
|
|
172
|
+
key
|
|
173
|
+
},
|
|
174
|
+
data: {
|
|
175
|
+
path: filename,
|
|
176
|
+
file: {
|
|
177
|
+
buffer,
|
|
178
|
+
filename,
|
|
179
|
+
contentType: "application/octet-stream"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
if (status !== 200) throw colors.bgRed(`上传接口错误,Server: ${targetServer},Status: ${status}`);
|
|
184
|
+
if (!data || typeof data !== "object" || "err" in data && data.err || !("data" in data) || typeof data.data !== "string") throw colors.bgRed(`上传接口响应错误,Server: ${targetServer},Response: ${JSON.stringify(data)}`);
|
|
185
|
+
return { previewUrl: `https://${pure(target)}` };
|
|
203
186
|
}
|
|
204
|
-
|
|
205
|
-
|
|
187
|
+
var getFinderServerFullPath = function(domain) {
|
|
188
|
+
return (domain.endsWith("internal") ? "http://" : "https://") + domain;
|
|
206
189
|
};
|
|
207
190
|
async function findTargetServer(target, debug, ignoreCache) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
await updateSupportedProjects(true, debug);
|
|
217
|
-
for (const domain in FinderServers) {
|
|
218
|
-
if (FinderServers[domain].find((url) => t.startsWith(url))) {
|
|
219
|
-
return domain;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return null;
|
|
191
|
+
const t = pure(target);
|
|
192
|
+
await updateSupportedProjects(!!ignoreCache, debug);
|
|
193
|
+
for (const domain in FinderServers) if (FinderServers[domain].find((url) => t.startsWith(url))) return domain;
|
|
194
|
+
if (!ignoreCache) {
|
|
195
|
+
await updateSupportedProjects(true, debug);
|
|
196
|
+
for (const domain in FinderServers) if (FinderServers[domain].find((url) => t.startsWith(url))) return domain;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
224
199
|
}
|
|
225
200
|
async function updateSupportedProjects(force = false, debug) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
FinderServers[domain] = await getServerSupportedProjects(domain, force, debug) || [];
|
|
229
|
-
}
|
|
201
|
+
const domains = Object.keys(FinderServers);
|
|
202
|
+
await Promise.all(domains.map((domain) => getServerSupportedProjects(domain, force, debug).then((r) => FinderServers[domain] = r, () => FinderServers[domain] = [])));
|
|
230
203
|
}
|
|
231
204
|
async function getServerSupportedProjects(serverDomain, ignoreCache = false, debug) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
205
|
+
const cacheFile = resolve(getSystemTempDir(), `${serverDomain}.json`);
|
|
206
|
+
if (existsSync(cacheFile) && !ignoreCache) try {
|
|
207
|
+
const cache = JSON.parse(readFileSync(cacheFile).toString());
|
|
208
|
+
if (Array.isArray(cache) && cache.every((d) => typeof d === "string")) {
|
|
209
|
+
if (debug) console.log({
|
|
210
|
+
method: "getServerSupportedProjects",
|
|
211
|
+
serverDomain,
|
|
212
|
+
cache
|
|
213
|
+
});
|
|
214
|
+
return cache;
|
|
215
|
+
}
|
|
216
|
+
} catch (e) {
|
|
217
|
+
console.error(colors.bgRed("ReadFinderCacheError"), e);
|
|
218
|
+
}
|
|
219
|
+
const inspectURL = `${getFinderServerFullPath(serverDomain)}${FinderApiPaths.inspect}`;
|
|
220
|
+
const { status, message, data } = await request({
|
|
221
|
+
url: inspectURL,
|
|
222
|
+
method: "GET",
|
|
223
|
+
headers: { "user-agent": `web finder agent v2` }
|
|
224
|
+
});
|
|
225
|
+
if (status !== 200) {
|
|
226
|
+
if (debug) console.error(colors.bgRed(`服务器 ${inspectURL} 检查接口错误`), colors.red(message || ""));
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
if (!Array.isArray(data) || !data.every((d) => typeof d === "string")) {
|
|
230
|
+
console.error(colors.bgRed(`服务器 ${inspectURL} 接口返回内容错误`), colors.red(JSON.stringify(data)));
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
if (debug) console.log({
|
|
234
|
+
method: "getServerSupportedProjects",
|
|
235
|
+
serverDomain,
|
|
236
|
+
list: data
|
|
237
|
+
});
|
|
238
|
+
const pureList = data.map(pure);
|
|
239
|
+
writeFileSync(cacheFile, JSON.stringify(pureList));
|
|
240
|
+
return pureList;
|
|
268
241
|
}
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/core.ts
|
|
244
|
+
/** 部署一个目录 */
|
|
269
245
|
async function finderDeploy(option) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
246
|
+
const { dist, ignoreFiles, deployTo, user, key, debug, preview, commitLogs, ignoreCache } = option;
|
|
247
|
+
if (!dist) throw colors.bgRed("部署参数 dist 缺失");
|
|
248
|
+
if (!existsSync(resolve(dist)) || !lstatSync(resolve(dist)).isDirectory()) throw colors.bgRed("部署参数错误,dist 需要是一个存在的文件目录") + " " + colors.red(dist);
|
|
249
|
+
const payload = commitLogs ? { 更新内容: commitLogs } : void 0;
|
|
250
|
+
if (debug) console.log({
|
|
251
|
+
method: "finderDeploy",
|
|
252
|
+
dist,
|
|
253
|
+
deployTo,
|
|
254
|
+
ignoreFiles,
|
|
255
|
+
payload,
|
|
256
|
+
user,
|
|
257
|
+
preview
|
|
258
|
+
});
|
|
259
|
+
const buffer = await compressToBuffer(dist, ignoreFiles, debug).catch((e) => {
|
|
260
|
+
throw colors.bgRed("部署预处理之压缩代码失败") + " " + (e instanceof Error ? e.message : String(e));
|
|
261
|
+
});
|
|
262
|
+
if (Array.isArray(deployTo)) {
|
|
263
|
+
const results = await Promise.all(deployTo.map((target) => {
|
|
264
|
+
return deploy({
|
|
265
|
+
debug,
|
|
266
|
+
target,
|
|
267
|
+
buffer,
|
|
268
|
+
user,
|
|
269
|
+
key,
|
|
270
|
+
payload,
|
|
271
|
+
ignoreCache
|
|
272
|
+
});
|
|
273
|
+
}));
|
|
274
|
+
const lastDeployResult = results[results.length - 1];
|
|
275
|
+
if (lastDeployResult && lastDeployResult.previewUrl) doPreview(lastDeployResult.previewUrl, preview);
|
|
276
|
+
return deployTo.join(",");
|
|
277
|
+
}
|
|
278
|
+
const deployResult = await deploy({
|
|
279
|
+
debug,
|
|
280
|
+
target: deployTo,
|
|
281
|
+
buffer,
|
|
282
|
+
user,
|
|
283
|
+
key,
|
|
284
|
+
payload,
|
|
285
|
+
ignoreCache
|
|
286
|
+
});
|
|
287
|
+
if (deployResult && deployResult.previewUrl) doPreview(deployResult.previewUrl, preview);
|
|
288
|
+
return deployTo;
|
|
309
289
|
}
|
|
310
290
|
function doPreview(defaultPreviewUrl, option) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
files.forEach((file) => {
|
|
321
|
-
open(base + file);
|
|
322
|
-
});
|
|
291
|
+
if (!option) return;
|
|
292
|
+
if (option === true) {
|
|
293
|
+
open(defaultPreviewUrl);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const base = defaultPreviewUrl.replace(/index\.html.*$/i, "");
|
|
297
|
+
(Array.isArray(option) ? option : [option]).forEach((file) => {
|
|
298
|
+
open(base + file);
|
|
299
|
+
});
|
|
323
300
|
}
|
|
301
|
+
/** 上传一个文件到 finder */
|
|
324
302
|
async function finderUpload(option) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
303
|
+
const { filePath, fileContent, deployTo, user, key, preview, debug, ignoreCache } = option;
|
|
304
|
+
if (!filePath && !fileContent) throw colors.bgRed("部署缺少参数 filePath(文件全路径) 或 fileContent(文件内容)");
|
|
305
|
+
if (filePath && !existsSync(filePath)) throw colors.bgRed("部署文件不存在(请确保传入完整文件路径)") + " " + colors.red(filePath);
|
|
306
|
+
if (!deployTo) throw colors.bgRed("部署缺少参数 deployTo(部署目标)");
|
|
307
|
+
const content = filePath ? Buffer.from(readFileSync(filePath)) : Buffer.isBuffer(fileContent) ? fileContent : Buffer.from(fileContent || "");
|
|
308
|
+
const resp = await upload({
|
|
309
|
+
debug,
|
|
310
|
+
target: pure(deployTo),
|
|
311
|
+
buffer: content,
|
|
312
|
+
user,
|
|
313
|
+
key,
|
|
314
|
+
ignoreCache
|
|
315
|
+
});
|
|
316
|
+
if (preview && resp.previewUrl) open(resp.previewUrl);
|
|
340
317
|
}
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/plugin.ts
|
|
341
320
|
function viteDeployPlugin(option) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
};
|
|
321
|
+
let distDir = null;
|
|
322
|
+
return {
|
|
323
|
+
name: "finerDeployAgent",
|
|
324
|
+
generateBundle({ dir }) {
|
|
325
|
+
distDir = process.cwd();
|
|
326
|
+
if (dir) distDir = resolve(distDir, dir);
|
|
327
|
+
},
|
|
328
|
+
async closeBundle() {
|
|
329
|
+
if (!distDir) {
|
|
330
|
+
console.error(colors.bgRed("没有找到部署资源,请尝试检查 build 是否生成了正确的资源"));
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
await option.onBeforeDeploy?.(distDir);
|
|
334
|
+
const result = await finderDeploy({
|
|
335
|
+
preview: true,
|
|
336
|
+
...option,
|
|
337
|
+
dist: distDir
|
|
338
|
+
}).catch((e) => e instanceof Error ? e : typeof e === "string" ? new Error(e) : /* @__PURE__ */ new Error(e + ""));
|
|
339
|
+
if (result instanceof Error) {
|
|
340
|
+
option.onError?.();
|
|
341
|
+
console.log(colors.bgRed("部署失败"), colors.red(result.message));
|
|
342
|
+
} else {
|
|
343
|
+
option.onFinished?.();
|
|
344
|
+
console.log(colors.bgGreen("部署成功"), colors.green(result || ""));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
371
348
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
finderUpload,
|
|
375
|
-
viteDeployPlugin
|
|
376
|
-
};
|
|
349
|
+
//#endregion
|
|
350
|
+
export { finderDeploy, finderUpload, viteDeployPlugin };
|
package/package.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seayoo-web/finder",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "agent for web finder",
|
|
5
|
-
"
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "web@seayoo.com",
|
|
6
7
|
"source": "index.ts",
|
|
7
|
-
"main": "./dist/index.js",
|
|
8
|
-
"module": "./dist/index.js",
|
|
9
|
-
"types": "./types/index.d.ts",
|
|
10
8
|
"files": [
|
|
11
9
|
"dist",
|
|
12
10
|
"types",
|
|
13
11
|
"README.md"
|
|
14
12
|
],
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"license": "MIT",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"module": "./dist/index.js",
|
|
16
|
+
"types": "./types/index.d.ts",
|
|
20
17
|
"publishConfig": {
|
|
21
18
|
"access": "public"
|
|
22
19
|
},
|
|
23
20
|
"dependencies": {
|
|
24
|
-
"colors": "^1.4.0",
|
|
25
21
|
"compressing": "^1.10.1",
|
|
26
|
-
"open": "^10.1.0"
|
|
22
|
+
"open": "^10.1.0",
|
|
23
|
+
"picocolors": "^1.1.1"
|
|
27
24
|
},
|
|
28
25
|
"devDependencies": {
|
|
29
26
|
"@types/node": "^22.13.1",
|
|
30
|
-
"vitest": "^
|
|
31
|
-
"@seayoo-web/tsconfig": "^1.0.
|
|
27
|
+
"vitest": "^4.1.4",
|
|
28
|
+
"@seayoo-web/tsconfig": "^1.0.6"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "vite build && tsc --emitDeclarationOnly",
|
package/types/src/core.d.ts
CHANGED