image-minimizer-webpack-plugin
Version:
Webpack loader and plugin to optimize (compress) images using imagemin
153 lines (120 loc) • 4.55 kB
JavaScript
const path = require("path");
const worker = require("./worker");
const schema = require("./loader-options.json");
const {
isAbsoluteURL
} = require("./utils.js");
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
/** @typedef {import("webpack").Compilation} Compilation */
/**
* @template T
* @typedef {Object} LoaderOptions<T>
* @property {string} [severityError] Allows to choose how errors are displayed.
* @property {import("./index").Minimizer<T> | import("./index").Minimizer<T>[]} [minimizer]
* @property {import("./index").Generator<T>[]} [generator]
*/
/**
* @template T
* @this {import("webpack").LoaderContext<LoaderOptions<T>>}
* @param {Buffer} content
* @returns {Promise<Buffer | undefined>}
*/
async function loader(content) {
// Avoid optimize twice
if (this._module && this._module.buildMeta && this._module.buildMeta.imageMinimizerPluginInfo && (this._module.buildMeta.imageMinimizerPluginInfo.minimized || this._module.buildMeta.imageMinimizerPluginInfo.generated)) {
return content;
} // @ts-ignore
const options = this.getOptions(
/** @type {Schema} */
schema);
const callback = this.async();
const {
generator,
minimizer,
severityError
} = options;
if (!minimizer && !generator) {
callback(new Error("Not configured 'minimizer' or 'generator' options, please setup them"));
return;
}
let transformer = minimizer;
let parsedQuery;
if (this.resourceQuery.length > 0) {
parsedQuery = new URLSearchParams(this.resourceQuery);
if (parsedQuery.has("as")) {
if (!generator) {
callback(new Error("Please specify the 'generator' option to use 'as' query param for generation purposes."));
return;
}
const as = parsedQuery.get("as");
const presets = generator.filter(item => item.preset === as);
if (presets.length > 1) {
callback(new Error("Found several identical pereset names, the 'preset' option should be unique"));
return;
}
if (presets.length === 0) {
callback(new Error(`Can't find '${as}' preset in the 'generator' option`));
return;
}
[transformer] = presets;
}
}
if (!transformer) {
callback(null, content);
return;
}
const isAbsolute = isAbsoluteURL(this.resourcePath);
const filename = isAbsolute ? this.resourcePath : path.relative(this.rootContext, this.resourcePath);
const minifyOptions =
/** @type {import("./index").InternalWorkerOptions<T>} */
{
input: content,
filename,
severityError,
transformer,
generateFilename:
/** @type {Compilation} */
this._compilation.getAssetPath.bind(this._compilation)
};
const output = await worker(minifyOptions);
if (output.errors && output.errors.length > 0) {
output.errors.forEach(error => {
this.emitError(error);
});
callback(null, content);
return;
}
if (output.warnings && output.warnings.length > 0) {
output.warnings.forEach(warning => {
this.emitWarning(warning);
});
} // Change content of the data URI after minimizer
if (this._module && this._module.resourceResolveData && this._module.resourceResolveData.encodedContent) {
const isBase64 = /^base64$/i.test(this._module.resourceResolveData.encoding);
this._module.resourceResolveData.encodedContent = isBase64 ? output.data.toString("base64") : encodeURIComponent(output.data.toString("utf-8")).replace(/[!'()*]/g, character => `%${
/** @type {number} */
character.codePointAt(0).toString(16)}`);
} else {
let query = this.resourceQuery;
if (parsedQuery) {
// Remove query param from the bundle due we need that only for bundle purposes
const stringifiedParsedQuery = parsedQuery.toString();
query = stringifiedParsedQuery.length > 0 ? `?${stringifiedParsedQuery}` : "";
parsedQuery.delete("as");
} // Old approach for `file-loader` and other old loaders
this.resourcePath = isAbsolute ? output.filename : path.join(this.rootContext, output.filename);
this.resourceQuery = query; // Change name of assets modules after generator
if (this._module && !this._module.matchResource) {
this._module.matchResource = `${output.filename}${query}`;
}
} // TODO: search better API
if (this._module) {
this._module.buildMeta = { ...this._module.buildMeta,
imageMinimizerPluginInfo: output.info
};
}
callback(null, output.data);
}
loader.raw = true;
module.exports = loader;
;