content-injector-webpack-plugin
Version:
A flexible Webpack plugin for injecting dynamic content (e.g., version, timestamps) into build assets with precise file matching and position control.
80 lines (79 loc) • 3.05 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const webpack_1 = require("webpack");
const crypto_1 = require("crypto");
class ContentInjectorWebpackPlugin {
constructor(options) {
this.options = {
match: /\.js$/,
position: 'head',
injectHash: false,
hashAlgorithm: 'md5',
hashLength: 8,
...options,
};
}
apply(compiler) {
compiler.hooks.emit.tapAsync('ContentInjectorWebpackPlugin', (compilation, callback) => {
Object.keys(compilation.assets).forEach((filename) => {
if (this.shouldProcessFile(filename) &&
this.validateFilename(filename)) {
const asset = compilation.assets[filename];
const originalSource = asset.source().toString();
const fileHash = this.options.injectHash
? this.getFileHash(originalSource)
: '';
let bannerContent;
if (typeof this.options.content === 'function') {
bannerContent = this.options.injectHash
? this.options.content(fileHash)
: this.options.content();
}
else {
bannerContent = this.options.content;
}
const newSource = this.options.position === 'head'
? new webpack_1.sources.ConcatSource(bannerContent, originalSource)
: new webpack_1.sources.ConcatSource(originalSource, bannerContent);
compilation.assets[filename] = newSource;
}
});
callback();
});
}
getFileHash(content) {
const hash = (0, crypto_1.createHash)(this.options.hashAlgorithm)
.update(content)
.digest('hex');
return this.options.hashLength
? hash.substring(0, this.options.hashLength)
: '';
}
validateFilename(filename) {
const { include, exclude } = this.options;
if (exclude && this.matchCondition(filename, exclude)) {
return false;
}
if (include) {
return this.matchCondition(filename, include);
}
return this.matchCondition(filename, this.options.match);
}
matchCondition(filename, condition) {
if (typeof condition === 'function') {
return condition(filename);
}
if (typeof condition === 'string') {
return filename.includes(condition);
}
if (condition instanceof RegExp) {
return condition.test(filename);
}
return false;
}
shouldProcessFile(filename) {
const isTextAsset = /\.(js|css|txt|html?|xml|json|svg)$/i.test(filename);
return isTextAsset && !/\.map$/.test(filename);
}
}
module.exports = ContentInjectorWebpackPlugin;