手搓一个 Webpack5 插件:让每一行代码都自带「回声」——BannerWebpackPlugin 从 0 到全球下载
摘要:本文带你从 0 到 1 实现一个「BannerWebpackPlugin」——自动为 JS/CSS 插入版权 banner,并额外生成独立版权文件。过程中,我们将深入 Webpack5 的钩子宇宙、Source 家族与时间戳陷阱,最后附赠「发布 npm + CI 自动注入」一条龙方案。读完,你可以自信地在简历里写下:「写过 Webpack 插件,线上日下载 3k+」。
一、为什么需要「自动版权」?
- 法务合规:公司代码必须带版权头,人工粘贴容易漏。
- 品牌露出:开源库构建后无 banner,等于「裸奔」。
- CI/CD 友好:构建阶段一次性注入,不污染源码,不增加网络请求。
二、效果速览
构建前:
// src/index.js
console.log('hello');
构建后:
/*! Copyright (c) 2025 YourCompany */
console.log('hello');
同时生成:
dist/copyright.txt
----------------------------------------
Copyright by YourCompany
Generated at: 2025-06-25T08:00:00.000Z
三、核心原理:Webpack 的「processAssets」钩子
Webpack5 把「输出文件列表」抽象成一个内存级 Map —— compilation.assets。
在 processAssets 阶段(官方叫 PROCESS_ASSETS_STAGE_ADDITIONS):
- 所有 chunk 已编码完成,尚未写盘;
- 我们可以读取 / 修改 / 新增任意文件;
- 改完后 webpack 会统一写磁盘,无需关心路径或文件名冲突。
四、插件实现(TypeScript 友好版)
// plugins/banner-webpack-plugin.js
const { RawSource } = require('webpack-sources');
class BannerWebpackPlugin {
constructor(options = {}) {
this.banner = options.banner || '/*! (c) 2025 */\n';
}
apply(compiler) {
const pluginName = 'BannerWebpackPlugin';
compiler.hooks.compilation.tap(pluginName, (compilation) => {
compilation.hooks.processAssets.tap(
{
name: pluginName,
stage: compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
},
(assets) => {
// 1. 遍历所有待输出文件
for (const name in assets) {
if (!/\.(js|css)$/i.test(name)) continue;
const oldSource = assets[name];
const newSource = this.banner + oldSource.source().toString();
assets[name] = new RawSource(newSource);
}
// 2. 额外生成版权清单
const txt = `Copyright by YourCompany\nGenerated at: ${new Date().toISOString()}`;
compilation.emitAsset('copyright.txt', new RawSource(txt));
}
);
});
}
}
module.exports = BannerWebpackPlugin;
五、踩坑记录:「时间戳」导致双文件?
现象:每次保存后 dist 里出现 两个 fileList-xxx.md。
原因:processAssets 在 watch 模式下会触发多次 compilation,而时间戳文件名永远唯一。
解决:① 固定文件名;② 或先删除旧 fileList-*.md 再写入。
六、使用方式
// webpack.config.js
const BannerWebpackPlugin = require('./plugins/banner-webpack-plugin');
module.exports = {
plugins: [
new BannerWebpackPlugin({
banner: '//! MIT License - (c) 2025 YourCompany\n',
}),
],
};
构建:
npm run build
产物:
dist/
├── app.1234.js // 顶部自带 banner
├── main.css
└── copyright.txt // 独立版权文件
七、再进一步:发布到 npm + CI 自动注入
-
发包
npm init -y npm publish --access public -
GitHub Actions 自动注入
- name: Build run: | npm i your-copyright-plugin -D npm run build -
徽章炫耀
写进简历:「维护 xxx-webpack-plugin,周下载 3k+」
八、总结:插件思维模型
| 阶段 | 钩子 | 操作 |
|---|---|---|
| 编译开始 | compiler.hooks.compilation | 拿到 compilation 实例 |
| 资源已生成 | compilation.hooks.processAssets | 读写 assets Map |
| 写盘前 | compilation.emitAsset | 新增文件 |
| 写盘后 | compiler.hooks.done | 打印日志 |
记住:**「钩子 → assets → RawSource → emitAsset」**四步曲,
任何「自动注入 / 自动生成 / 自动上传」需求都能套用这个模板。
现在,打开你的项目,敲下
npm run build,
听——每一行代码都在回声:
「Copyright (c) 2025,这是我写下的痕迹。」
- 个人发布的插件www.npmjs.com/package/cop…
自动为 **JS/CSS** 顶部插入版权 banner,同时生成独立 `copyright.txt` 清单——**零配置、无侵入、构建即合规**。
npm i copyright-webpack-plugin-qw