vite-plugin-deploy-oss 3.3.0 → 3.4.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.
@@ -0,0 +1,5 @@
1
+ dist
2
+ node_modules
3
+ playground/__dist__
4
+ coverage
5
+ *.log
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/prettierrc",
3
+ "arrowParens": "always",
4
+ "bracketSameLine": false,
5
+ "jsxSingleQuote": true,
6
+ "printWidth": 120,
7
+ "quoteProps": "as-needed",
8
+ "semi": false,
9
+ "singleQuote": true,
10
+ "tabWidth": 2,
11
+ "endOfLine": "lf"
12
+ }
package/README.md CHANGED
@@ -1,10 +1,6 @@
1
1
  # vite-plugin-deploy-oss
2
2
 
3
- dist 目录上传到 OSS
4
-
5
- ## 介绍
6
-
7
- `vite-plugin-deploy-oss` 是一个 Vite 插件,它可以将你的 dist 目录上传到 OSS 上。
3
+ Vite 打包后的文件上传到阿里云 OSS
8
4
 
9
5
  ## 安装
10
6
 
@@ -12,53 +8,92 @@
12
8
  pnpm add vite-plugin-deploy-oss -D
13
9
  ```
14
10
 
15
- ## 使用
11
+ ## 快速开始
12
+
13
+ 推荐用环境变量控制上传,避免本地随手打包时误上传。
16
14
 
17
15
  ```ts
18
16
  // vite.config.ts
17
+ import { defineConfig } from 'vite'
19
18
  import vitePluginDeployOss from 'vite-plugin-deploy-oss'
20
19
 
21
- // ...existing code...
22
- export default {
23
- // ...existing code...
20
+ export default defineConfig({
24
21
  plugins: [
25
- // 在最后一个插件中使用
26
22
  vitePluginDeployOss({
27
- // 建议按环境变量开关上传,避免本地/CI误上传
28
23
  open: process.env.DEPLOY_OSS === '1',
29
- // 终端实时动效进度面板(默认 true)
30
- fancy: true,
31
24
 
32
- accessKeyId: '***',
33
- accessKeySecret: '***',
34
- bucket: '***',
35
- region: '***',
36
- uploadDir: `H5/zz/test`,
37
- skip: ['**/index.html'],
25
+ accessKeyId: process.env.OSS_ACCESS_KEY_ID || '',
26
+ accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET || '',
27
+ bucket: process.env.OSS_BUCKET || '',
28
+ region: process.env.OSS_REGION || '',
38
29
 
39
- // 默认 true:有上传失败时抛错并让构建失败
40
- failOnError: true,
30
+ uploadDir: 'H5/demo/prod',
31
+ configBase: 'https://example.com/H5/demo/prod/',
41
32
 
42
- // 生成并上传 OSS 汇总文件
43
33
  manifest: true,
44
-
45
- // 修改打包后的资源路径
46
- configBase: `https://oss.eventnet.cn/H5/zz/test/`,
34
+ failOnError: true,
47
35
  }),
48
36
  ],
49
- }
37
+ })
38
+ ```
39
+
40
+ 发布时执行:
41
+
42
+ ```bash
43
+ DEPLOY_OSS=1 pnpm build
44
+ ```
45
+
46
+ ## 常用配置
47
+
48
+ | 配置 | 默认值 | 说明 |
49
+ | ----------------- | ----------------- | ---------------------------------- |
50
+ | `open` | `true` | 是否开启上传。建议用环境变量控制。 |
51
+ | `accessKeyId` | - | OSS 访问密钥。 |
52
+ | `accessKeySecret` | - | OSS 访问密钥。 |
53
+ | `bucket` | - | OSS bucket 名称。 |
54
+ | `region` | - | OSS 区域,例如 `oss-cn-beijing`。 |
55
+ | `uploadDir` | - | 文件上传到 OSS 的目标目录。 |
56
+ | `configBase` | - | 同步修改 Vite 的资源访问路径。 |
57
+ | `skip` | `'**/index.html'` | 不上传的文件规则。 |
58
+ | `overwrite` | `true` | 是否允许覆盖远端同名文件。 |
59
+ | `autoDelete` | `false` | 上传成功后是否删除本地构建文件。 |
60
+ | `manifest` | `false` | 是否生成并上传文件清单。 |
61
+ | `failOnError` | `true` | 上传失败时是否中断构建。 |
62
+ | `debug` | `false` | 是否输出耗时信息。 |
63
+ | `fancy` | `true` | 是否显示更友好的终端进度。 |
64
+
65
+ ## 重要行为
66
+
67
+ - `open: true` 时,如果缺少 `accessKeyId`、`accessKeySecret`、`bucket`、`region` 或 `uploadDir`,会直接中断构建。
68
+ - `manifest` 开启后,会自动上传全部文件,并自动保留本地构建文件。
69
+ - `manifest` 开启后,`skip` 会按空数组处理,`autoDelete` 会按 `false` 处理。
70
+ - `oss-manifest.json` 只记录本次成功上传的文件,不包含清单文件自身。
71
+ - `configBase` 会影响 Vite 打包后的资源路径,也会影响清单里的访问地址。
72
+ - `alias` 只影响清单里生成的访问地址,不会改变实际上传路径。
73
+
74
+ ## Manifest
75
+
76
+ 开启:
77
+
78
+ ```ts
79
+ vitePluginDeployOss({
80
+ // ...其他配置
81
+ manifest: true,
82
+ })
50
83
  ```
51
84
 
52
- ## 说明
85
+ 自定义文件名:
53
86
 
54
- - 当前版本仅支持 ESM(`import`),不再提供 CommonJS(`require`)入口。
55
- - `open` 默认 `true`,建议通过环境变量控制开关(例如 `DEPLOY_OSS=1` 时再上传)。
56
- - `fancy` 默认 `true`,TTY 终端下会显示实时动效进度(速度、预计剩余、并发、当前文件)。
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` 内容示例:
87
+ ```ts
88
+ vitePluginDeployOss({
89
+ // ...其他配置
90
+ manifest: {
91
+ fileName: 'meta/oss-manifest.json',
92
+ },
93
+ })
94
+ ```
95
+
96
+ 清单内容示例:
62
97
 
63
98
  ```json
64
99
  {
@@ -66,10 +101,34 @@ export default {
66
101
  "files": [
67
102
  {
68
103
  "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",
104
+ "key": "H5/demo/prod/assets/index-abc123.js",
105
+ "url": "https://example.com/H5/demo/prod/assets/index-abc123.js",
71
106
  "md5": "d41d8cd98f00b204e9800998ecf8427e"
72
107
  }
73
108
  ]
74
109
  }
75
110
  ```
111
+
112
+ ## 调试
113
+
114
+ 开启 `debug` 后,构建结束会额外输出关键步骤耗时,方便判断慢在哪里。
115
+
116
+ ```ts
117
+ vitePluginDeployOss({
118
+ // ...其他配置
119
+ debug: process.env.DEPLOY_OSS_DEBUG === '1',
120
+ })
121
+ ```
122
+
123
+ 也可以使用项目内的 playground 命令:
124
+
125
+ ```bash
126
+ pnpm run build:test:debug
127
+ ```
128
+
129
+ ## 注意事项
130
+
131
+ - 建议不要在配置里直接写真实密钥,优先使用环境变量。
132
+ - 建议生产发布时保持 `failOnError: true`,避免部分文件没上传却继续完成流程。
133
+ - 如果开启 `autoDelete`,请确认不需要保留本地构建文件。
134
+ - 当前版本仅支持 ESM,也就是 `import` 用法,不提供 `require` 入口。
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ interface vitePluginDeployOssOption extends Omit<oss.Options, 'accessKeyId' | 'a
18
18
  autoDelete?: boolean;
19
19
  skip?: string | string[];
20
20
  open?: boolean;
21
+ debug?: boolean;
21
22
  fancy?: boolean;
22
23
  noCache?: boolean;
23
24
  failOnError?: boolean;
package/dist/index.js CHANGED
@@ -179,6 +179,18 @@ ${body}
179
179
  ${bottom}`;
180
180
  };
181
181
  var renderInlineStats = (items) => items.filter(Boolean).join(chalk2.gray(" \xB7 "));
182
+ var getPanelDot = (tone = "success") => {
183
+ switch (tone) {
184
+ case "info":
185
+ return chalk2.green("\u25CF");
186
+ case "success":
187
+ return chalk2.green("\u25CF");
188
+ case "warning":
189
+ return chalk2.yellow("\u25CF");
190
+ case "danger":
191
+ return chalk2.red("\u25CF");
192
+ }
193
+ };
182
194
  var getLogSymbol = (tone) => {
183
195
  switch (tone) {
184
196
  case "success":
@@ -191,6 +203,20 @@ var getLogSymbol = (tone) => {
191
203
  };
192
204
 
193
205
  // src/index.ts
206
+ var formatTimingDuration = (durationMs) => {
207
+ if (durationMs < 1e3) return `${durationMs}ms`;
208
+ const seconds = durationMs / 1e3;
209
+ return `${seconds.toFixed(seconds >= 10 ? 1 : 2)}s`;
210
+ };
211
+ var renderDebugPanel = (entries) => {
212
+ const rows = entries.map((entry) => ({
213
+ label: `${entry.label}:`,
214
+ value: chalk3.cyan(
215
+ entry.detail ? `${formatTimingDuration(entry.durationMs)} \xB7 ${truncateTerminalText(entry.detail, 24)}` : formatTimingDuration(entry.durationMs)
216
+ )
217
+ }));
218
+ return renderPanel(`${getPanelDot("success")} \u8C03\u8BD5\u8017\u65F6`, rows, "info");
219
+ };
194
220
  var createManifestPayload = async (results, configBase, alias) => {
195
221
  const successfulResults = results.filter((result) => result.success);
196
222
  const files = await Promise.all(
@@ -223,6 +249,7 @@ function vitePluginDeployOss(option) {
223
249
  autoDelete = false,
224
250
  alias,
225
251
  open = true,
252
+ debug = false,
226
253
  fancy = true,
227
254
  noCache = false,
228
255
  failOnError = true,
@@ -235,6 +262,9 @@ function vitePluginDeployOss(option) {
235
262
  const normalizedUploadDir = normalizePathSegments(uploadDir);
236
263
  const normalizedConfigBase = configBase ? ensureTrailingSlash(normalizeUrlLikeBase(configBase)) : void 0;
237
264
  const normalizedAlias = alias ? normalizeUrlLikeBase(alias) : void 0;
265
+ const manifestFileName = resolveManifestFileName(manifest);
266
+ const effectiveAutoDelete = manifestFileName ? false : autoDelete;
267
+ const effectiveSkip = manifestFileName ? [] : Array.isArray(skip) ? skip : [skip];
238
268
  let buildFailed = false;
239
269
  let upload = false;
240
270
  let outDir = normalizePath(resolve2("dist"));
@@ -277,7 +307,7 @@ function vitePluginDeployOss(option) {
277
307
  headers
278
308
  });
279
309
  if (result.res.status === 200) {
280
- if (autoDelete) {
310
+ if (effectiveAutoDelete) {
281
311
  try {
282
312
  await unlink(task.filePath);
283
313
  } catch (error) {
@@ -334,12 +364,14 @@ function vitePluginDeployOss(option) {
334
364
  };
335
365
  const uploadFilesInBatches = async (client, files, windowSize = concurrency) => {
336
366
  const results = [];
367
+ const debugEntries = [];
337
368
  const totalFiles = files.length;
338
369
  const tasks = [];
339
370
  let completed = 0;
340
371
  let failed = 0;
341
372
  let uploadedBytes = 0;
342
373
  let retries = 0;
374
+ const taskPrepareStartedAt = Date.now();
343
375
  const taskCandidates = await Promise.all(
344
376
  files.map(async (relativeFilePath) => {
345
377
  const filePath = normalizePath(resolve2(outDir, relativeFilePath));
@@ -352,6 +384,11 @@ function vitePluginDeployOss(option) {
352
384
  }
353
385
  })
354
386
  );
387
+ debugEntries.push({
388
+ label: "\u751F\u6210\u4E0A\u4F20\u4EFB\u52A1",
389
+ durationMs: Date.now() - taskPrepareStartedAt,
390
+ detail: `${files.length} \u4E2A\u6587\u4EF6`
391
+ });
355
392
  for (const candidate of taskCandidates) {
356
393
  if (candidate.task) {
357
394
  tasks.push(candidate.task);
@@ -453,7 +490,12 @@ function vitePluginDeployOss(option) {
453
490
  } else {
454
491
  console.log(`${getLogSymbol("success")} \u6240\u6709\u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210 (${totalFiles}/${totalFiles})`);
455
492
  }
456
- return results;
493
+ debugEntries.push({
494
+ label: "\u4E0A\u4F20\u6587\u4EF6",
495
+ durationMs: Date.now() - startAt,
496
+ detail: `${tasks.length} \u4E2A\u6210\u529F\u5019\u9009 \xB7 \u5E76\u53D1 ${safeWindowSize}`
497
+ });
498
+ return { results, debugEntries };
457
499
  };
458
500
  return {
459
501
  name: "vite-plugin-deploy-oss",
@@ -466,9 +508,8 @@ function vitePluginDeployOss(option) {
466
508
  if (!open || buildFailed) return;
467
509
  const validationErrors = validateOptions();
468
510
  if (validationErrors.length > 0) {
469
- console.log(`${chalk3.red("\u2717 \u914D\u7F6E\u9519\u8BEF:")}
511
+ throw new Error(`vite-plugin-deploy-oss \u914D\u7F6E\u9519\u8BEF:
470
512
  ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
471
- return;
472
513
  }
473
514
  upload = true;
474
515
  config.base = normalizedConfigBase || config.base;
@@ -484,13 +525,19 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
484
525
  async handler() {
485
526
  if (!open || !upload || buildFailed || !resolvedConfig) return;
486
527
  const startTime = Date.now();
528
+ const debugEntries = [];
487
529
  const client = new oss({ region, accessKeyId, accessKeySecret, secure, bucket, ...props });
488
- const manifestFileName = resolveManifestFileName(manifest);
530
+ const collectFilesStartedAt = Date.now();
489
531
  const files = globSync("**/*", {
490
532
  cwd: outDir,
491
533
  nodir: true,
492
- ignore: Array.isArray(skip) ? skip : [skip]
534
+ ignore: effectiveSkip
493
535
  }).map((file) => normalizePath(file)).filter((file) => file !== manifestFileName);
536
+ debugEntries.push({
537
+ label: "\u626B\u63CF\u672C\u5730\u6587\u4EF6",
538
+ durationMs: Date.now() - collectFilesStartedAt,
539
+ detail: `${files.length} \u4E2A\u6587\u4EF6`
540
+ });
494
541
  if (files.length === 0) {
495
542
  console.log(`${getLogSymbol("warning")} \u6CA1\u6709\u627E\u5230\u9700\u8981\u4E0A\u4F20\u7684\u6587\u4EF6`);
496
543
  return;
@@ -498,7 +545,7 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
498
545
  clearViewport();
499
546
  console.log(
500
547
  renderPanel(
501
- "\u51C6\u5907\u90E8\u7F72",
548
+ `${getPanelDot("success")} \u51C6\u5907\u90E8\u7F72`,
502
549
  [
503
550
  { label: "\u4F4D\u7F6E:", value: chalk3.green(`${bucket} \xB7 ${region}`) },
504
551
  {
@@ -519,7 +566,11 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
519
566
  )
520
567
  );
521
568
  try {
522
- const results = await uploadFilesInBatches(client, files, concurrency);
569
+ const uploadExecution = await uploadFilesInBatches(client, files, concurrency);
570
+ const { results, debugEntries: uploadDebugEntries } = uploadExecution;
571
+ if (debug) {
572
+ debugEntries.push(...uploadDebugEntries);
573
+ }
523
574
  const successCount = results.filter((r) => r.success).length;
524
575
  const failedCount = results.length - successCount;
525
576
  const durationSeconds = (Date.now() - startTime) / 1e3;
@@ -531,13 +582,22 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
531
582
  const manifestRelativeFilePath = manifestFileName;
532
583
  const manifestFilePath = normalizePath(resolve2(outDir, manifestRelativeFilePath));
533
584
  const manifestObjectKey = normalizeObjectKey(normalizedUploadDir, manifestRelativeFilePath);
585
+ const manifestStartedAt = Date.now();
534
586
  await mkdir(dirname(manifestFilePath), { recursive: true });
535
587
  await writeFile(
536
588
  manifestFilePath,
537
589
  JSON.stringify(await createManifestPayload(results, normalizedConfigBase, normalizedAlias), null, 2),
538
590
  "utf8"
539
591
  );
592
+ if (debug) {
593
+ debugEntries.push({
594
+ label: "\u751F\u6210\u6E05\u5355\u6587\u4EF6",
595
+ durationMs: Date.now() - manifestStartedAt,
596
+ detail: manifestRelativeFilePath
597
+ });
598
+ }
540
599
  const manifestStats = await stat(manifestFilePath);
600
+ const manifestUploadStartedAt = Date.now();
541
601
  const manifestResult = await uploadSingleTask(client, {
542
602
  filePath: manifestFilePath,
543
603
  relativeFilePath: manifestRelativeFilePath,
@@ -548,6 +608,13 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
548
608
  if (!manifestResult.success) {
549
609
  throw manifestResult.error || new Error(`Failed to upload manifest: ${manifestRelativeFilePath}`);
550
610
  }
611
+ if (debug) {
612
+ debugEntries.push({
613
+ label: "\u4E0A\u4F20\u6E05\u5355\u6587\u4EF6",
614
+ durationMs: Date.now() - manifestUploadStartedAt,
615
+ detail: manifestRelativeFilePath
616
+ });
617
+ }
551
618
  const manifestUrl = resolveUploadedFileUrl(
552
619
  manifestRelativeFilePath,
553
620
  manifestObjectKey,
@@ -557,7 +624,14 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
557
624
  manifestSummary = truncateTerminalText(manifestUrl || manifestObjectKey, 20);
558
625
  }
559
626
  try {
627
+ const cleanupStartedAt = Date.now();
560
628
  await removeEmptyDirectories(outDir);
629
+ if (debug) {
630
+ debugEntries.push({
631
+ label: "\u6E05\u7406\u7A7A\u76EE\u5F55",
632
+ durationMs: Date.now() - cleanupStartedAt
633
+ });
634
+ }
561
635
  } catch (error) {
562
636
  console.warn(`${getLogSymbol("warning")} \u6E05\u7406\u7A7A\u76EE\u5F55\u5931\u8D25: ${error}`);
563
637
  }
@@ -596,11 +670,18 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
596
670
  }
597
671
  console.log(
598
672
  renderPanel(
599
- failedCount === 0 ? `${getLogSymbol("success")} \u90E8\u7F72\u5B8C\u6210` : `${getLogSymbol("warning")} \u90E8\u7F72\u5B8C\u6210`,
673
+ failedCount === 0 ? `${getPanelDot("success")} \u90E8\u7F72\u5B8C\u6210` : `${getPanelDot("warning")} \u90E8\u7F72\u5B8C\u6210`,
600
674
  resultRows,
601
675
  failedCount === 0 ? "success" : "warning"
602
676
  )
603
677
  );
678
+ if (debug) {
679
+ debugEntries.push({
680
+ label: "\u603B\u8017\u65F6",
681
+ durationMs: Date.now() - startTime
682
+ });
683
+ console.log(renderDebugPanel(debugEntries));
684
+ }
604
685
  if (failedCount > 0 && failOnError) {
605
686
  throw new Error(`Failed to upload ${failedCount} of ${results.length} files`);
606
687
  }
@@ -608,6 +689,13 @@ ${validationErrors.map((err) => ` - ${err}`).join("\n")}`);
608
689
  console.log(`
609
690
  ${getLogSymbol("danger")} \u4E0A\u4F20\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF: ${error}
610
691
  `);
692
+ if (debug && debugEntries.length > 0) {
693
+ debugEntries.push({
694
+ label: "\u5931\u8D25\u524D\u8017\u65F6",
695
+ durationMs: Date.now() - startTime
696
+ });
697
+ console.log(renderDebugPanel(debugEntries));
698
+ }
611
699
  if (failOnError) {
612
700
  throw error instanceof Error ? error : new Error(String(error));
613
701
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-deploy-oss",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",
@@ -32,7 +32,8 @@
32
32
  "description": "将dist目录下的文件上传到阿里云oss",
33
33
  "devDependencies": {
34
34
  "@types/ali-oss": "^6.23.3",
35
- "@types/node": "^22.19.13",
35
+ "@types/node": "^22.19.19",
36
+ "prettier": "3.8.1",
36
37
  "tsup": "^8.5.1",
37
38
  "typescript": "^5.9.3"
38
39
  },
@@ -44,16 +45,19 @@
44
45
  "ali-oss": "^6.23.0",
45
46
  "chalk": "^5.6.2",
46
47
  "cli-progress": "^3.12.0",
47
- "cli-truncate": "^5.2.0",
48
+ "cli-truncate": "^6.0.0",
48
49
  "glob": "^13.0.6",
49
50
  "log-symbols": "^7.0.1",
50
- "string-width": "^8.2.0"
51
+ "string-width": "^8.2.1"
51
52
  },
52
53
  "scripts": {
53
54
  "build": "tsup",
54
55
  "typecheck": "tsc -p tsconfig.json",
55
56
  "pack": "pnpm run build && pnpm pack",
57
+ "format": "prettier --write .",
58
+ "format:check": "prettier --check .",
56
59
  "build:test": "cd playground && vite build",
57
- "build:test:deploy": "cd playground && vite build --mode deploy"
60
+ "build:test:deploy": "cd playground && vite build --mode deploy",
61
+ "build:test:debug": "cd playground && vite build --mode deploy-debug"
58
62
  }
59
63
  }
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ esbuild: false