new webpack.BannerPlugin({
banner: 'hello world',
raw: boolean, // true,不会变成注释
entryOnly: boolean, // 只针对入口 chunk
test: string | RegExp | [string, RegExp], // Include all modules that pass test assertion.
include: string | RegExp | [string, RegExp], // Include all modules matching any of these conditions.
exclude: string | RegExp | [string, RegExp], // Exclude all modules matching any of these conditions.
footer?: boolean, // true,插在文件尾部
}),
for (const chunk of compilation.chunks) {
if(chunk.canBeInitial()){ // 判断入口 chunk
// ...
}
}
ModuleFilenameHelpers.matchObject(optisn,filename) // filename: 'main.js'
// 实现
ModuleFilenameHelpers.matchObject = (obj, str) => {
if (obj.test) {
if (!ModuleFilenameHelpers.matchPart(str, obj.test)) {
return false;
}
}
if (obj.include) {
if (!ModuleFilenameHelpers.matchPart(str, obj.include)) {
return false;
}
}
if (obj.exclude) {
if (ModuleFilenameHelpers.matchPart(str, obj.exclude)) {
return false;
}
}
return true;
};
ModuleFilenameHelpers.matchPart = (str, test) => {
if (!test) return true;
test = asRegExp(test);
if (Array.isArray(test)) {
return test.map(asRegExp).some(regExp => regExp.test(str));
} else {
return test.test(str);
}
};
// 将字符串转换成正则表达式
const asRegExp = test => {
if (typeof test === "string") {
test = new RegExp("^" + test.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); // 将字符串变成正则表达式,匹配给定字符串中的和正则相关的特殊字符,统一在前面加上\进行转译
// $&:表示前面正则匹配到的内容
// \\:表示转译 \,使得其不能转译 $,从而使 '*.' => '/\*\./'
}
return test;
};
Template.toComment
static toComment(str) {
if (!str) return "";
return `/*! ${str.replace(/\*\//g;, "* /")} */`;
}
let res=compilation.getPath((data)=>`dawdaa ${data.name}`,{name:'gg'})
// "dawdaa gg"
const { ConcatSource } = require("webpack-sources");
compilation.updateAsset(file, old => {
let cached = cache.get(old);
if (!cached || cached.comment !== comment) {
const source = options.footer
// 合并文件内容
? new ConcatSource(old, "\n", comment)
: new ConcatSource(comment, "\n", old);
cache.set(old, { source, comment });
return source;
}
return cached.source;
});
class BannerPlugin {
constructor(options) {
if (typeof options === "string" || typeof options === "function") {
options = {
banner: options
};
}
validate(options);
this.options = options;
const bannerOption = options.banner;
if (typeof bannerOption === "function") {
const getBanner = bannerOption;
this.banner = this.options.raw
? getBanner
: data => wrapComment(getBanner(data));
} else {
const banner = this.options.raw // false 会将字符串变成注释
? bannerOption
: wrapComment(bannerOption);
this.banner = () => banner;
}
}
}
wrapComment
const wrapComment = str => {
if (!str.includes("\n")) {
return Template.toComment(str); '/*! hello world */'
}
return `/*!\n * ${str // 将一个字符串转换成一个多行注释的格式
.replace(/\*\//g, "* /")
.split("\n")
.join("\n * ")
.replace(/\s+\n/g, "\n")
.trimEnd()}\n */`;
// 比如:
//'Hello, world!\nThis is a test.';
/*!
* Hello, world!
* This is a test.
*/
};
apply(compiler) {
const options = this.options;
const banner = this.banner;
// 文件名匹配规则方法
const matchObject = ModuleFilenameHelpers.matchObject.bind(
undefined,
options
);
const cache = new WeakMap(); // 从这里创建缓存可以看出 apply 只执行一次,后续重新编译只会执行 tap 注册的方法
compiler.hooks.compilation.tap("BannerPlugin", compilation => {
compilation.hooks.processAssets.tap(
{
name: "BannerPlugin",
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
},
() => {
// 遍历生成的 chunk
for (const chunk of compilation.chunks) {
if (options.entryOnly && !chunk.canBeInitial()) {
continue;
}
// 拿到每一个 chunk 包含的文件
for (const file of chunk.files) {
// 匹配文件名和开发者传入的 test、include、exclude 配置
if (!matchObject(file)) {
continue;
}
// 要交给生成模版字符串的 data
const data = {
chunk,
filename: file
};
// banner: () => 'hello world'
// comment: "/*! hello world */"
const comment = compilation.getPath(banner, data);
// 更新文件资源,插入注释信息
compilation.updateAsset(file, old => {
// 先读取缓存信息
let cached = cache.get(old);
// 如果没有添加过或者注释内容改变了,重新生成资源
if (!cached || cached.comment !== comment) {
const source = options.footer
? new ConcatSource(old, "\n", comment)
: new ConcatSource(comment, "\n", old);
cache.set(old, { source, comment });
// 返回更新后的文件内容
return source;
}
// 返回缓存文件中的内容
return cached.source;
});
}
}
}
}