blob: 57e375592d984e9a429bcd9f800fa2d15cd662e4 [file] [log] [blame]
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _crypto = _interopRequireDefault(require("crypto"));
var _path = _interopRequireDefault(require("path"));
var _webpack = _interopRequireWildcard(require("webpack"));
var _schemaUtils = require("schema-utils");
var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
var _options = _interopRequireDefault(require("./options.json"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
const {
RawSource
} = // eslint-disable-next-line global-require
_webpack.default.sources || require("webpack-sources");
class CompressionPlugin {
constructor(options = {}) {
(0, _schemaUtils.validate)(_options.default, options, {
name: "Compression Plugin",
baseDataPath: "options"
});
const {
test,
include,
exclude,
cache = true,
algorithm = "gzip",
compressionOptions = {},
filename = "[path][base].gz",
threshold = 0,
minRatio = 0.8,
deleteOriginalAssets = false
} = options;
this.options = {
test,
include,
exclude,
cache,
algorithm,
compressionOptions,
filename,
threshold,
minRatio,
deleteOriginalAssets
};
this.algorithm = this.options.algorithm;
if (typeof this.algorithm === "string") {
// eslint-disable-next-line global-require
const zlib = require("zlib");
this.algorithm = zlib[this.algorithm];
if (!this.algorithm) {
throw new Error(`Algorithm "${this.options.algorithm}" is not found in "zlib"`);
}
const defaultCompressionOptions = {
gzip: {
level: zlib.constants.Z_BEST_COMPRESSION
},
deflate: {
level: zlib.constants.Z_BEST_COMPRESSION
},
deflateRaw: {
level: zlib.constants.Z_BEST_COMPRESSION
},
brotliCompress: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY
}
}
}[algorithm] || {};
this.options.compressionOptions = { ...defaultCompressionOptions,
...this.options.compressionOptions
};
}
} // eslint-disable-next-line consistent-return
static getAsset(compilation, name) {
// New API
if (compilation.getAsset) {
return compilation.getAsset(name);
}
if (compilation.assets[name]) {
return {
name,
source: compilation.assets[name],
info: {}
};
}
}
static emitAsset(compilation, name, source, assetInfo) {
// New API
if (compilation.emitAsset) {
compilation.emitAsset(name, source, assetInfo);
} // eslint-disable-next-line no-param-reassign
compilation.assets[name] = source;
}
static updateAsset(compilation, name, newSource, assetInfo) {
// New API
if (compilation.updateAsset) {
compilation.updateAsset(name, newSource, assetInfo);
} // eslint-disable-next-line no-param-reassign
compilation.assets[name] = newSource;
}
static deleteAsset(compilation, name) {
// New API
if (compilation.deleteAsset) {
compilation.deleteAsset(name);
} // eslint-disable-next-line no-param-reassign
delete compilation.assets[name];
}
runCompressionAlgorithm(input) {
return new Promise((resolve, reject) => {
this.algorithm(input, this.options.compressionOptions, (error, result) => {
if (error) {
return reject(error);
}
if (!Buffer.isBuffer(result)) {
// eslint-disable-next-line no-param-reassign
result = Buffer.from(result);
}
return resolve(result);
});
});
}
async compress(compilation, assets, CacheEngine, weakCache) {
const assetNames = Object.keys(typeof assets === "undefined" ? compilation.assets : assets).filter(assetName => // eslint-disable-next-line no-undefined
_webpack.ModuleFilenameHelpers.matchObject.bind(undefined, this.options)(assetName));
if (assetNames.length === 0) {
return Promise.resolve();
}
const scheduledTasks = [];
const cache = new CacheEngine(compilation, {
cache: this.options.cache
}, weakCache);
for (const name of assetNames) {
scheduledTasks.push((async () => {
const {
source: inputSource,
info
} = CompressionPlugin.getAsset(compilation, name);
if (info.compressed) {
return;
}
let relatedName;
if (typeof this.options.algorithm === "function") {
let filenameForRelatedName = this.options.filename;
const index = filenameForRelatedName.lastIndexOf("?");
if (index >= 0) {
filenameForRelatedName = filenameForRelatedName.substr(0, index);
}
relatedName = `${_path.default.extname(filenameForRelatedName).slice(1)}ed`;
} else if (this.options.algorithm === "gzip") {
relatedName = "gzipped";
} else {
relatedName = `${this.options.algorithm}ed`;
}
if (info.related && info.related[relatedName]) {
return;
}
let input = inputSource.source();
if (!Buffer.isBuffer(input)) {
input = Buffer.from(input);
}
if (input.length < this.options.threshold) {
return;
}
const cacheData = {
inputSource
};
if (CompressionPlugin.isWebpack4()) {
cacheData.cacheKeys = {
nodeVersion: process.version,
// eslint-disable-next-line global-require
"compression-webpack-plugin": require("../package.json").version,
algorithm: this.algorithm,
originalAlgorithm: this.options.algorithm,
compressionOptions: this.options.compressionOptions,
name,
contentHash: _crypto.default.createHash("md4").update(input).digest("hex")
};
} else {
cacheData.name = (0, _serializeJavascript.default)({
name,
algorithm: this.options.algorithm,
compressionOptions: this.options.compressionOptions
});
}
let output = await cache.get(cacheData, {
RawSource
});
if (!output) {
try {
output = new RawSource(await this.runCompressionAlgorithm(input));
} catch (error) {
compilation.errors.push(error);
return;
}
cacheData.output = output;
await cache.store(cacheData);
}
if (output.source().length / input.length > this.options.minRatio) {
return;
}
const match = /^([^?#]*)(\?[^#]*)?(#.*)?$/.exec(name);
const [, replacerFile] = match;
const replacerQuery = match[2] || "";
const replacerFragment = match[3] || "";
const replacerExt = _path.default.extname(replacerFile);
const replacerBase = _path.default.basename(replacerFile);
const replacerName = replacerBase.slice(0, replacerBase.length - replacerExt.length);
const replacerPath = replacerFile.slice(0, replacerFile.length - replacerBase.length);
const pathData = {
file: replacerFile,
query: replacerQuery,
fragment: replacerFragment,
path: replacerPath,
base: replacerBase,
name: replacerName,
ext: replacerExt || ""
};
let newFilename = this.options.filename;
if (typeof newFilename === "function") {
newFilename = newFilename(pathData);
}
const newName = newFilename.replace(/\[(file|query|fragment|path|base|name|ext)]/g, (p0, p1) => pathData[p1]);
const newInfo = {
compressed: true
};
if (info.immutable && /(\[name]|\[base]|\[file])/.test(newFilename)) {
newInfo.immutable = true;
}
if (this.options.deleteOriginalAssets) {
if (this.options.deleteOriginalAssets === "keep-source-map") {
// TODO `...` required only for webpack@4
const updatedAssetInfo = { ...info,
related: { ...info.related,
sourceMap: null
}
};
CompressionPlugin.updateAsset(compilation, name, inputSource, updatedAssetInfo);
}
CompressionPlugin.deleteAsset(compilation, name);
} else {
// TODO `...` required only for webpack@4
const newOriginalInfo = { ...info,
related: {
[relatedName]: newName,
...info.related
}
};
CompressionPlugin.updateAsset(compilation, name, inputSource, newOriginalInfo);
}
CompressionPlugin.emitAsset(compilation, newName, output, newInfo);
})());
}
return Promise.all(scheduledTasks);
}
static isWebpack4() {
return _webpack.version[0] === "4";
}
apply(compiler) {
const pluginName = this.constructor.name;
if (CompressionPlugin.isWebpack4()) {
// eslint-disable-next-line global-require
const CacheEngine = require("./Webpack4Cache").default;
const weakCache = new WeakMap();
compiler.hooks.emit.tapPromise({
name: pluginName
}, compilation => // eslint-disable-next-line no-undefined
this.compress(compilation, undefined, CacheEngine, weakCache));
} else {
// eslint-disable-next-line global-require
const CacheEngine = require("./Webpack5Cache").default;
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
// eslint-disable-next-line global-require
const Compilation = require("webpack/lib/Compilation");
compilation.hooks.processAssets.tapPromise({
name: pluginName,
stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER
}, assets => this.compress(compilation, assets, CacheEngine));
compilation.hooks.statsPrinter.tap(pluginName, stats => {
stats.hooks.print.for("asset.info.compressed").tap("compression-webpack-plugin", (compressed, {
green,
formatFlag
}) => // eslint-disable-next-line no-undefined
compressed ? green(formatFlag("compressed")) : undefined);
});
});
}
}
}
var _default = CompressionPlugin;
exports.default = _default;