vite-plugin-deploy-oss 3.1.0 → 3.2.0
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 +20 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +93 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,9 @@ export default {
|
|
|
39
39
|
// 默认 true:有上传失败时抛错并让构建失败
|
|
40
40
|
failOnError: true,
|
|
41
41
|
|
|
42
|
+
// 生成并上传 OSS 汇总文件
|
|
43
|
+
manifest: true,
|
|
44
|
+
|
|
42
45
|
// 修改打包后的资源路径
|
|
43
46
|
configBase: `https://oss.eventnet.cn/H5/zz/test/`,
|
|
44
47
|
}),
|
|
@@ -52,3 +55,20 @@ export default {
|
|
|
52
55
|
- `open` 默认 `true`,建议通过环境变量控制开关(例如 `DEPLOY_OSS=1` 时再上传)。
|
|
53
56
|
- `fancy` 默认 `true`,TTY 终端下会显示实时动效进度(速度、预计剩余、并发、当前文件)。
|
|
54
57
|
- `failOnError` 默认 `true`,上传有失败会抛错,适合 CI 场景保证发布质量。
|
|
58
|
+
- `manifest` 默认关闭。开启后会在构建目录生成并上传 `oss-manifest.json`。
|
|
59
|
+
- `manifest: true` 时默认文件名为 `oss-manifest.json`,也支持 `manifest: { fileName: 'meta/oss-manifest.json' }` 自定义路径。
|
|
60
|
+
- `oss-manifest.json` 仅包含本次成功上传的文件,不包含汇总文件自身。
|
|
61
|
+
- `oss-manifest.json` 内容示例:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"version": 1742467200000,
|
|
66
|
+
"files": [
|
|
67
|
+
{
|
|
68
|
+
"file": "assets/index-abc123.js",
|
|
69
|
+
"key": "H5/zz/test/assets/index-abc123.js",
|
|
70
|
+
"url": "https://oss.eventnet.cn/H5/zz/test/assets/index-abc123.js"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import oss from 'ali-oss';
|
|
2
2
|
import { Plugin } from 'vite';
|
|
3
3
|
|
|
4
|
+
interface ManifestOption {
|
|
5
|
+
fileName?: string;
|
|
6
|
+
}
|
|
4
7
|
interface vitePluginDeployOssOption extends Omit<oss.Options, 'accessKeyId' | 'accessKeySecret' | 'bucket' | 'region'> {
|
|
5
8
|
configBase?: string;
|
|
6
9
|
accessKeyId: string;
|
|
@@ -20,6 +23,7 @@ interface vitePluginDeployOssOption extends Omit<oss.Options, 'accessKeyId' | 'a
|
|
|
20
23
|
concurrency?: number;
|
|
21
24
|
retryTimes?: number;
|
|
22
25
|
multipartThreshold?: number;
|
|
26
|
+
manifest?: boolean | ManifestOption;
|
|
23
27
|
}
|
|
24
28
|
declare function vitePluginDeployOss(option: vitePluginDeployOssOption): Plugin;
|
|
25
29
|
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import oss from "ali-oss";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { globSync } from "glob";
|
|
5
|
-
import { readdir, rm, stat, unlink } from "fs/promises";
|
|
6
|
-
import { resolve } from "path";
|
|
5
|
+
import { mkdir, readdir, rm, stat, unlink, writeFile } from "fs/promises";
|
|
6
|
+
import { dirname, resolve } from "path";
|
|
7
7
|
import ora from "ora";
|
|
8
8
|
import { normalizePath } from "vite";
|
|
9
9
|
var GARBAGE_FILE_REGEX = /(?:Thumbs\.db|\.DS_Store)$/i;
|
|
10
|
+
var DEFAULT_MANIFEST_FILE_NAME = "oss-manifest.json";
|
|
10
11
|
var removeEmptyDirectories = async (rootDir) => {
|
|
11
12
|
const deletedDirectories = [];
|
|
12
13
|
const visit = async (dirPath) => {
|
|
@@ -32,6 +33,45 @@ var removeEmptyDirectories = async (rootDir) => {
|
|
|
32
33
|
return deletedDirectories;
|
|
33
34
|
};
|
|
34
35
|
var normalizeObjectKey = (targetDir, relativeFilePath) => normalizePath(`${targetDir}/${relativeFilePath}`).replace(/\/{2,}/g, "/").replace(/^\/+/, "");
|
|
36
|
+
var normalizeManifestFileName = (fileName) => {
|
|
37
|
+
const normalized = normalizePath(fileName || DEFAULT_MANIFEST_FILE_NAME).replace(/^\/+/, "").replace(/\/{2,}/g, "/");
|
|
38
|
+
return normalized || DEFAULT_MANIFEST_FILE_NAME;
|
|
39
|
+
};
|
|
40
|
+
var resolveManifestFileName = (manifest) => {
|
|
41
|
+
if (!manifest) return null;
|
|
42
|
+
if (manifest === true) return DEFAULT_MANIFEST_FILE_NAME;
|
|
43
|
+
return normalizeManifestFileName(manifest.fileName);
|
|
44
|
+
};
|
|
45
|
+
var normalizeUrlBase = (base) => {
|
|
46
|
+
const normalized = base.replace(/\\/g, "/");
|
|
47
|
+
const protocolSeparatorIndex = normalized.indexOf("://");
|
|
48
|
+
if (protocolSeparatorIndex >= 0) {
|
|
49
|
+
const pathIndex = normalized.indexOf("/", protocolSeparatorIndex + 3);
|
|
50
|
+
if (pathIndex < 0) return normalized;
|
|
51
|
+
return `${normalized.slice(0, pathIndex)}${normalized.slice(pathIndex).replace(/\/{2,}/g, "/")}`;
|
|
52
|
+
}
|
|
53
|
+
if (normalized.startsWith("//")) {
|
|
54
|
+
const pathIndex = normalized.indexOf("/", 2);
|
|
55
|
+
if (pathIndex < 0) return normalized;
|
|
56
|
+
return `${normalized.slice(0, pathIndex)}${normalized.slice(pathIndex).replace(/\/{2,}/g, "/")}`;
|
|
57
|
+
}
|
|
58
|
+
return normalized.replace(/\/{2,}/g, "/");
|
|
59
|
+
};
|
|
60
|
+
var encodeUrlPath = (path) => encodeURI(path.replace(/^\/+/, ""));
|
|
61
|
+
var joinUrlPath = (base, path) => `${normalizeUrlBase(base).replace(/\/+$/, "")}/${encodeUrlPath(path)}`;
|
|
62
|
+
var resolveUploadedFileUrl = (relativeFilePath, objectKey, configBase, alias) => {
|
|
63
|
+
if (configBase) return joinUrlPath(configBase, relativeFilePath);
|
|
64
|
+
if (alias) return joinUrlPath(alias, objectKey);
|
|
65
|
+
return objectKey;
|
|
66
|
+
};
|
|
67
|
+
var createManifestPayload = (results, configBase, alias) => ({
|
|
68
|
+
version: Date.now(),
|
|
69
|
+
files: results.filter((result) => result.success).map((result) => ({
|
|
70
|
+
file: result.relativeFilePath,
|
|
71
|
+
key: result.name,
|
|
72
|
+
url: resolveUploadedFileUrl(result.relativeFilePath, result.name, configBase, alias)
|
|
73
|
+
}))
|
|
74
|
+
});
|
|
35
75
|
var formatBytes = (bytes) => {
|
|
36
76
|
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
|
|
37
77
|
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
@@ -91,6 +131,7 @@ function vitePluginDeployOss(option) {
|
|
|
91
131
|
concurrency = 5,
|
|
92
132
|
retryTimes = 3,
|
|
93
133
|
multipartThreshold = 10 * 1024 * 1024,
|
|
134
|
+
manifest = false,
|
|
94
135
|
...props
|
|
95
136
|
} = option || {};
|
|
96
137
|
let buildFailed = false;
|
|
@@ -115,12 +156,13 @@ function vitePluginDeployOss(option) {
|
|
|
115
156
|
errors.push("multipartThreshold must be > 0");
|
|
116
157
|
return errors;
|
|
117
158
|
};
|
|
159
|
+
const uploadSingleTask = async (client, task) => uploadFileWithRetry(client, task, false);
|
|
118
160
|
const uploadFileWithRetry = async (client, task, silentLogs, maxRetries = retryTimes) => {
|
|
119
161
|
const shouldUseMultipart = task.size >= multipartThreshold;
|
|
120
162
|
const headers = {
|
|
121
163
|
"x-oss-storage-class": "Standard",
|
|
122
164
|
"x-oss-object-acl": "default",
|
|
123
|
-
"Cache-Control": noCache ? "no-cache" : "public, max-age=86400, immutable",
|
|
165
|
+
"Cache-Control": task.cacheControl || (noCache ? "no-cache" : "public, max-age=86400, immutable"),
|
|
124
166
|
"x-oss-forbid-overwrite": overwrite ? "false" : "true"
|
|
125
167
|
};
|
|
126
168
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -142,7 +184,14 @@ function vitePluginDeployOss(option) {
|
|
|
142
184
|
console.warn(`${chalk.yellow("\u26A0")} \u5220\u9664\u672C\u5730\u6587\u4EF6\u5931\u8D25: ${task.filePath}`);
|
|
143
185
|
}
|
|
144
186
|
}
|
|
145
|
-
return {
|
|
187
|
+
return {
|
|
188
|
+
success: true,
|
|
189
|
+
file: task.filePath,
|
|
190
|
+
relativeFilePath: task.relativeFilePath,
|
|
191
|
+
name: task.name,
|
|
192
|
+
size: task.size,
|
|
193
|
+
retries: attempt - 1
|
|
194
|
+
};
|
|
146
195
|
} else {
|
|
147
196
|
throw new Error(`Upload failed with status: ${result.res.status}`);
|
|
148
197
|
}
|
|
@@ -156,6 +205,7 @@ function vitePluginDeployOss(option) {
|
|
|
156
205
|
return {
|
|
157
206
|
success: false,
|
|
158
207
|
file: task.filePath,
|
|
208
|
+
relativeFilePath: task.relativeFilePath,
|
|
159
209
|
name: task.name,
|
|
160
210
|
size: task.size,
|
|
161
211
|
retries: attempt - 1,
|
|
@@ -172,6 +222,7 @@ function vitePluginDeployOss(option) {
|
|
|
172
222
|
return {
|
|
173
223
|
success: false,
|
|
174
224
|
file: task.filePath,
|
|
225
|
+
relativeFilePath: task.relativeFilePath,
|
|
175
226
|
name: task.name,
|
|
176
227
|
size: task.size,
|
|
177
228
|
retries: maxRetries,
|
|
@@ -192,9 +243,9 @@ function vitePluginDeployOss(option) {
|
|
|
192
243
|
const name = normalizeObjectKey(uploadDir, relativeFilePath);
|
|
193
244
|
try {
|
|
194
245
|
const fileStats = await stat(filePath);
|
|
195
|
-
return { task: { filePath, name, size: fileStats.size } };
|
|
246
|
+
return { task: { filePath, relativeFilePath, name, size: fileStats.size } };
|
|
196
247
|
} catch (error) {
|
|
197
|
-
return { task: null, error, filePath, name };
|
|
248
|
+
return { task: null, error, filePath, relativeFilePath, name };
|
|
198
249
|
}
|
|
199
250
|
})
|
|
200
251
|
);
|
|
@@ -207,6 +258,7 @@ function vitePluginDeployOss(option) {
|
|
|
207
258
|
results.push({
|
|
208
259
|
success: false,
|
|
209
260
|
file: candidate.filePath,
|
|
261
|
+
relativeFilePath: candidate.relativeFilePath,
|
|
210
262
|
name: candidate.name,
|
|
211
263
|
size: 0,
|
|
212
264
|
retries: 0,
|
|
@@ -321,11 +373,12 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
|
|
|
321
373
|
if (!open || !upload || buildFailed || !resolvedConfig) return;
|
|
322
374
|
const startTime = Date.now();
|
|
323
375
|
const client = new oss({ region, accessKeyId, accessKeySecret, secure, bucket, ...props });
|
|
376
|
+
const manifestFileName = resolveManifestFileName(manifest);
|
|
324
377
|
const files = globSync("**/*", {
|
|
325
378
|
cwd: outDir,
|
|
326
379
|
nodir: true,
|
|
327
380
|
ignore: Array.isArray(skip) ? skip : [skip]
|
|
328
|
-
}).map((file) => normalizePath(file));
|
|
381
|
+
}).map((file) => normalizePath(file)).filter((file) => file !== manifestFileName);
|
|
329
382
|
if (files.length === 0) {
|
|
330
383
|
console.log(`${chalk.yellow("\u26A0 \u6CA1\u6709\u627E\u5230\u9700\u8981\u4E0A\u4F20\u7684\u6587\u4EF6")}`);
|
|
331
384
|
return;
|
|
@@ -382,6 +435,39 @@ ${chalk.gray("\u7EDF\u8BA1:")}`);
|
|
|
382
435
|
}
|
|
383
436
|
console.log("");
|
|
384
437
|
}
|
|
438
|
+
if (manifestFileName) {
|
|
439
|
+
const manifestRelativeFilePath = manifestFileName;
|
|
440
|
+
const manifestFilePath = normalizePath(resolve(outDir, manifestRelativeFilePath));
|
|
441
|
+
const manifestObjectKey = normalizeObjectKey(uploadDir, manifestRelativeFilePath);
|
|
442
|
+
await mkdir(dirname(manifestFilePath), { recursive: true });
|
|
443
|
+
await writeFile(
|
|
444
|
+
manifestFilePath,
|
|
445
|
+
JSON.stringify(createManifestPayload(results, configBase, alias), null, 2),
|
|
446
|
+
"utf8"
|
|
447
|
+
);
|
|
448
|
+
const manifestStats = await stat(manifestFilePath);
|
|
449
|
+
const manifestResult = await uploadSingleTask(client, {
|
|
450
|
+
filePath: manifestFilePath,
|
|
451
|
+
relativeFilePath: manifestRelativeFilePath,
|
|
452
|
+
name: manifestObjectKey,
|
|
453
|
+
size: manifestStats.size,
|
|
454
|
+
cacheControl: "no-cache"
|
|
455
|
+
});
|
|
456
|
+
if (!manifestResult.success) {
|
|
457
|
+
throw manifestResult.error || new Error(`Failed to upload manifest: ${manifestRelativeFilePath}`);
|
|
458
|
+
}
|
|
459
|
+
const manifestUrl = resolveUploadedFileUrl(
|
|
460
|
+
manifestRelativeFilePath,
|
|
461
|
+
manifestObjectKey,
|
|
462
|
+
configBase,
|
|
463
|
+
alias
|
|
464
|
+
);
|
|
465
|
+
console.log(chalk.cyan("Manifest:"));
|
|
466
|
+
console.log(` ${chalk.gray("File:")} ${chalk.yellow(manifestFilePath)}`);
|
|
467
|
+
console.log(` ${chalk.gray("Target:")} ${chalk.yellow(manifestObjectKey)}`);
|
|
468
|
+
console.log(` ${chalk.gray("URL:")} ${chalk.green(manifestUrl)}`);
|
|
469
|
+
console.log("");
|
|
470
|
+
}
|
|
385
471
|
try {
|
|
386
472
|
await removeEmptyDirectories(outDir);
|
|
387
473
|
} catch (error) {
|