vite-plugin-deploy-oss 1.2.3 → 2.0.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 CHANGED
@@ -24,15 +24,30 @@ export default {
24
24
  plugins: [
25
25
  // 在最后一个插件中使用
26
26
  vitePluginDeployOss({
27
+ // 建议按环境变量开关上传,避免本地/CI误上传
28
+ open: process.env.DEPLOY_OSS === '1',
29
+ // 终端实时动效进度面板(默认 true)
30
+ fancy: true,
31
+
27
32
  accessKeyId: '***',
28
33
  accessKeySecret: '***',
29
34
  bucket: '***',
30
35
  region: '***',
31
36
  uploadDir: `H5/zz/test`,
32
37
  skip: ['**/index.html'],
38
+
39
+ // 默认 true:有上传失败时抛错并让构建失败
40
+ failOnError: true,
41
+
33
42
  // 修改打包后的资源路径
34
43
  configBase: `https://oss.eventnet.cn/H5/zz/test/`,
35
44
  }),
36
45
  ],
37
46
  }
38
47
  ```
48
+
49
+ ## 说明
50
+
51
+ - `open` 默认 `true`,建议通过环境变量控制开关(例如 `DEPLOY_OSS=1` 时再上传)。
52
+ - `fancy` 默认 `true`,TTY 终端下会显示实时动效进度(速度、预计剩余、并发、当前文件)。
53
+ - `failOnError` 默认 `true`,上传有失败会抛错,适合 CI 场景保证发布质量。
package/dist/index.d.mts CHANGED
@@ -14,9 +14,12 @@ interface vitePluginDeployOssOption extends Omit<oss.Options, 'accessKeyId' | 'a
14
14
  autoDelete?: boolean;
15
15
  skip?: string | string[];
16
16
  open?: boolean;
17
+ fancy?: boolean;
17
18
  noCache?: boolean;
19
+ failOnError?: boolean;
18
20
  concurrency?: number;
19
21
  retryTimes?: number;
22
+ multipartThreshold?: number;
20
23
  }
21
24
  declare function vitePluginDeployOss(option: vitePluginDeployOssOption): Plugin;
22
25
 
package/dist/index.d.ts CHANGED
@@ -14,9 +14,12 @@ interface vitePluginDeployOssOption extends Omit<oss.Options, 'accessKeyId' | 'a
14
14
  autoDelete?: boolean;
15
15
  skip?: string | string[];
16
16
  open?: boolean;
17
+ fancy?: boolean;
17
18
  noCache?: boolean;
19
+ failOnError?: boolean;
18
20
  concurrency?: number;
19
21
  retryTimes?: number;
22
+ multipartThreshold?: number;
20
23
  }
21
24
  declare function vitePluginDeployOss(option: vitePluginDeployOssOption): Plugin;
22
25
 
package/dist/index.js CHANGED
@@ -37,10 +37,50 @@ var import_ali_oss = __toESM(require("ali-oss"));
37
37
  var import_chalk = __toESM(require("chalk"));
38
38
  var import_delete_empty = __toESM(require("delete-empty"));
39
39
  var import_glob = require("glob");
40
- var import_node_fs = require("fs");
40
+ var import_promises = require("fs/promises");
41
41
  var import_node_path = require("path");
42
42
  var import_ora = __toESM(require("ora"));
43
43
  var import_vite = require("vite");
44
+ var normalizeObjectKey = (targetDir, relativeFilePath) => (0, import_vite.normalizePath)(`${targetDir}/${relativeFilePath}`).replace(/\/{2,}/g, "/").replace(/^\/+/, "");
45
+ var formatBytes = (bytes) => {
46
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
47
+ const units = ["B", "KB", "MB", "GB", "TB"];
48
+ let value = bytes;
49
+ let unitIndex = 0;
50
+ while (value >= 1024 && unitIndex < units.length - 1) {
51
+ value /= 1024;
52
+ unitIndex++;
53
+ }
54
+ const digits = value >= 100 || unitIndex === 0 ? 0 : 1;
55
+ return `${value.toFixed(digits)} ${units[unitIndex]}`;
56
+ };
57
+ var formatDuration = (seconds) => {
58
+ if (!Number.isFinite(seconds) || seconds < 0) return "--";
59
+ const rounded = Math.round(seconds);
60
+ const mins = Math.floor(rounded / 60);
61
+ const secs = rounded % 60;
62
+ if (mins === 0) return `${secs}s`;
63
+ return `${mins}m${String(secs).padStart(2, "0")}s`;
64
+ };
65
+ var trimMiddle = (text, maxLength) => {
66
+ if (text.length <= maxLength) return text;
67
+ if (maxLength <= 10) return text.slice(0, maxLength);
68
+ const leftLength = Math.floor((maxLength - 3) / 2);
69
+ const rightLength = maxLength - 3 - leftLength;
70
+ return `${text.slice(0, leftLength)}...${text.slice(-rightLength)}`;
71
+ };
72
+ var buildCapsuleBar = (ratio, width = 30) => {
73
+ const safeRatio = Math.max(0, Math.min(1, ratio));
74
+ if (width <= 0) return "";
75
+ if (safeRatio >= 1) {
76
+ return import_chalk.default.green("\u2588".repeat(width));
77
+ }
78
+ const pointerIndex = Math.min(width - 1, Math.floor(width * safeRatio));
79
+ const done = pointerIndex > 0 ? import_chalk.default.green("\u2588".repeat(pointerIndex)) : "";
80
+ const pointer = import_chalk.default.cyanBright("\u25B8");
81
+ const pending = pointerIndex < width - 1 ? import_chalk.default.gray("\u2591".repeat(width - pointerIndex - 1)) : "";
82
+ return `${done}${pointer}${pending}`;
83
+ };
44
84
  function vitePluginDeployOss(option) {
45
85
  const {
46
86
  accessKeyId,
@@ -55,17 +95,23 @@ function vitePluginDeployOss(option) {
55
95
  autoDelete = false,
56
96
  alias,
57
97
  open = true,
98
+ fancy = true,
58
99
  noCache = false,
100
+ failOnError = true,
59
101
  concurrency = 5,
60
102
  retryTimes = 3,
103
+ multipartThreshold = 10 * 1024 * 1024,
61
104
  ...props
62
105
  } = option || {};
63
106
  let buildFailed = false;
64
107
  let upload = false;
65
- let outDir = "";
66
- const maxListeners = Math.max(20, concurrency * 3);
67
- process.stdout?.setMaxListeners?.(maxListeners);
68
- process.stderr?.setMaxListeners?.(maxListeners);
108
+ let outDir = (0, import_vite.normalizePath)((0, import_node_path.resolve)("dist"));
109
+ let resolvedConfig = null;
110
+ const useInteractiveOutput = fancy && Boolean(process.stdout?.isTTY) && Boolean(process.stderr?.isTTY) && !process.env.CI;
111
+ const clearScreen = () => {
112
+ if (!useInteractiveOutput) return;
113
+ process.stdout.write("\x1B[2J\x1B[0f");
114
+ };
69
115
  const validateOptions = () => {
70
116
  const errors = [];
71
117
  if (!accessKeyId) errors.push("accessKeyId is required");
@@ -73,79 +119,185 @@ function vitePluginDeployOss(option) {
73
119
  if (!bucket) errors.push("bucket is required");
74
120
  if (!region) errors.push("region is required");
75
121
  if (!uploadDir) errors.push("uploadDir is required");
122
+ if (!Number.isInteger(retryTimes) || retryTimes < 1) errors.push("retryTimes must be >= 1");
123
+ if (!Number.isInteger(concurrency) || concurrency < 1) errors.push("concurrency must be >= 1");
124
+ if (!Number.isFinite(multipartThreshold) || multipartThreshold <= 0)
125
+ errors.push("multipartThreshold must be > 0");
76
126
  return errors;
77
127
  };
78
- const uploadFileWithRetry = async (client, name, filePath, maxRetries = retryTimes) => {
128
+ const uploadFileWithRetry = async (client, task, silentLogs, maxRetries = retryTimes) => {
129
+ const shouldUseMultipart = task.size >= multipartThreshold;
130
+ const headers = {
131
+ "x-oss-storage-class": "Standard",
132
+ "x-oss-object-acl": "default",
133
+ "Cache-Control": noCache ? "no-cache" : "public, max-age=86400, immutable",
134
+ "x-oss-forbid-overwrite": overwrite ? "false" : "true"
135
+ };
79
136
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
80
137
  try {
81
- const result = await client.put(name, filePath, {
138
+ const result = shouldUseMultipart ? await client.multipartUpload(task.name, task.filePath, {
82
139
  timeout: 6e5,
83
- headers: {
84
- "x-oss-storage-class": "Standard",
85
- "x-oss-object-acl": "default",
86
- "Cache-Control": noCache ? "no-cache" : "public, max-age=86400, immutable",
87
- ...overwrite && {
88
- "x-oss-forbid-overwrite": "false"
89
- }
90
- }
140
+ partSize: 1024 * 1024,
141
+ parallel: Math.max(1, Math.min(concurrency, 4)),
142
+ headers
143
+ }) : await client.put(task.name, task.filePath, {
144
+ timeout: 6e5,
145
+ headers
91
146
  });
92
147
  if (result.res.status === 200) {
93
- const url = alias ? alias + name : result.url;
94
148
  if (autoDelete) {
95
149
  try {
96
- (0, import_node_fs.unlinkSync)(filePath);
150
+ await (0, import_promises.unlink)(task.filePath);
97
151
  } catch (error) {
98
- console.warn(`${import_chalk.default.yellow("\u26A0")} \u5220\u9664\u672C\u5730\u6587\u4EF6\u5931\u8D25: ${filePath}`);
152
+ console.warn(`${import_chalk.default.yellow("\u26A0")} \u5220\u9664\u672C\u5730\u6587\u4EF6\u5931\u8D25: ${task.filePath}`);
99
153
  }
100
154
  }
101
- return { success: true, file: filePath };
155
+ return { success: true, file: task.filePath, name: task.name, size: task.size, retries: attempt - 1 };
102
156
  } else {
103
157
  throw new Error(`Upload failed with status: ${result.res.status}`);
104
158
  }
105
159
  } catch (error) {
106
160
  if (attempt === maxRetries) {
107
- console.log(`${import_chalk.default.red("\u2717")} ${filePath} => ${error instanceof Error ? error.message : String(error)}`);
108
- return { success: false, file: filePath, error };
161
+ if (!silentLogs) {
162
+ console.log(
163
+ `${import_chalk.default.red("\u2717")} ${task.filePath} => ${error instanceof Error ? error.message : String(error)}`
164
+ );
165
+ }
166
+ return {
167
+ success: false,
168
+ file: task.filePath,
169
+ name: task.name,
170
+ size: task.size,
171
+ retries: attempt - 1,
172
+ error
173
+ };
109
174
  } else {
110
- console.log(`${import_chalk.default.yellow("\u26A0")} ${filePath} \u4E0A\u4F20\u5931\u8D25\uFF0C\u6B63\u5728\u91CD\u8BD5 (${attempt}/${maxRetries})...`);
175
+ if (!silentLogs) {
176
+ console.log(`${import_chalk.default.yellow("\u26A0")} ${task.filePath} \u4E0A\u4F20\u5931\u8D25\uFF0C\u6B63\u5728\u91CD\u8BD5 (${attempt}/${maxRetries})...`);
177
+ }
111
178
  await new Promise((resolve2) => setTimeout(resolve2, 1e3 * attempt));
112
179
  }
113
180
  }
114
181
  }
115
- return { success: false, file: filePath, error: new Error("Max retries exceeded") };
182
+ return {
183
+ success: false,
184
+ file: task.filePath,
185
+ name: task.name,
186
+ size: task.size,
187
+ retries: maxRetries,
188
+ error: new Error("Max retries exceeded")
189
+ };
116
190
  };
117
- const uploadFilesInBatches = async (client, files, batchSize = concurrency) => {
191
+ const uploadFilesInBatches = async (client, files, windowSize = concurrency) => {
118
192
  const results = [];
119
193
  const totalFiles = files.length;
194
+ const tasks = [];
120
195
  let completed = 0;
121
- const spinner = (0, import_ora.default)("\u51C6\u5907\u4E0A\u4F20...").start();
122
- const updateSpinner = (currentFile) => {
123
- const percentage = Math.round(completed / totalFiles * 100);
124
- const width2 = 30;
125
- const filled = Math.round(width2 * completed / totalFiles);
126
- const empty = width2 - filled;
127
- const bar2 = import_chalk.default.green("\u2588".repeat(filled)) + import_chalk.default.gray("\u2591".repeat(empty));
128
- spinner.text = `\u6B63\u5728\u4E0A\u4F20: ${import_chalk.default.cyan(currentFile)}
129
- ${bar2} ${percentage}% (${completed}/${totalFiles})`;
196
+ let failed = 0;
197
+ let uploadedBytes = 0;
198
+ let retries = 0;
199
+ const taskCandidates = await Promise.all(
200
+ files.map(async (relativeFilePath) => {
201
+ const filePath = (0, import_vite.normalizePath)((0, import_node_path.resolve)(outDir, relativeFilePath));
202
+ const name = normalizeObjectKey(uploadDir, relativeFilePath);
203
+ try {
204
+ const fileStats = await (0, import_promises.stat)(filePath);
205
+ return { task: { filePath, name, size: fileStats.size } };
206
+ } catch (error) {
207
+ return { task: null, error, filePath, name };
208
+ }
209
+ })
210
+ );
211
+ for (const candidate of taskCandidates) {
212
+ if (candidate.task) {
213
+ tasks.push(candidate.task);
214
+ } else {
215
+ failed++;
216
+ completed++;
217
+ results.push({
218
+ success: false,
219
+ file: candidate.filePath,
220
+ name: candidate.name,
221
+ size: 0,
222
+ retries: 0,
223
+ error: candidate.error
224
+ });
225
+ }
226
+ }
227
+ const totalBytes = tasks.reduce((sum, task) => sum + task.size, 0);
228
+ const startAt = Date.now();
229
+ const activeFiles = /* @__PURE__ */ new Set();
230
+ const safeWindowSize = Math.max(1, Math.min(windowSize, tasks.length || 1));
231
+ const silentLogs = Boolean(useInteractiveOutput);
232
+ const spinner = useInteractiveOutput ? (0, import_ora.default)({ text: "\u51C6\u5907\u4E0A\u4F20...", spinner: "dots12" }).start() : null;
233
+ const reportEvery = Math.max(1, Math.ceil(totalFiles / 10));
234
+ let lastReportedCompleted = -1;
235
+ const updateProgress = () => {
236
+ const progressRatio = totalFiles > 0 ? completed / totalFiles : 1;
237
+ const percentage = Math.round(progressRatio * 100);
238
+ const elapsedSeconds = (Date.now() - startAt) / 1e3;
239
+ const speed = elapsedSeconds > 0 ? uploadedBytes / elapsedSeconds : 0;
240
+ const etaSeconds = speed > 0 ? Math.max(0, (totalBytes - uploadedBytes) / speed) : 0;
241
+ const activeList = Array.from(activeFiles);
242
+ const currentFile = activeList.length > 0 ? trimMiddle(activeList[activeList.length - 1], 86) : "-";
243
+ if (!spinner) {
244
+ if (completed === lastReportedCompleted) return;
245
+ if (completed === totalFiles || completed % reportEvery === 0) {
246
+ console.log(
247
+ `${import_chalk.default.gray("\u8FDB\u5EA6:")} ${completed}/${totalFiles} (${percentage}%) | ${import_chalk.default.gray("\u6570\u636E:")} ${formatBytes(uploadedBytes)}/${formatBytes(totalBytes)} | ${import_chalk.default.gray("\u901F\u5EA6:")} ${formatBytes(speed)}/s`
248
+ );
249
+ lastReportedCompleted = completed;
250
+ }
251
+ return;
252
+ }
253
+ const bar = buildCapsuleBar(progressRatio);
254
+ const warnLine = retries > 0 || failed > 0 ? `
255
+ ${import_chalk.default.yellow("\u91CD\u8BD5")}: ${retries} ${import_chalk.default.yellow("\u5931\u8D25")}: ${failed}` : "";
256
+ spinner.text = [
257
+ `${import_chalk.default.cyan("\u6B63\u5728\u4E0A\u4F20:")} ${import_chalk.default.white(currentFile)}`,
258
+ `${bar} ${import_chalk.default.bold(`${percentage}%`)} ${import_chalk.default.gray(`(${completed}/${totalFiles})`)} ${import_chalk.default.gray("|")} ${import_chalk.default.blue(formatBytes(uploadedBytes))}/${import_chalk.default.blue(formatBytes(totalBytes))} ${import_chalk.default.gray("|")} ${import_chalk.default.magenta(`${formatBytes(speed)}/s`)} ${import_chalk.default.gray("|")} \u9884\u8BA1 ${import_chalk.default.yellow(formatDuration(etaSeconds))}`
259
+ ].join("\n");
260
+ spinner.text += warnLine;
130
261
  };
131
- for (let i = 0; i < files.length; i += batchSize) {
132
- const batch = files.slice(i, i + batchSize);
133
- const batchPromises = batch.map(async (file) => {
134
- const filePath = (0, import_vite.normalizePath)(file);
135
- const name = filePath.replace(outDir, uploadDir).replace(/\/\//g, "/");
136
- updateSpinner(name);
137
- const result = await uploadFileWithRetry(client, name, filePath);
262
+ const refreshTimer = spinner ? setInterval(updateProgress, 120) : null;
263
+ let currentIndex = 0;
264
+ const worker = async () => {
265
+ while (true) {
266
+ const index = currentIndex++;
267
+ if (index >= tasks.length) return;
268
+ const task = tasks[index];
269
+ activeFiles.add(task.name);
270
+ updateProgress();
271
+ const result = await uploadFileWithRetry(client, task, silentLogs);
138
272
  completed++;
139
- updateSpinner(name);
140
- return result;
141
- });
142
- const batchResults = await Promise.all(batchPromises);
143
- results.push(...batchResults);
273
+ retries += result.retries;
274
+ if (result.success) {
275
+ uploadedBytes += result.size;
276
+ } else {
277
+ failed++;
278
+ }
279
+ results.push(result);
280
+ activeFiles.delete(task.name);
281
+ updateProgress();
282
+ }
283
+ };
284
+ updateProgress();
285
+ try {
286
+ await Promise.all(Array.from({ length: safeWindowSize }, () => worker()));
287
+ } finally {
288
+ if (refreshTimer) clearInterval(refreshTimer);
289
+ }
290
+ if (spinner) {
291
+ const elapsedSeconds = (Date.now() - startAt) / 1e3;
292
+ const successCount = results.filter((item) => item.success).length;
293
+ const speed = elapsedSeconds > 0 ? uploadedBytes / elapsedSeconds : 0;
294
+ spinner.succeed(
295
+ `${import_chalk.default.green("\u4E0A\u4F20\u6210\u529F")} ${successCount} \u4E2A\u6587\u4EF6\u3002
296
+ ${buildCapsuleBar(1)} 100% (${totalFiles}/${totalFiles}) ${import_chalk.default.gray("|")} \u901F\u5EA6 ${import_chalk.default.magenta(`${formatBytes(speed)}/s`)} ${import_chalk.default.gray("|")} \u8017\u65F6 ${import_chalk.default.yellow(formatDuration(elapsedSeconds))}`
297
+ );
298
+ } else {
299
+ console.log(`${import_chalk.default.green("\u2714")} \u6240\u6709\u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210 (${totalFiles}/${totalFiles})`);
144
300
  }
145
- const width = 30;
146
- const bar = import_chalk.default.green("\u2588".repeat(width));
147
- spinner.succeed(`\u6240\u6709\u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210!
148
- ${bar} 100% (${totalFiles}/${totalFiles})`);
149
301
  return results;
150
302
  };
151
303
  return {
@@ -157,7 +309,7 @@ ${bar} 100% (${totalFiles}/${totalFiles})`);
157
309
  },
158
310
  config(config) {
159
311
  if (!open || buildFailed) return;
160
- process.stdout.write("\x1B[2J\x1B[0f");
312
+ clearScreen();
161
313
  const validationErrors = validateOptions();
162
314
  if (validationErrors.length > 0) {
163
315
  console.log(`${import_chalk.default.red("\u2717 \u914D\u7F6E\u9519\u8BEF:")}
@@ -166,25 +318,29 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
166
318
  }
167
319
  upload = true;
168
320
  config.base = configBase || config.base;
169
- outDir = config.build?.outDir || "dist";
170
321
  return config;
171
322
  },
323
+ configResolved(config) {
324
+ resolvedConfig = config;
325
+ outDir = (0, import_vite.normalizePath)((0, import_node_path.resolve)(config.root, config.build.outDir));
326
+ },
172
327
  closeBundle: {
173
328
  sequential: true,
174
329
  order: "post",
175
330
  async handler() {
176
- if (!open || !upload || buildFailed) return;
331
+ if (!open || !upload || buildFailed || !resolvedConfig) return;
177
332
  const startTime = Date.now();
178
333
  const client = new import_ali_oss.default({ region, accessKeyId, accessKeySecret, secure, bucket, ...props });
179
- const files = (0, import_glob.globSync)(outDir + "/**/*", {
334
+ const files = (0, import_glob.globSync)("**/*", {
335
+ cwd: outDir,
180
336
  nodir: true,
181
337
  ignore: Array.isArray(skip) ? skip : [skip]
182
- });
338
+ }).map((file) => (0, import_vite.normalizePath)(file));
183
339
  if (files.length === 0) {
184
340
  console.log(`${import_chalk.default.yellow("\u26A0 \u6CA1\u6709\u627E\u5230\u9700\u8981\u4E0A\u4F20\u7684\u6587\u4EF6")}`);
185
341
  return;
186
342
  }
187
- process.stdout.write("\x1B[2J\x1B[0f");
343
+ clearScreen();
188
344
  console.log(import_chalk.default.cyan(`
189
345
  \u{1F680} OSS \u90E8\u7F72\u5F00\u59CB
190
346
  `));
@@ -192,14 +348,19 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
192
348
  console.log(`${import_chalk.default.gray("Region:")} ${import_chalk.default.green(region)}`);
193
349
  console.log(`${import_chalk.default.gray("Source:")} ${import_chalk.default.yellow(outDir)}`);
194
350
  console.log(`${import_chalk.default.gray("Target:")} ${import_chalk.default.yellow(uploadDir)}`);
351
+ if (alias) console.log(`${import_chalk.default.gray("Alias:")} ${import_chalk.default.green(alias)}`);
195
352
  console.log(`${import_chalk.default.gray("Files:")} ${import_chalk.default.blue(files.length)}
196
353
  `);
197
354
  try {
198
355
  const results = await uploadFilesInBatches(client, files, concurrency);
199
356
  const successCount = results.filter((r) => r.success).length;
200
357
  const failedCount = results.length - successCount;
201
- const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
202
- process.stdout.write("\x1B[2J\x1B[0f");
358
+ const durationSeconds = (Date.now() - startTime) / 1e3;
359
+ const duration = durationSeconds.toFixed(2);
360
+ const uploadedBytes = results.reduce((sum, result) => result.success ? sum + result.size : sum, 0);
361
+ const retryCount = results.reduce((sum, result) => sum + result.retries, 0);
362
+ const avgSpeed = durationSeconds > 0 ? uploadedBytes / durationSeconds : 0;
363
+ clearScreen();
203
364
  console.log("\n" + import_chalk.default.gray("\u2500".repeat(40)) + "\n");
204
365
  if (failedCount === 0) {
205
366
  console.log(`${import_chalk.default.green("\u{1F389} \u90E8\u7F72\u6210\u529F!")}`);
@@ -212,17 +373,40 @@ ${import_chalk.default.gray("\u7EDF\u8BA1:")}`);
212
373
  if (failedCount > 0) {
213
374
  console.log(` ${import_chalk.default.red("\u2717")} \u5931\u8D25: ${import_chalk.default.bold(failedCount)}`);
214
375
  }
376
+ console.log(` ${import_chalk.default.cyan("\u21C4")} \u91CD\u8BD5: ${import_chalk.default.bold(retryCount)}`);
377
+ console.log(` ${import_chalk.default.blue("\u{1F4E6}")} \u6570\u636E: ${import_chalk.default.bold(formatBytes(uploadedBytes))}`);
378
+ console.log(` ${import_chalk.default.magenta("\u26A1")} \u5E73\u5747\u901F\u5EA6: ${import_chalk.default.bold(`${formatBytes(avgSpeed)}/s`)}`);
215
379
  console.log(` ${import_chalk.default.blue("\u23F1")} \u8017\u65F6: ${import_chalk.default.bold(duration)}s`);
216
380
  console.log("");
381
+ if (failedCount > 0) {
382
+ const failedItems = results.filter((result) => !result.success);
383
+ const previewCount = Math.min(5, failedItems.length);
384
+ console.log(import_chalk.default.red("\u5931\u8D25\u660E\u7EC6:"));
385
+ for (let i = 0; i < previewCount; i++) {
386
+ const item = failedItems[i];
387
+ const reason = item.error?.message || "unknown error";
388
+ console.log(` ${import_chalk.default.red("\u2022")} ${item.name} => ${reason}`);
389
+ }
390
+ if (failedItems.length > previewCount) {
391
+ console.log(import_chalk.default.gray(` ... \u8FD8\u6709 ${failedItems.length - previewCount} \u4E2A\u5931\u8D25\u6587\u4EF6`));
392
+ }
393
+ console.log("");
394
+ }
217
395
  try {
218
- (0, import_delete_empty.default)((0, import_node_path.resolve)(outDir));
396
+ await (0, import_delete_empty.default)((0, import_node_path.resolve)(outDir));
219
397
  } catch (error) {
220
398
  console.warn(`${import_chalk.default.yellow("\u26A0 \u6E05\u7406\u7A7A\u76EE\u5F55\u5931\u8D25:")} ${error}`);
221
399
  }
400
+ if (failedCount > 0 && failOnError) {
401
+ throw new Error(`Failed to upload ${failedCount} of ${results.length} files`);
402
+ }
222
403
  } catch (error) {
223
404
  console.log(`
224
405
  ${import_chalk.default.red("\u274C \u4E0A\u4F20\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF:")} ${error}
225
406
  `);
407
+ if (failOnError) {
408
+ throw error instanceof Error ? error : new Error(String(error));
409
+ }
226
410
  }
227
411
  }
228
412
  }
package/dist/index.mjs CHANGED
@@ -3,10 +3,50 @@ import oss from "ali-oss";
3
3
  import chalk from "chalk";
4
4
  import deleteEmpty from "delete-empty";
5
5
  import { globSync } from "glob";
6
- import { unlinkSync } from "fs";
6
+ import { stat, unlink } from "fs/promises";
7
7
  import { resolve } from "path";
8
8
  import ora from "ora";
9
9
  import { normalizePath } from "vite";
10
+ var normalizeObjectKey = (targetDir, relativeFilePath) => normalizePath(`${targetDir}/${relativeFilePath}`).replace(/\/{2,}/g, "/").replace(/^\/+/, "");
11
+ var formatBytes = (bytes) => {
12
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
13
+ const units = ["B", "KB", "MB", "GB", "TB"];
14
+ let value = bytes;
15
+ let unitIndex = 0;
16
+ while (value >= 1024 && unitIndex < units.length - 1) {
17
+ value /= 1024;
18
+ unitIndex++;
19
+ }
20
+ const digits = value >= 100 || unitIndex === 0 ? 0 : 1;
21
+ return `${value.toFixed(digits)} ${units[unitIndex]}`;
22
+ };
23
+ var formatDuration = (seconds) => {
24
+ if (!Number.isFinite(seconds) || seconds < 0) return "--";
25
+ const rounded = Math.round(seconds);
26
+ const mins = Math.floor(rounded / 60);
27
+ const secs = rounded % 60;
28
+ if (mins === 0) return `${secs}s`;
29
+ return `${mins}m${String(secs).padStart(2, "0")}s`;
30
+ };
31
+ var trimMiddle = (text, maxLength) => {
32
+ if (text.length <= maxLength) return text;
33
+ if (maxLength <= 10) return text.slice(0, maxLength);
34
+ const leftLength = Math.floor((maxLength - 3) / 2);
35
+ const rightLength = maxLength - 3 - leftLength;
36
+ return `${text.slice(0, leftLength)}...${text.slice(-rightLength)}`;
37
+ };
38
+ var buildCapsuleBar = (ratio, width = 30) => {
39
+ const safeRatio = Math.max(0, Math.min(1, ratio));
40
+ if (width <= 0) return "";
41
+ if (safeRatio >= 1) {
42
+ return chalk.green("\u2588".repeat(width));
43
+ }
44
+ const pointerIndex = Math.min(width - 1, Math.floor(width * safeRatio));
45
+ const done = pointerIndex > 0 ? chalk.green("\u2588".repeat(pointerIndex)) : "";
46
+ const pointer = chalk.cyanBright("\u25B8");
47
+ const pending = pointerIndex < width - 1 ? chalk.gray("\u2591".repeat(width - pointerIndex - 1)) : "";
48
+ return `${done}${pointer}${pending}`;
49
+ };
10
50
  function vitePluginDeployOss(option) {
11
51
  const {
12
52
  accessKeyId,
@@ -21,17 +61,23 @@ function vitePluginDeployOss(option) {
21
61
  autoDelete = false,
22
62
  alias,
23
63
  open = true,
64
+ fancy = true,
24
65
  noCache = false,
66
+ failOnError = true,
25
67
  concurrency = 5,
26
68
  retryTimes = 3,
69
+ multipartThreshold = 10 * 1024 * 1024,
27
70
  ...props
28
71
  } = option || {};
29
72
  let buildFailed = false;
30
73
  let upload = false;
31
- let outDir = "";
32
- const maxListeners = Math.max(20, concurrency * 3);
33
- process.stdout?.setMaxListeners?.(maxListeners);
34
- process.stderr?.setMaxListeners?.(maxListeners);
74
+ let outDir = normalizePath(resolve("dist"));
75
+ let resolvedConfig = null;
76
+ const useInteractiveOutput = fancy && Boolean(process.stdout?.isTTY) && Boolean(process.stderr?.isTTY) && !process.env.CI;
77
+ const clearScreen = () => {
78
+ if (!useInteractiveOutput) return;
79
+ process.stdout.write("\x1B[2J\x1B[0f");
80
+ };
35
81
  const validateOptions = () => {
36
82
  const errors = [];
37
83
  if (!accessKeyId) errors.push("accessKeyId is required");
@@ -39,79 +85,185 @@ function vitePluginDeployOss(option) {
39
85
  if (!bucket) errors.push("bucket is required");
40
86
  if (!region) errors.push("region is required");
41
87
  if (!uploadDir) errors.push("uploadDir is required");
88
+ if (!Number.isInteger(retryTimes) || retryTimes < 1) errors.push("retryTimes must be >= 1");
89
+ if (!Number.isInteger(concurrency) || concurrency < 1) errors.push("concurrency must be >= 1");
90
+ if (!Number.isFinite(multipartThreshold) || multipartThreshold <= 0)
91
+ errors.push("multipartThreshold must be > 0");
42
92
  return errors;
43
93
  };
44
- const uploadFileWithRetry = async (client, name, filePath, maxRetries = retryTimes) => {
94
+ const uploadFileWithRetry = async (client, task, silentLogs, maxRetries = retryTimes) => {
95
+ const shouldUseMultipart = task.size >= multipartThreshold;
96
+ const headers = {
97
+ "x-oss-storage-class": "Standard",
98
+ "x-oss-object-acl": "default",
99
+ "Cache-Control": noCache ? "no-cache" : "public, max-age=86400, immutable",
100
+ "x-oss-forbid-overwrite": overwrite ? "false" : "true"
101
+ };
45
102
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
46
103
  try {
47
- const result = await client.put(name, filePath, {
104
+ const result = shouldUseMultipart ? await client.multipartUpload(task.name, task.filePath, {
48
105
  timeout: 6e5,
49
- headers: {
50
- "x-oss-storage-class": "Standard",
51
- "x-oss-object-acl": "default",
52
- "Cache-Control": noCache ? "no-cache" : "public, max-age=86400, immutable",
53
- ...overwrite && {
54
- "x-oss-forbid-overwrite": "false"
55
- }
56
- }
106
+ partSize: 1024 * 1024,
107
+ parallel: Math.max(1, Math.min(concurrency, 4)),
108
+ headers
109
+ }) : await client.put(task.name, task.filePath, {
110
+ timeout: 6e5,
111
+ headers
57
112
  });
58
113
  if (result.res.status === 200) {
59
- const url = alias ? alias + name : result.url;
60
114
  if (autoDelete) {
61
115
  try {
62
- unlinkSync(filePath);
116
+ await unlink(task.filePath);
63
117
  } catch (error) {
64
- console.warn(`${chalk.yellow("\u26A0")} \u5220\u9664\u672C\u5730\u6587\u4EF6\u5931\u8D25: ${filePath}`);
118
+ console.warn(`${chalk.yellow("\u26A0")} \u5220\u9664\u672C\u5730\u6587\u4EF6\u5931\u8D25: ${task.filePath}`);
65
119
  }
66
120
  }
67
- return { success: true, file: filePath };
121
+ return { success: true, file: task.filePath, name: task.name, size: task.size, retries: attempt - 1 };
68
122
  } else {
69
123
  throw new Error(`Upload failed with status: ${result.res.status}`);
70
124
  }
71
125
  } catch (error) {
72
126
  if (attempt === maxRetries) {
73
- console.log(`${chalk.red("\u2717")} ${filePath} => ${error instanceof Error ? error.message : String(error)}`);
74
- return { success: false, file: filePath, error };
127
+ if (!silentLogs) {
128
+ console.log(
129
+ `${chalk.red("\u2717")} ${task.filePath} => ${error instanceof Error ? error.message : String(error)}`
130
+ );
131
+ }
132
+ return {
133
+ success: false,
134
+ file: task.filePath,
135
+ name: task.name,
136
+ size: task.size,
137
+ retries: attempt - 1,
138
+ error
139
+ };
75
140
  } else {
76
- console.log(`${chalk.yellow("\u26A0")} ${filePath} \u4E0A\u4F20\u5931\u8D25\uFF0C\u6B63\u5728\u91CD\u8BD5 (${attempt}/${maxRetries})...`);
141
+ if (!silentLogs) {
142
+ console.log(`${chalk.yellow("\u26A0")} ${task.filePath} \u4E0A\u4F20\u5931\u8D25\uFF0C\u6B63\u5728\u91CD\u8BD5 (${attempt}/${maxRetries})...`);
143
+ }
77
144
  await new Promise((resolve2) => setTimeout(resolve2, 1e3 * attempt));
78
145
  }
79
146
  }
80
147
  }
81
- return { success: false, file: filePath, error: new Error("Max retries exceeded") };
148
+ return {
149
+ success: false,
150
+ file: task.filePath,
151
+ name: task.name,
152
+ size: task.size,
153
+ retries: maxRetries,
154
+ error: new Error("Max retries exceeded")
155
+ };
82
156
  };
83
- const uploadFilesInBatches = async (client, files, batchSize = concurrency) => {
157
+ const uploadFilesInBatches = async (client, files, windowSize = concurrency) => {
84
158
  const results = [];
85
159
  const totalFiles = files.length;
160
+ const tasks = [];
86
161
  let completed = 0;
87
- const spinner = ora("\u51C6\u5907\u4E0A\u4F20...").start();
88
- const updateSpinner = (currentFile) => {
89
- const percentage = Math.round(completed / totalFiles * 100);
90
- const width2 = 30;
91
- const filled = Math.round(width2 * completed / totalFiles);
92
- const empty = width2 - filled;
93
- const bar2 = chalk.green("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
94
- spinner.text = `\u6B63\u5728\u4E0A\u4F20: ${chalk.cyan(currentFile)}
95
- ${bar2} ${percentage}% (${completed}/${totalFiles})`;
162
+ let failed = 0;
163
+ let uploadedBytes = 0;
164
+ let retries = 0;
165
+ const taskCandidates = await Promise.all(
166
+ files.map(async (relativeFilePath) => {
167
+ const filePath = normalizePath(resolve(outDir, relativeFilePath));
168
+ const name = normalizeObjectKey(uploadDir, relativeFilePath);
169
+ try {
170
+ const fileStats = await stat(filePath);
171
+ return { task: { filePath, name, size: fileStats.size } };
172
+ } catch (error) {
173
+ return { task: null, error, filePath, name };
174
+ }
175
+ })
176
+ );
177
+ for (const candidate of taskCandidates) {
178
+ if (candidate.task) {
179
+ tasks.push(candidate.task);
180
+ } else {
181
+ failed++;
182
+ completed++;
183
+ results.push({
184
+ success: false,
185
+ file: candidate.filePath,
186
+ name: candidate.name,
187
+ size: 0,
188
+ retries: 0,
189
+ error: candidate.error
190
+ });
191
+ }
192
+ }
193
+ const totalBytes = tasks.reduce((sum, task) => sum + task.size, 0);
194
+ const startAt = Date.now();
195
+ const activeFiles = /* @__PURE__ */ new Set();
196
+ const safeWindowSize = Math.max(1, Math.min(windowSize, tasks.length || 1));
197
+ const silentLogs = Boolean(useInteractiveOutput);
198
+ const spinner = useInteractiveOutput ? ora({ text: "\u51C6\u5907\u4E0A\u4F20...", spinner: "dots12" }).start() : null;
199
+ const reportEvery = Math.max(1, Math.ceil(totalFiles / 10));
200
+ let lastReportedCompleted = -1;
201
+ const updateProgress = () => {
202
+ const progressRatio = totalFiles > 0 ? completed / totalFiles : 1;
203
+ const percentage = Math.round(progressRatio * 100);
204
+ const elapsedSeconds = (Date.now() - startAt) / 1e3;
205
+ const speed = elapsedSeconds > 0 ? uploadedBytes / elapsedSeconds : 0;
206
+ const etaSeconds = speed > 0 ? Math.max(0, (totalBytes - uploadedBytes) / speed) : 0;
207
+ const activeList = Array.from(activeFiles);
208
+ const currentFile = activeList.length > 0 ? trimMiddle(activeList[activeList.length - 1], 86) : "-";
209
+ if (!spinner) {
210
+ if (completed === lastReportedCompleted) return;
211
+ if (completed === totalFiles || completed % reportEvery === 0) {
212
+ console.log(
213
+ `${chalk.gray("\u8FDB\u5EA6:")} ${completed}/${totalFiles} (${percentage}%) | ${chalk.gray("\u6570\u636E:")} ${formatBytes(uploadedBytes)}/${formatBytes(totalBytes)} | ${chalk.gray("\u901F\u5EA6:")} ${formatBytes(speed)}/s`
214
+ );
215
+ lastReportedCompleted = completed;
216
+ }
217
+ return;
218
+ }
219
+ const bar = buildCapsuleBar(progressRatio);
220
+ const warnLine = retries > 0 || failed > 0 ? `
221
+ ${chalk.yellow("\u91CD\u8BD5")}: ${retries} ${chalk.yellow("\u5931\u8D25")}: ${failed}` : "";
222
+ spinner.text = [
223
+ `${chalk.cyan("\u6B63\u5728\u4E0A\u4F20:")} ${chalk.white(currentFile)}`,
224
+ `${bar} ${chalk.bold(`${percentage}%`)} ${chalk.gray(`(${completed}/${totalFiles})`)} ${chalk.gray("|")} ${chalk.blue(formatBytes(uploadedBytes))}/${chalk.blue(formatBytes(totalBytes))} ${chalk.gray("|")} ${chalk.magenta(`${formatBytes(speed)}/s`)} ${chalk.gray("|")} \u9884\u8BA1 ${chalk.yellow(formatDuration(etaSeconds))}`
225
+ ].join("\n");
226
+ spinner.text += warnLine;
96
227
  };
97
- for (let i = 0; i < files.length; i += batchSize) {
98
- const batch = files.slice(i, i + batchSize);
99
- const batchPromises = batch.map(async (file) => {
100
- const filePath = normalizePath(file);
101
- const name = filePath.replace(outDir, uploadDir).replace(/\/\//g, "/");
102
- updateSpinner(name);
103
- const result = await uploadFileWithRetry(client, name, filePath);
228
+ const refreshTimer = spinner ? setInterval(updateProgress, 120) : null;
229
+ let currentIndex = 0;
230
+ const worker = async () => {
231
+ while (true) {
232
+ const index = currentIndex++;
233
+ if (index >= tasks.length) return;
234
+ const task = tasks[index];
235
+ activeFiles.add(task.name);
236
+ updateProgress();
237
+ const result = await uploadFileWithRetry(client, task, silentLogs);
104
238
  completed++;
105
- updateSpinner(name);
106
- return result;
107
- });
108
- const batchResults = await Promise.all(batchPromises);
109
- results.push(...batchResults);
239
+ retries += result.retries;
240
+ if (result.success) {
241
+ uploadedBytes += result.size;
242
+ } else {
243
+ failed++;
244
+ }
245
+ results.push(result);
246
+ activeFiles.delete(task.name);
247
+ updateProgress();
248
+ }
249
+ };
250
+ updateProgress();
251
+ try {
252
+ await Promise.all(Array.from({ length: safeWindowSize }, () => worker()));
253
+ } finally {
254
+ if (refreshTimer) clearInterval(refreshTimer);
255
+ }
256
+ if (spinner) {
257
+ const elapsedSeconds = (Date.now() - startAt) / 1e3;
258
+ const successCount = results.filter((item) => item.success).length;
259
+ const speed = elapsedSeconds > 0 ? uploadedBytes / elapsedSeconds : 0;
260
+ spinner.succeed(
261
+ `${chalk.green("\u4E0A\u4F20\u6210\u529F")} ${successCount} \u4E2A\u6587\u4EF6\u3002
262
+ ${buildCapsuleBar(1)} 100% (${totalFiles}/${totalFiles}) ${chalk.gray("|")} \u901F\u5EA6 ${chalk.magenta(`${formatBytes(speed)}/s`)} ${chalk.gray("|")} \u8017\u65F6 ${chalk.yellow(formatDuration(elapsedSeconds))}`
263
+ );
264
+ } else {
265
+ console.log(`${chalk.green("\u2714")} \u6240\u6709\u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210 (${totalFiles}/${totalFiles})`);
110
266
  }
111
- const width = 30;
112
- const bar = chalk.green("\u2588".repeat(width));
113
- spinner.succeed(`\u6240\u6709\u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210!
114
- ${bar} 100% (${totalFiles}/${totalFiles})`);
115
267
  return results;
116
268
  };
117
269
  return {
@@ -123,7 +275,7 @@ ${bar} 100% (${totalFiles}/${totalFiles})`);
123
275
  },
124
276
  config(config) {
125
277
  if (!open || buildFailed) return;
126
- process.stdout.write("\x1B[2J\x1B[0f");
278
+ clearScreen();
127
279
  const validationErrors = validateOptions();
128
280
  if (validationErrors.length > 0) {
129
281
  console.log(`${chalk.red("\u2717 \u914D\u7F6E\u9519\u8BEF:")}
@@ -132,25 +284,29 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
132
284
  }
133
285
  upload = true;
134
286
  config.base = configBase || config.base;
135
- outDir = config.build?.outDir || "dist";
136
287
  return config;
137
288
  },
289
+ configResolved(config) {
290
+ resolvedConfig = config;
291
+ outDir = normalizePath(resolve(config.root, config.build.outDir));
292
+ },
138
293
  closeBundle: {
139
294
  sequential: true,
140
295
  order: "post",
141
296
  async handler() {
142
- if (!open || !upload || buildFailed) return;
297
+ if (!open || !upload || buildFailed || !resolvedConfig) return;
143
298
  const startTime = Date.now();
144
299
  const client = new oss({ region, accessKeyId, accessKeySecret, secure, bucket, ...props });
145
- const files = globSync(outDir + "/**/*", {
300
+ const files = globSync("**/*", {
301
+ cwd: outDir,
146
302
  nodir: true,
147
303
  ignore: Array.isArray(skip) ? skip : [skip]
148
- });
304
+ }).map((file) => normalizePath(file));
149
305
  if (files.length === 0) {
150
306
  console.log(`${chalk.yellow("\u26A0 \u6CA1\u6709\u627E\u5230\u9700\u8981\u4E0A\u4F20\u7684\u6587\u4EF6")}`);
151
307
  return;
152
308
  }
153
- process.stdout.write("\x1B[2J\x1B[0f");
309
+ clearScreen();
154
310
  console.log(chalk.cyan(`
155
311
  \u{1F680} OSS \u90E8\u7F72\u5F00\u59CB
156
312
  `));
@@ -158,14 +314,19 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
158
314
  console.log(`${chalk.gray("Region:")} ${chalk.green(region)}`);
159
315
  console.log(`${chalk.gray("Source:")} ${chalk.yellow(outDir)}`);
160
316
  console.log(`${chalk.gray("Target:")} ${chalk.yellow(uploadDir)}`);
317
+ if (alias) console.log(`${chalk.gray("Alias:")} ${chalk.green(alias)}`);
161
318
  console.log(`${chalk.gray("Files:")} ${chalk.blue(files.length)}
162
319
  `);
163
320
  try {
164
321
  const results = await uploadFilesInBatches(client, files, concurrency);
165
322
  const successCount = results.filter((r) => r.success).length;
166
323
  const failedCount = results.length - successCount;
167
- const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
168
- process.stdout.write("\x1B[2J\x1B[0f");
324
+ const durationSeconds = (Date.now() - startTime) / 1e3;
325
+ const duration = durationSeconds.toFixed(2);
326
+ const uploadedBytes = results.reduce((sum, result) => result.success ? sum + result.size : sum, 0);
327
+ const retryCount = results.reduce((sum, result) => sum + result.retries, 0);
328
+ const avgSpeed = durationSeconds > 0 ? uploadedBytes / durationSeconds : 0;
329
+ clearScreen();
169
330
  console.log("\n" + chalk.gray("\u2500".repeat(40)) + "\n");
170
331
  if (failedCount === 0) {
171
332
  console.log(`${chalk.green("\u{1F389} \u90E8\u7F72\u6210\u529F!")}`);
@@ -178,17 +339,40 @@ ${chalk.gray("\u7EDF\u8BA1:")}`);
178
339
  if (failedCount > 0) {
179
340
  console.log(` ${chalk.red("\u2717")} \u5931\u8D25: ${chalk.bold(failedCount)}`);
180
341
  }
342
+ console.log(` ${chalk.cyan("\u21C4")} \u91CD\u8BD5: ${chalk.bold(retryCount)}`);
343
+ console.log(` ${chalk.blue("\u{1F4E6}")} \u6570\u636E: ${chalk.bold(formatBytes(uploadedBytes))}`);
344
+ console.log(` ${chalk.magenta("\u26A1")} \u5E73\u5747\u901F\u5EA6: ${chalk.bold(`${formatBytes(avgSpeed)}/s`)}`);
181
345
  console.log(` ${chalk.blue("\u23F1")} \u8017\u65F6: ${chalk.bold(duration)}s`);
182
346
  console.log("");
347
+ if (failedCount > 0) {
348
+ const failedItems = results.filter((result) => !result.success);
349
+ const previewCount = Math.min(5, failedItems.length);
350
+ console.log(chalk.red("\u5931\u8D25\u660E\u7EC6:"));
351
+ for (let i = 0; i < previewCount; i++) {
352
+ const item = failedItems[i];
353
+ const reason = item.error?.message || "unknown error";
354
+ console.log(` ${chalk.red("\u2022")} ${item.name} => ${reason}`);
355
+ }
356
+ if (failedItems.length > previewCount) {
357
+ console.log(chalk.gray(` ... \u8FD8\u6709 ${failedItems.length - previewCount} \u4E2A\u5931\u8D25\u6587\u4EF6`));
358
+ }
359
+ console.log("");
360
+ }
183
361
  try {
184
- deleteEmpty(resolve(outDir));
362
+ await deleteEmpty(resolve(outDir));
185
363
  } catch (error) {
186
364
  console.warn(`${chalk.yellow("\u26A0 \u6E05\u7406\u7A7A\u76EE\u5F55\u5931\u8D25:")} ${error}`);
187
365
  }
366
+ if (failedCount > 0 && failOnError) {
367
+ throw new Error(`Failed to upload ${failedCount} of ${results.length} files`);
368
+ }
188
369
  } catch (error) {
189
370
  console.log(`
190
371
  ${chalk.red("\u274C \u4E0A\u4F20\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF:")} ${error}
191
372
  `);
373
+ if (failOnError) {
374
+ throw error instanceof Error ? error : new Error(String(error));
375
+ }
192
376
  }
193
377
  }
194
378
  }
package/package.json CHANGED
@@ -1,21 +1,28 @@
1
1
  {
2
2
  "name": "vite-plugin-deploy-oss",
3
- "version": "1.2.3",
3
+ "version": "2.0.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
7
+ "type": "commonjs",
7
8
  "homepage": "https://github.com/yulin96/vite-plugin-deploy-oss",
8
9
  "repository": {
9
10
  "type": "git",
10
- "url": "https://github.com/yulin96/vite-plugin-deploy-oss"
11
+ "url": "git+https://github.com/yulin96/vite-plugin-deploy-oss.git"
11
12
  },
12
13
  "bugs": {
13
14
  "url": "https://github.com/yulin96/vite-plugin-deploy-oss/issues"
14
15
  },
15
16
  "exports": {
16
17
  ".": {
17
- "import": "./dist/index.mjs",
18
- "require": "./dist/index.js"
18
+ "import": {
19
+ "types": "./dist/index.d.mts",
20
+ "default": "./dist/index.mjs"
21
+ },
22
+ "require": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ }
19
26
  }
20
27
  },
21
28
  "keywords": [
@@ -31,25 +38,27 @@
31
38
  "license": "MIT",
32
39
  "description": "将dist目录下的文件上传到阿里云oss",
33
40
  "devDependencies": {
34
- "@types/node": "^22.15.32",
35
- "tsup": "^8.5.0",
36
- "typescript": "^5.8.3"
41
+ "@types/ali-oss": "^6.23.3",
42
+ "@types/delete-empty": "^3.0.5",
43
+ "@types/node": "^22.19.11",
44
+ "tsup": "^8.5.1",
45
+ "typescript": "^5.9.3"
37
46
  },
38
47
  "peerDependencies": {
39
48
  "vite": "^6.0.3 || ^7"
40
49
  },
41
50
  "dependencies": {
42
- "@types/ali-oss": "^6.16.11",
43
- "@types/delete-empty": "^3.0.5",
44
51
  "ali-oss": "^6.23.0",
45
- "chalk": "^5.4.1",
52
+ "chalk": "^5.6.2",
46
53
  "delete-empty": "^3.0.0",
47
- "glob": "^13.0.1",
48
- "ora": "^9.0.0"
54
+ "glob": "^13.0.6",
55
+ "ora": "^9.3.0"
49
56
  },
50
57
  "scripts": {
51
58
  "build": "tsup",
59
+ "typecheck": "tsc -p tsconfig.json",
52
60
  "pack": "pnpm run build && pnpm pack",
53
- "build:test": "cd playground && vite build"
61
+ "build:test": "cd playground && vite build",
62
+ "build:test:deploy": "cd playground && vite build --mode deploy"
54
63
  }
55
64
  }