UNPKG

webpack

Version:

Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

116 lines (95 loc) 3.38 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Alexander Akait @alexander-akait */ "use strict"; const { JAVASCRIPT_MODULE_TYPE_AUTO, JAVASCRIPT_MODULE_TYPE_DYNAMIC, JAVASCRIPT_MODULE_TYPE_ESM } = require("../ModuleTypeConstants"); const EvalUsageWarning = require("../errors/EvalUsageWarning"); const { compareStrings } = require("../util/comparators"); /** @import { PerformanceOptions } from "../../declarations/WebpackOptions" */ /** @import Compiler from "../Compiler" */ /** @import { BuildInfo } from "../Module" */ /** @import JavascriptParser from "../javascript/JavascriptParser" */ const PLUGIN_NAME = "EvalUsagePlugin"; // Enough to name the offenders without listing the graph. const MAX_REPORTED_MODULES = 5; class EvalUsagePlugin { /** * Creates an instance of EvalUsagePlugin. * @param {PerformanceOptions} options the plugin options */ constructor(options) { /** @type {PerformanceOptions["hints"]} */ this.hints = options.hints; } /** * Applies the plugin by registering its hooks on the compiler. * @param {Compiler} compiler the compiler instance * @returns {void} */ apply(compiler) { const hints = this.hints; if (!hints) return; compiler.hooks.compilation.tap( PLUGIN_NAME, (compilation, { normalModuleFactory }) => { /** * @param {JavascriptParser} parser the parser */ const handler = (parser) => { // Only a call of the name itself is a direct eval; `(0, eval)()` and // `globalThis.eval()` run in global scope and reach no local name. parser.hooks.call.for("eval").tap(PLUGIN_NAME, () => { // Kept on the module rather than in a map here, so a module the // filesystem cache restores rather than parses still reports. const buildInfo = /** @type {BuildInfo} */ ( parser.state.module.buildInfo ); buildInfo.usesEval = true; }); }; normalModuleFactory.hooks.parser .for(JAVASCRIPT_MODULE_TYPE_AUTO) .tap(PLUGIN_NAME, handler); normalModuleFactory.hooks.parser .for(JAVASCRIPT_MODULE_TYPE_ESM) .tap(PLUGIN_NAME, handler); normalModuleFactory.hooks.parser .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC) .tap(PLUGIN_NAME, handler); // `afterSeal` is past the hash, which folds every message into it — a // hint reported earlier would change the build's identity. compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => { const { requestShortener } = compilation; /** @type {string[]} */ const callers = []; for (const module of compilation.modules) { // Every built module carries one, so only the field is in question. const buildInfo = /** @type {BuildInfo} */ (module.buildInfo); if (!buildInfo.usesEval) continue; callers.push(module.readableIdentifier(requestShortener)); } if (callers.length === 0) return; // Sorted by name: module order is not stable across runs. callers.sort(compareStrings); const warning = new EvalUsageWarning( callers.slice(0, MAX_REPORTED_MODULES), callers.length ); if (hints === "error") { compilation.errors.push(warning); } else if (hints === "stats") { compilation.hints.push(warning); } else { compilation.warnings.push(warning); } }); } ); } } module.exports = EvalUsagePlugin;