npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fleet-frontend/vite-plugin-upload-s3

v0.1.0

Published

A universal plugin for uploading assets to AWS S3 (supports Vite and Webpack)

Readme

@fleet-frontend/vite-plugin-upload-s3

一个通用的插件,用于自动将构建产物上传到 AWS S3 和兼容 S3 的服务。同时支持 ViteWebpack

特性

  • 🚀 自动上传: 构建完成后自动将构建产物上传到 S3
  • 🔧 多构建工具支持: 同时支持 Vite 和 Webpack
  • S3 兼容: 支持任何 S3 兼容的提供商(AWS、DigitalOcean Spaces 等)
  • 🎯 智能过滤: 通过文件扩展名、自定义函数或模式过滤文件
  • 📦 灵活配置: 支持 ES 模块和 CommonJS
  • 🔧 可定制: 灵活的配置选项,适用于不同的使用场景
  • 📊 实时进度: 实时显示上传进度,包括文件数量和百分比
  • 🔄 重试机制: 失败上传自动重试,支持指数退避
  • 🔒 TypeScript: 完整的 TypeScript 支持,包含类型定义
  • 🌐 多格式: 支持 ESM 和 CommonJS 模块格式
  • 📦 压缩支持: 自动处理 .gz、.br 和其他压缩文件

安装

pnpm add -D @fleet-frontend/vite-plugin-upload-s3
# 或
npm install -D @fleet-frontend/vite-plugin-upload-s3
# 或
yarn add -D @fleet-frontend/vite-plugin-upload-s3

注意: 此插件需要 vite (^4.0.0 || ^5.0.0) 或 webpack (^5.0.0) 作为 peer dependency。请安装你正在使用的构建工具:

# Vite 项目
pnpm add -D vite

# Webpack 项目
pnpm add -D webpack

快速开始

Vite

// vite.config.ts
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default {
  plugins: [
    ViteS3(true, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-east-1'
      },
      uploadOptions: {
        Bucket: 'your-bucket-name'
      }
    })
  ]
}

Webpack

// webpack.config.js
const { WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')

module.exports = {
  plugins: [
    new WebpackS3Plugin(true, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        },
        region: 'us-east-1'
      },
      uploadOptions: {
        Bucket: 'your-bucket-name'
      }
    })
  ]
}

基本使用

Vite

// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
// 或使用统一入口
// import { UploadS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default defineConfig({
  plugins: [
    // 方式 1: 使用 ViteS3(推荐用于 Vite)
    ViteS3(!!process.env.UPLOAD_ENABLED, {
      basePath: '/build',
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-east-1'
      },
      uploadOptions: {
        Bucket: 'your-bucket-name'
      }
    })
    
    // 方式 2: 使用统一入口
    // UploadS3(!!process.env.UPLOAD_ENABLED, { ... }, 'vite')
  ]
})

Webpack

// webpack.config.js
const { UploadS3, WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')

module.exports = {
  // ... 其他 webpack 配置
  plugins: [
    // 方式 1: 使用统一入口(推荐)
    UploadS3(!!process.env.UPLOAD_ENABLED, {
      basePath: '/build',
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        },
        region: 'us-east-1'
      },
      uploadOptions: {
        Bucket: 'your-bucket-name'
      }
    }, 'webpack'),
    
    // 方式 2: 直接使用 WebpackS3Plugin
    // new WebpackS3Plugin(!!process.env.UPLOAD_ENABLED, { ... })
  ]
}

注意: Webpack 插件仅在生产模式下上传。开发构建会自动跳过。

配置

必需选项

| 选项 | 类型 | 描述 | |--------|------|-------------| | clientConfig | S3ClientConfig | S3Client 类构造函数的配置接口 | | uploadOptions | PutObjectRequest | PutObjectRequest 选项(除了 Body 和 Key) |

可选选项

| 选项 | 类型 | 默认值 | 描述 | |--------|------|---------|-------------| | basePath | string | null | 上传文件在 S3 上的命名空间 | | directory | string | build.outDir | 要上传的目录 | | include | string\|RegExp\|Function\|Array | null | 用于匹配包含内容的模式 | | exclude | string\|RegExp\|Function\|Array | null | 用于匹配排除内容的模式(支持文件扩展名如 .map.txt 和 glob 模式如 **/*.map) | | sequentialUploads | boolean | false | 如果为 true,文件将按顺序上传 | | retryAttempts | number | 3 | 失败上传的重试次数 | | retryDelay | number | 1000 | 重试尝试之间的延迟(毫秒) | | forceOverwrite | string\|RegExp\|Function\|Array | null | 应强制覆盖的文件,无需检查它们是否已存在(支持文件扩展名、glob 模式和自定义函数) | | skipDirectoryPrefix | boolean | false | 如果为 true,跳过目录名作为 S3 路径前缀(直接上传目录内容) | | failOnError | boolean | true | 如果为 true,当文件上传失败时抛出错误并中断构建;如果为 false,上传失败时只记录警告,不中断构建 |

高级使用

// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default defineConfig({
  plugins: [
    ViteS3(!!process.env.UPLOAD_ENABLED, {
      basePath: '/cdn/assets',
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-west-2'
      },
      uploadOptions: {
        Bucket: 'your-bucket-name',
        CacheControl: 'public, max-age=31536000',
        ContentEncoding: 'gzip'
      },
      include: [
        /.*\.(css|js)$/,
        function(path) { 
          return path.includes('assets/') && !path.includes('test')
        }
      ],
      exclude: ["**/*.map", "**/*.txt", "**/*.log"], // 排除所有 .map, .txt, .log 文件
      forceOverwrite: ["index.html", "**/*.html"], // 强制覆盖所有 HTML 文件
      sequentialUploads: false,
    })
  ]
})

DigitalOcean Spaces 示例

// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default defineConfig({
  plugins: [
    ViteS3(!!process.env.UPLOAD_ENABLED, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.DO_ACCESS_KEY_ID!,
          secretAccessKey: process.env.DO_SECRET_ACCESS_KEY!,
        },
        endpoint: 'https://fra1.digitaloceanspaces.com',
        region: 'fra1'
      },
      uploadOptions: {
        Bucket: 'your-bucket-name'
      }
    })
  ]
})

环境变量

将你的 AWS 凭证设置为环境变量:

export AWS_ACCESS_KEY_ID="your-access-key-id"
export AWS_SECRET_ACCESS_KEY="your-secret-access-key"
export UPLOAD_ENABLED="true"

如果在 EC2/ECS/Lambda 上运行,也可以使用 AWS IAM 角色。

统一入口函数

对于可能在不同构建工具之间切换的项目,你可以使用统一的 UploadS3 函数:

import { UploadS3 } from '@fleet-frontend/vite-plugin-upload-s3'

// 用于 Vite
UploadS3(true, options, 'vite')

// 用于 Webpack
UploadS3(true, options, 'webpack')

模块支持

此插件同时支持 ES 模块和 CommonJS:

ES 模块 (ESM)

import { ViteS3, UploadS3, WebpackS3Plugin } from '@fleet-frontend/vite-plugin-upload-s3'

CommonJS

const { ViteS3, UploadS3, WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')

目录路径控制

插件支持控制是否在 S3 路径中包含目录名作为前缀。当启用 skipDirectoryPrefix 时,只会上传指定目录内的文件和子目录,而不会在 S3 路径中包含目录名本身。

目录路径控制配置

// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default defineConfig({
  plugins: [
    ViteS3(true, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-east-1',
      },
      uploadOptions: {
        Bucket: 'your-bucket-name',
      },
      // 指定要上传的目录
      directory: 'dist',
      // 跳过目录名作为 S3 路径前缀
      skipDirectoryPrefix: true
    })
  ]
})

目录路径控制示例

不使用 skipDirectoryPrefix(默认行为)

本地结构:
dist/
├── index.html
├── assets/
│   ├── style.css
│   └── app.js
└── images/
    └── logo.png

S3 结构:
your-bucket/
├── dist/index.html
├── dist/assets/style.css
├── dist/assets/app.js
└── dist/images/logo.png

使用 skipDirectoryPrefix: true

本地结构:
dist/
├── index.html
├── assets/
│   ├── style.css
│   └── app.js
└── images/
    └── logo.png

S3 结构:
your-bucket/
├── index.html
├── assets/style.css
├── assets/app.js
└── images/logo.png

使用场景

  • 静态网站托管: 当你想要直接从 S3 存储桶的根目录提供文件时
  • CDN 分发: 当你的 CDN 期望文件位于根级别时
  • 简洁 URL: 当你想要更简洁、更短的 URL,不包含构建目录名时
  • 自定义路径结构: 当你想要使用 basePath 来控制 S3 路径结构而不是目录名时

强制覆盖

插件支持强制覆盖特定文件,而无需检查它们是否已存在于 S3 上。这对于应该始终更新的文件(如 index.html 或其他关键文件)很有用。

强制覆盖配置

// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default defineConfig({
  plugins: [
    ViteS3(true, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-east-1',
      },
      uploadOptions: {
        Bucket: 'your-bucket-name',
      },
      // 强制覆盖特定文件
      forceOverwrite: [
        "index.html",           // 强制覆盖 index.html
        "**/*.html",           // 强制覆盖所有 HTML 文件
        /\.html$/,             // 强制覆盖以 .html 结尾的文件
        (path) => path.includes('critical') // 强制覆盖包含 'critical' 的文件
      ]
    })
  ]
})

强制覆盖模式类型

forceOverwrite 选项支持与 includeexclude 相同的模式类型:

文件扩展名过滤

forceOverwrite: [".html", ".css", ".js"] // 强制覆盖特定文件扩展名

Glob 模式过滤

forceOverwrite: [
  "index.html",           // 强制覆盖 index.html
  "**/*.html",           // 强制覆盖所有 HTML 文件
  "dist/assets/*.js",    // 强制覆盖 dist/assets/ 中的 JS 文件
  "**/critical/**"       // 强制覆盖 critical 目录中的文件
]

正则表达式过滤

forceOverwrite: [/\.html$/, /index\./, /critical/] // 使用正则表达式

函数过滤

forceOverwrite: [
  (path) => path.includes('index'),
  (path) => path.endsWith('.html'),
  (path) => path.includes('critical')
]

混合模式过滤

forceOverwrite: [
  "index.html",                    // 文件名
  "**/*.html",                    // glob 模式
  /\.critical\./,                 // 正则表达式
  (path) => path.includes('temp') // 函数
]

文件过滤

插件支持使用多种模式类型进行灵活的文件过滤:

文件扩展名过滤

exclude: [".map", ".txt", ".log"] // 排除特定扩展名的文件

Glob 模式过滤

exclude: [
  "**/*.map",           // 排除所有 .map 文件
  "**/test/**",         // 排除 test 目录下的所有文件
  "**/*.test.*",        // 排除测试文件
  "dist/assets/*.js"    // 排除特定路径下的 .js 文件
]

正则表达式过滤

exclude: [/\.map$/, /\.txt$/, /test/] // 使用正则表达式

函数过滤

exclude: [
  (path) => path.includes('test'),
  (path) => path.endsWith('.map')
]

混合模式过滤

exclude: [
  ".map",                    // 文件扩展名
  "**/*.txt",               // glob 模式
  /\.log$/,                 // 正则表达式
  (path) => path.includes('temp') // 函数
]

错误处理配置

插件支持配置上传失败时的行为。默认情况下,当文件上传失败时,插件会抛出错误并中断构建流程。你可以通过 failOnError 选项来控制这个行为。

默认行为(failOnError: true)

failOnErrortrue(默认值)时,如果文件上传失败,构建流程会被中断:

// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default defineConfig({
  plugins: [
    ViteS3(true, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-east-1',
      },
      uploadOptions: {
        Bucket: 'your-bucket-name',
      },
      // failOnError 默认为 true,上传失败会中断构建
      // failOnError: true
    })
  ]
})

注意:文件已存在(跳过上传)的情况不算失败,不会中断构建。只有真正需要上传但上传失败的文件才会触发错误。

允许上传失败继续构建(failOnError: false)

如果你希望即使上传失败也继续构建流程,可以设置 failOnError: false

// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'

export default defineConfig({
  plugins: [
    ViteS3(true, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-east-1',
      },
      uploadOptions: {
        Bucket: 'your-bucket-name',
      },
      // 上传失败时不中断构建,只记录警告
      failOnError: false
    })
  ]
})

使用场景

  • failOnError: true(默认):适用于生产环境,确保所有文件都成功上传到 CDN
  • failOnError: false:适用于开发环境或测试场景,允许构建继续完成,即使上传失败

插件顺序和压缩支持

Vite

插件现在按照你在 Vite 配置中定义的顺序执行,使其能够与压缩插件正常工作:

import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
import { compression } from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    // 其他插件...
    
    // 压缩插件 - 生成 .gz 文件
    compression({
      algorithm: 'gzip',
      ext: '.gz'
    }),
    
    // S3 上传插件 - 在压缩插件之后执行
    ViteS3(true, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        region: 'us-east-1',
      },
      uploadOptions: {
        Bucket: 'your-bucket-name',
      },
      // 包含压缩文件
      include: [/\.(js|css|html|gz|br)$/],
      // 排除源映射文件
      exclude: ["**/*.map"]
    })
  ]
})

Webpack

对于 Webpack,插件在构建完成后自动运行。你可以将其与其他 Webpack 插件一起使用:

const { WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')
const CompressionPlugin = require('compression-webpack-plugin')

module.exports = {
  plugins: [
    // 压缩插件
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
    }),
    
    // S3 上传插件 - 在构建完成后运行
    new WebpackS3Plugin(!!process.env.UPLOAD_ENABLED, {
      clientConfig: {
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        },
        region: 'us-east-1',
      },
      uploadOptions: {
        Bucket: 'your-bucket-name',
      },
      // 包含压缩文件
      include: [/\.(js|css|html|gz|br)$/],
      // 排除源映射文件
      exclude: ['**/*.map']
    })
  ]
}

进度显示

插件在上传过程中提供实时进度更新:

🚀 Uploading 15 files to S3...
[8/15] 53% ✅ 6 ❌ 2 | 📁 style.css

进度信息:

  • [8/15] - 当前文件编号 / 总文件数
  • 53% - 完成百分比
  • ✅ 6 - 成功上传的文件数
  • ❌ 2 - 失败的上传数
  • 📁 style.css - 正在处理的当前文件

构建工具支持

此插件使用相同的配置选项同时支持 Vite 和 Webpack。主要区别在于如何导入和使用插件:

| 特性 | Vite | Webpack | |---------|------|---------| | 导入 | import { ViteS3 } from '...' | const { WebpackS3Plugin } = require('...') | | 使用 | ViteS3(enabled, options) | new WebpackS3Plugin(enabled, options) | | 统一入口 | UploadS3(enabled, options, 'vite') | UploadS3(enabled, options, 'webpack') | | 构建钩子 | closeBundle | done | | 开发模式 | 由 enabled 参数控制 | 自动跳过 | | 输出检测 | 从 config.build.outDir | 从 compiler.options.output.path |

Vite 支持

插件完全支持 Vite 的所有功能:

  • 使用 Vite 的 closeBundle 钩子确保可靠的上传时机
  • 自动从 Vite 配置中检测构建输出目录
  • 与其他 Vite 插件无缝协作
  • 尊重 enabled 参数进行条件上传

Webpack 支持

插件支持 Webpack 5:

  • 使用 Webpack 的 done 钩子在构建完成后上传
  • 在开发模式下自动跳过上传(无论 enabled 参数如何)
  • 从 Webpack 配置中读取输出目录
  • 支持所有 Webpack 5 功能

重要: 使用 Webpack 时,请确保你使用的是 Webpack 5 或更高版本。不支持 Webpack 4。

示例

查看 examples/ 目录以获取更详细的使用示例:

有关 Webpack 示例,请参阅上面的 Webpack 部分中的使用模式。

开发

# 安装依赖
pnpm install

# 构建插件
pnpm build

# 监听模式
pnpm dev

# 代码检查
pnpm lint

# 类型检查
pnpm type-check

许可证

MIT

贡献

欢迎贡献!请随时提交 Pull Request。