image-minimizer-webpack-plugin
Version:
Webpack loader and plugin to optimize (compress) images using imagemin
266 lines (230 loc) • 7.14 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _path = _interopRequireDefault(require("path"));
var _os = _interopRequireDefault(require("os"));
var _pLimit = _interopRequireDefault(require("p-limit"));
var _schemaUtils = require("schema-utils");
var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
var _minify = _interopRequireDefault(require("./minify"));
var _interpolateName = _interopRequireDefault(require("./utils/interpolate-name"));
var _pluginOptions = _interopRequireDefault(require("./plugin-options.json"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class ImageMinimizerPlugin {
constructor(options = {}) {
(0, _schemaUtils.validate)(_pluginOptions.default, options, {
name: 'Image Minimizer Plugin',
baseDataPath: 'options'
});
const {
filter = () => true,
test = /\.(jpe?g|png|gif|tif|webp|svg|avif)$/i,
include,
exclude,
severityError,
minimizerOptions = {
plugins: []
},
loader = true,
maxConcurrency,
filename = '[path][name][ext]',
deleteOriginalAssets = false
} = options;
this.options = {
severityError,
filter,
exclude,
minimizerOptions,
include,
loader,
maxConcurrency,
test,
filename,
deleteOriginalAssets
};
}
async optimize(compiler, compilation, assets, moduleAssets) {
const cache = compilation.getCache('ImageMinimizerWebpackPlugin');
const assetsForMinify = await Promise.all(Object.keys(assets).filter(name => {
const {
info,
source
} = compilation.getAsset(name); // Skip double minimize assets from child compilation
if (info.minimized) {
return false;
}
if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind( // eslint-disable-next-line no-undefined
undefined, this.options)(name)) {
return false;
} // Exclude already optimized assets from `image-minimizer-webpack-loader`
if (this.options.loader && moduleAssets.has(name)) {
return false;
}
const input = source.source();
if (this.options.filter && !this.options.filter(input, name)) {
return false;
}
return true;
}).map(async name => {
const {
info,
source
} = compilation.getAsset(name);
const cacheName = (0, _serializeJavascript.default)({
name,
minimizerOptions: this.options.minimizerOptions
});
const eTag = cache.getLazyHashedEtag(source);
const cacheItem = cache.getItemCache(cacheName, eTag);
const output = await cacheItem.getPromise();
return {
name,
info,
inputSource: source,
output,
cacheItem
};
}));
const cpus = _os.default.cpus() || {
length: 1
};
const limit = (0, _pLimit.default)(this.options.maxConcurrency || Math.max(1, cpus.length - 1));
const {
RawSource
} = compiler.webpack.sources;
const scheduledTasks = [];
for (const asset of assetsForMinify) {
scheduledTasks.push(limit(async () => {
const {
name,
inputSource,
cacheItem,
info
} = asset;
let {
output
} = asset;
let input;
const sourceFromInputSource = inputSource.source();
if (!output) {
input = sourceFromInputSource;
if (!Buffer.isBuffer(input)) {
input = Buffer.from(input);
}
const {
severityError,
isProductionMode,
minimizerOptions
} = this.options;
const minifyOptions = {
filename: name,
input,
severityError,
isProductionMode,
minimizerOptions
};
output = await (0, _minify.default)(minifyOptions);
if (output.errors.length > 0) {
output.errors.forEach(error => {
compilation.errors.push(error);
});
return;
}
output.source = new RawSource(output.output);
await cacheItem.storePromise({
source: output.source,
warnings: output.warnings
});
}
const {
source,
warnings
} = output;
if (warnings && warnings.length > 0) {
warnings.forEach(warning => {
compilation.warnings.push(warning);
});
}
const newName = (0, _interpolateName.default)(name, this.options.filename);
const isNewAsset = name !== newName;
if (isNewAsset) {
const newInfo = {
related: {
minimized: newName,
...info.related
},
minimized: true
};
compilation.emitAsset(newName, source, newInfo);
if (this.options.deleteOriginalAssets) {
compilation.deleteAsset(name);
}
} else {
const updatedAssetsInfo = {
minimized: true
};
compilation.updateAsset(name, source, updatedAssetsInfo);
}
}));
}
await Promise.all(scheduledTasks);
}
apply(compiler) {
this.options.isProductionMode = compiler.options.mode === 'production' || !compiler.options.mode;
const pluginName = this.constructor.name;
const moduleAssets = new Set();
if (this.options.loader) {
// Collect assets from modules
compiler.hooks.compilation.tap({
name: pluginName
}, compilation => {
compilation.hooks.moduleAsset.tap({
name: pluginName
}, (module, file) => {
moduleAssets.add(file);
});
});
compiler.hooks.afterPlugins.tap({
name: pluginName
}, () => {
const {
filename,
deleteOriginalAssets,
filter,
test,
include,
exclude,
severityError,
minimizerOptions
} = this.options;
const loader = {
test,
include,
exclude,
enforce: 'pre',
loader: _path.default.join(__dirname, 'loader.js'),
options: {
filename,
deleteOriginalAssets,
severityError,
filter,
minimizerOptions
}
};
compiler.options.module.rules.push(loader);
});
}
compiler.hooks.compilation.tap(pluginName, compilation => {
compilation.hooks.processAssets.tapPromise({
name: pluginName,
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE
}, assets => this.optimize(compiler, compilation, assets, moduleAssets));
});
}
}
ImageMinimizerPlugin.loader = require.resolve('./loader');
ImageMinimizerPlugin.normalizeConfig = require('./utils/normalize-config').default;
var _default = ImageMinimizerPlugin;
exports.default = _default;
;