# unplugin-pack-orchestrator **Repository Path**: wangkai000/unplugin-pack-orchestrator ## Basic Information - **Project Name**: unplugin-pack-orchestrator - **Description**: 基于 unplugin 的通用打包归档插件,支持 Vite、Webpack、Rollup、ESBuild 等多种构建工具,生成 ZIP / TAR / TAR.GZ / 7Z 归档,同时计算 MD5 / SHA1 / SHA256 校验和,支持自动重命名。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-01 - **Last Updated**: 2026-05-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # unplugin-pack-orchestrator
![NPM Version](https://img.shields.io/npm/v/unplugin-pack-orchestrator) ![NPM Downloads](https://img.shields.io/npm/dm/unplugin-pack-orchestrator) ![License](https://img.shields.io/npm/l/unplugin-pack-orchestrator) ![TypeScript](https://img.shields.io/npm/types/unplugin-pack-orchestrator) [English](./README.en.md) | **中文** > 基于 [unplugin](https://github.com/unjs/unplugin) 的通用打包归档插件,支持 **Vite**、**Webpack**、**Rollup**、**ESBuild**,可生成 **ZIP** / **TAR** / **TAR.GZ** / **7Z** 归档文件,运行时零额外依赖。构建完成后自动计算校验和(MD5/SHA1/SHA256),支持通过 `onAfterBuild` 钩子自动重命名归档文件(如追加哈希、版本号),实现产物可追溯。 [报告问题](https://github.com/wangkai000/unplugin-pack-orchestrator/issues) · [功能请求](https://github.com/wangkai000/unplugin-pack-orchestrator/issues)
--- ## 功能特点 - **通用适配** — 通过 unplugin 适配层,一套 API 同时支持 Vite、Webpack、Rollup、ESBuild - **多格式支持** — ZIP、TAR、TAR.GZ、7Z 四种归档格式 - **智能命名** — 文件名模板支持 `[name]`、`[version]`、`[hash]`、`[timestamp]` 占位符 - **精确过滤** — 基于 fast-glob 的 glob 模式文件过滤 - **生命周期钩子** — 构建前、产物生成后、归档完成后、错误处理 - **校验和** — 自动生成 MD5 / SHA1 / SHA256 哈希值 - **零额外依赖** — 归档创建完全基于 Node.js 内置模块 --- ## 安装 ```bash npm install unplugin-pack-orchestrator # 或 pnpm add unplugin-pack-orchestrator # 或 yarn add unplugin-pack-orchestrator ``` --- ## 快速开始 ### Vite ```typescript // vite.config.ts import { defineConfig } from 'vite' import packOrchestrator from 'unplugin-pack-orchestrator/vite' export default defineConfig({ plugins: [ packOrchestrator({ pack: { format: 'zip', fileName: 'dist-[name]-[version]', outDir: 'dist', // 要打包的源目录 archiveOutDir: '.', // 归档文件输出目录,默认项目根目录 include: ['**/*'], exclude: ['**/*.map', '**/*.d.ts', 'node_modules/**'], compressionLevel: 9, // 0-9,数字越大压缩率越高 }, hooks: { onBeforeBuild: () => console.log('开始构建...'), onBundleGenerated: (bundle) => console.log('产物已生成:', bundle), onAfterBuild: (path, format, checksums) => { console.log('归档完成:', path) console.log('MD5:', checksums.md5) console.log('SHA256:', checksums.sha256) }, onError: (err) => console.error('错误:', err.message), }, }), ], }) ``` > **注意**: Vite 构建完成后插件自动将 `outDir` 目录打包为归档,无需额外操作。 ### Rollup ```javascript // rollup.config.js import packOrchestrator from 'unplugin-pack-orchestrator/rollup' export default { input: './src/index.js', output: { dir: 'dist', format: 'es' }, plugins: [ packOrchestrator({ pack: { format: 'tar.gz', fileName: 'bundle-[name]-[version]', outDir: 'dist', archiveOutDir: './releases', include: ['**/*'], exclude: ['**/*.map', '**/*.d.ts'], compressionLevel: 9, }, hooks: { onBeforeBuild: () => console.log('开始打包...'), onAfterBuild: (path, format, checksums) => { console.log('归档已生成:', path) }, onError: (err) => console.error('打包失败:', err.message), }, }), ], } ``` > **注意**: Rollup 输出到目录时,插件在 `output.dir` 写入完成后触发打包。 ### Webpack ```javascript // webpack.config.js const packOrchestrator = require('unplugin-pack-orchestrator/webpack') module.exports = { mode: 'production', entry: './src/index.js', output: { path: 'dist', filename: 'bundle.js', }, plugins: [ packOrchestrator({ pack: { format: 'zip', fileName: 'webpack-[name]-[version]-[timestamp]', outDir: 'dist', archiveOutDir: './dist-archives', include: ['**/*'], exclude: ['**/*.map', '**/*.d.ts', 'node_modules/**'], compressionLevel: 9, }, hooks: { onBeforeBuild: () => console.log('Webpack 构建开始'), onBundleGenerated: (bundle) => console.log('产物已生成:', bundle), onAfterBuild: (path, format, checksums) => { console.log('归档已生成:', path) console.log('MD5:', checksums.md5) console.log('SHA256:', checksums.sha256) }, onError: (err) => console.error('错误:', err.message), }, }), ], } ``` > **注意**: Webpack 使用 CommonJS 导入方式,插件路径为 `unplugin-pack-orchestrator/webpack`。 ### ESBuild ```javascript // build.js const packOrchestrator = require('unplugin-pack-orchestrator/esbuild') async function build() { const result = await import('esbuild').then(esbuild => esbuild.build({ entryPoints: ['./src/index.js'], bundle: true, outfile: 'dist/bundle.js', platform: 'node', target: 'node18', plugins: [ packOrchestrator({ pack: { format: '7z', fileName: 'esbuild-[name]-[version]', outDir: 'dist', archiveOutDir: './artifacts', include: ['**/*'], exclude: ['**/*.map'], compressionLevel: 9, }, hooks: { onAfterBuild: (path, format, checksums) => { console.log('归档已生成:', path) console.log('MD5:', checksums.md5) }, }, }), ], })) await result } build() ``` > **注意**: ESBuild 使用动态 import,需注意插件路径引用方式。ESBuild 模式支持 `format: '7z'` 格式。 --- ## 配置选项 ### pack 参数 | 参数 | 类型 | 默认值 | 说明 | |------|------|---------|------| | `format` | `'zip' \| 'tar' \| 'tar.gz' \| '7z'` | `'zip'` | 归档格式 | | `fileName` | `string` | `'[name]-[version]'` | 输出文件名,支持占位符 | | `outDir` | `string` | `'dist'` | 要打包的源目录 | | `archiveOutDir` | `string` | `'.'` | 归档文件输出目录 | | `compressionLevel` | `number` | `9` | 压缩级别 0-9 | | `include` | `string[]` | `[]` | 包含的 glob 模式 | | `exclude` | `string[]` | `[]` | 排除的 glob 模式 | ### fileName 占位符 | 占位符 | 说明 | 示例值 | |--------|------|--------| | `[name]` | package.json 中的 name | `my-app` | | `[version]` | package.json 中的 version | `1.0.0` | | `[timestamp]` | 当前时间戳(毫秒) | `1714012345678` | | `[hash]` | 归档内容 MD5 哈希(32 位) | `a1b2c3d4e5f6a7b8...` | | `[hash:N]` | MD5 哈希前 N 位 | `[hash:8]` → `a1b2c3d4` | > 不写扩展名时,插件自动根据 `format` 追加后缀。如 `fileName: '[name]-v[version]'` → `my-app-v1.0.0.zip` ### 生命周期钩子 | 钩子 | 参数 | 触发时机 | |-------|------|---------| | `onBeforeBuild` | `()` | 构建开始前执行 | | `onBundleGenerated` | `(bundle: string)` | 产物生成后、压缩前执行 | | `onAfterBuild` | `(archivePath, format, checksums)` | 归档创建完成后执行 | | `onError` | `(error: Error)` | 发生错误时执行 | ### onAfterBuild 压缩完成后执行,可返回新路径实现自动重命名(返回值与原路径不同时生效): ```typescript // 1. 在扩展名前插入 sha1 哈希 // app.zip → app-3a7b2c1d.zip onAfterBuild: (path, format, checksums) => path.replace(/(\.(?:zip|tar\.gz|tar|7z))$/, `-${checksums.sha1.slice(0, 8)}$1`); // 2. 用 MD5 全量替换文件名 // app.zip → a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6.zip onAfterBuild: (path, format, checksums) => path.replace(/^.+(?=\.\w+$)/, checksums.md5); // 3. 追加格式和哈希到原始文件名 // app.zip → app-zip-3a7b2c1d.zip onAfterBuild: (path, format, checksums) => path.replace(/(\.\w+)$/, `-${format}-${checksums.sha256.slice(0, 8)}$1`); // 4. 完全自定义文件名,用 format 参数自动适配后缀 // app.zip → release-a1b2c3d4e5f6.zip onAfterBuild: (path, format, checksums) => `release-${checksums.md5.slice(0, 12)}.${format}`; ``` `checksums` 结构: ```typescript { md5: string; sha1: string; sha256: string } ``` > ⚠️ `onAfterBuild` 返回的路径后缀必须与打包 `format` 一致(如 `zip` 格式必须以 `.zip` 结尾),否则会输出警告。文件仍会重命名,但后缀不匹配可能导致下游解析异常。 --- ## 完整示例 ### Vite + tar.gz + 发布流程 ```typescript // vite.config.ts import { defineConfig } from 'vite' import packOrchestrator from 'unplugin-pack-orchestrator/vite' export default defineConfig({ plugins: [ packOrchestrator({ pack: { format: 'tar.gz', fileName: 'release-[name]-v[version]-[timestamp]', outDir: 'dist', archiveOutDir: './releases', compressionLevel: 9, include: ['**/*'], exclude: [ '**/*.map', '**/*.d.ts', 'node_modules/**', '.git/**', ], }, hooks: { onBeforeBuild: () => console.log('开始构建和打包...'), onBundleGenerated: (bundle) => console.log('构建产物已生成'), onAfterBuild: (path, format, checksums) => { console.log('归档完成!') console.log('文件:', path) console.log('MD5: ', checksums.md5) console.log('SHA256: ', checksums.sha256) }, onError: (err) => console.error('发生错误:', err.message), }, }), ], }) ``` ### Webpack + 7z + 自动重命名 ```javascript // webpack.config.js const packOrchestrator = require('unplugin-pack-orchestrator/webpack') const fs = require('fs') const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', output: { path: 'dist', filename: 'bundle.js', }, plugins: [ packOrchestrator({ pack: { format: '7z', fileName: '[name]-[version]', outDir: 'dist', archiveOutDir: './artifacts', include: ['**/*'], exclude: ['**/*.map', 'node_modules/**'], compressionLevel: 9, }, hooks: { onAfterBuild: (archivePath, format, checksums) => { const dir = path.dirname(archivePath) const ext = path.extname(archivePath) const base = path.basename(archivePath, ext) const newPath = path.join(dir, `${base}-${checksums.sha256.slice(0, 8)}${ext}`) fs.renameSync(archivePath, newPath) console.log('已重命名:', newPath) }, }, }), ], } ``` --- ## License MIT