summaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/DefinePlugin.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/webpack/lib/DefinePlugin.js')
-rw-r--r--node_modules/webpack/lib/DefinePlugin.js707
1 files changed, 707 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/DefinePlugin.js b/node_modules/webpack/lib/DefinePlugin.js
new file mode 100644
index 00000000..574d8ca5
--- /dev/null
+++ b/node_modules/webpack/lib/DefinePlugin.js
@@ -0,0 +1,707 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const {
+ JAVASCRIPT_MODULE_TYPE_AUTO,
+ JAVASCRIPT_MODULE_TYPE_ESM,
+ JAVASCRIPT_MODULE_TYPE_DYNAMIC
+} = require("./ModuleTypeConstants");
+const RuntimeGlobals = require("./RuntimeGlobals");
+const WebpackError = require("./WebpackError");
+const ConstDependency = require("./dependencies/ConstDependency");
+const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
+
+const {
+ evaluateToString,
+ toConstantDependency
+} = require("./javascript/JavascriptParserHelpers");
+const createHash = require("./util/createHash");
+
+/** @typedef {import("estree").Expression} Expression */
+/** @typedef {import("./Compilation").ValueCacheVersion} ValueCacheVersion */
+/** @typedef {import("./Compiler")} Compiler */
+/** @typedef {import("./Module").BuildInfo} BuildInfo */
+/** @typedef {import("./NormalModule")} NormalModule */
+/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
+/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
+/** @typedef {import("./javascript/JavascriptParser").DestructuringAssignmentProperty} DestructuringAssignmentProperty */
+/** @typedef {import("./javascript/JavascriptParser").Range} Range */
+/** @typedef {import("./logging/Logger").Logger} Logger */
+/** @typedef {import("./util/createHash").Algorithm} Algorithm */
+
+/** @typedef {null|undefined|RegExp|Function|string|number|boolean|bigint|undefined} CodeValuePrimitive */
+/** @typedef {RecursiveArrayOrRecord<CodeValuePrimitive|RuntimeValue>} CodeValue */
+
+/**
+ * @typedef {object} RuntimeValueOptions
+ * @property {string[]=} fileDependencies
+ * @property {string[]=} contextDependencies
+ * @property {string[]=} missingDependencies
+ * @property {string[]=} buildDependencies
+ * @property {string|function(): string=} version
+ */
+
+/** @typedef {function({ module: NormalModule, key: string, readonly version: ValueCacheVersion }): CodeValuePrimitive} GeneratorFn */
+
+class RuntimeValue {
+ /**
+ * @param {GeneratorFn} fn generator function
+ * @param {true | string[] | RuntimeValueOptions=} options options
+ */
+ constructor(fn, options) {
+ this.fn = fn;
+ if (Array.isArray(options)) {
+ options = {
+ fileDependencies: options
+ };
+ }
+ this.options = options || {};
+ }
+
+ get fileDependencies() {
+ return this.options === true ? true : this.options.fileDependencies;
+ }
+
+ /**
+ * @param {JavascriptParser} parser the parser
+ * @param {Map<string, ValueCacheVersion>} valueCacheVersions valueCacheVersions
+ * @param {string} key the defined key
+ * @returns {CodeValuePrimitive} code
+ */
+ exec(parser, valueCacheVersions, key) {
+ const buildInfo = /** @type {BuildInfo} */ (parser.state.module.buildInfo);
+ if (this.options === true) {
+ buildInfo.cacheable = false;
+ } else {
+ if (this.options.fileDependencies) {
+ for (const dep of this.options.fileDependencies) {
+ /** @type {NonNullable<BuildInfo["fileDependencies"]>} */
+ (buildInfo.fileDependencies).add(dep);
+ }
+ }
+ if (this.options.contextDependencies) {
+ for (const dep of this.options.contextDependencies) {
+ /** @type {NonNullable<BuildInfo["contextDependencies"]>} */
+ (buildInfo.contextDependencies).add(dep);
+ }
+ }
+ if (this.options.missingDependencies) {
+ for (const dep of this.options.missingDependencies) {
+ /** @type {NonNullable<BuildInfo["missingDependencies"]>} */
+ (buildInfo.missingDependencies).add(dep);
+ }
+ }
+ if (this.options.buildDependencies) {
+ for (const dep of this.options.buildDependencies) {
+ /** @type {NonNullable<BuildInfo["buildDependencies"]>} */
+ (buildInfo.buildDependencies).add(dep);
+ }
+ }
+ }
+
+ return this.fn({
+ module: parser.state.module,
+ key,
+ get version() {
+ return valueCacheVersions.get(VALUE_DEP_PREFIX + key);
+ }
+ });
+ }
+
+ getCacheVersion() {
+ return this.options === true
+ ? undefined
+ : (typeof this.options.version === "function"
+ ? this.options.version()
+ : this.options.version) || "unset";
+ }
+}
+
+/**
+ * @param {Set<DestructuringAssignmentProperty> | undefined} properties properties
+ * @returns {Set<string> | undefined} used keys
+ */
+function getObjKeys(properties) {
+ if (!properties) return;
+ return new Set([...properties].map(p => p.id));
+}
+
+/** @typedef {Set<string> | null} ObjKeys */
+/** @typedef {boolean | undefined | null} AsiSafe */
+
+/**
+ * @param {any[]|{[k: string]: any}} obj obj
+ * @param {JavascriptParser} parser Parser
+ * @param {Map<string, ValueCacheVersion>} valueCacheVersions valueCacheVersions
+ * @param {string} key the defined key
+ * @param {RuntimeTemplate} runtimeTemplate the runtime template
+ * @param {Logger} logger the logger object
+ * @param {AsiSafe=} asiSafe asi safe (undefined: unknown, null: unneeded)
+ * @param {ObjKeys=} objKeys used keys
+ * @returns {string} code converted to string that evaluates
+ */
+const stringifyObj = (
+ obj,
+ parser,
+ valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ asiSafe,
+ objKeys
+) => {
+ let code;
+ const arr = Array.isArray(obj);
+ if (arr) {
+ code = `[${
+ /** @type {any[]} */ (obj)
+ .map(code =>
+ toCode(
+ code,
+ parser,
+ valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ null
+ )
+ )
+ .join(",")
+ }]`;
+ } else {
+ let keys = Object.keys(obj);
+ if (objKeys) {
+ keys = objKeys.size === 0 ? [] : keys.filter(k => objKeys.has(k));
+ }
+ code = `{${keys
+ .map(key => {
+ const code = /** @type {{[k: string]: any}} */ (obj)[key];
+ return `${JSON.stringify(key)}:${toCode(
+ code,
+ parser,
+ valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ null
+ )}`;
+ })
+ .join(",")}}`;
+ }
+
+ switch (asiSafe) {
+ case null:
+ return code;
+ case true:
+ return arr ? code : `(${code})`;
+ case false:
+ return arr ? `;${code}` : `;(${code})`;
+ default:
+ return `/*#__PURE__*/Object(${code})`;
+ }
+};
+
+/**
+ * Convert code to a string that evaluates
+ * @param {CodeValue} code Code to evaluate
+ * @param {JavascriptParser} parser Parser
+ * @param {Map<string, ValueCacheVersion>} valueCacheVersions valueCacheVersions
+ * @param {string} key the defined key
+ * @param {RuntimeTemplate} runtimeTemplate the runtime template
+ * @param {Logger} logger the logger object
+ * @param {boolean | undefined | null=} asiSafe asi safe (undefined: unknown, null: unneeded)
+ * @param {ObjKeys=} objKeys used keys
+ * @returns {string} code converted to string that evaluates
+ */
+const toCode = (
+ code,
+ parser,
+ valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ asiSafe,
+ objKeys
+) => {
+ const transformToCode = () => {
+ if (code === null) {
+ return "null";
+ }
+ if (code === undefined) {
+ return "undefined";
+ }
+ if (Object.is(code, -0)) {
+ return "-0";
+ }
+ if (code instanceof RuntimeValue) {
+ return toCode(
+ code.exec(parser, valueCacheVersions, key),
+ parser,
+ valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ asiSafe
+ );
+ }
+ if (code instanceof RegExp && code.toString) {
+ return code.toString();
+ }
+ if (typeof code === "function" && code.toString) {
+ return `(${code.toString()})`;
+ }
+ if (typeof code === "object") {
+ return stringifyObj(
+ code,
+ parser,
+ valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ asiSafe,
+ objKeys
+ );
+ }
+ if (typeof code === "bigint") {
+ return runtimeTemplate.supportsBigIntLiteral()
+ ? `${code}n`
+ : `BigInt("${code}")`;
+ }
+ return `${code}`;
+ };
+
+ const strCode = transformToCode();
+
+ logger.debug(`Replaced "${key}" with "${strCode}"`);
+
+ return strCode;
+};
+
+/**
+ * @param {CodeValue} code code
+ * @returns {string | undefined} result
+ */
+const toCacheVersion = code => {
+ if (code === null) {
+ return "null";
+ }
+ if (code === undefined) {
+ return "undefined";
+ }
+ if (Object.is(code, -0)) {
+ return "-0";
+ }
+ if (code instanceof RuntimeValue) {
+ return code.getCacheVersion();
+ }
+ if (code instanceof RegExp && code.toString) {
+ return code.toString();
+ }
+ if (typeof code === "function" && code.toString) {
+ return `(${code.toString()})`;
+ }
+ if (typeof code === "object") {
+ const items = Object.keys(code).map(key => ({
+ key,
+ value: toCacheVersion(/** @type {Record<string, any>} */ (code)[key])
+ }));
+ if (items.some(({ value }) => value === undefined)) return;
+ return `{${items.map(({ key, value }) => `${key}: ${value}`).join(", ")}}`;
+ }
+ if (typeof code === "bigint") {
+ return `${code}n`;
+ }
+ return `${code}`;
+};
+
+const PLUGIN_NAME = "DefinePlugin";
+const VALUE_DEP_PREFIX = `webpack/${PLUGIN_NAME} `;
+const VALUE_DEP_MAIN = `webpack/${PLUGIN_NAME}_hash`;
+const TYPEOF_OPERATOR_REGEXP = /^typeof\s+/;
+const WEBPACK_REQUIRE_FUNCTION_REGEXP = new RegExp(
+ `${RuntimeGlobals.require}\\s*(!?\\.)`
+);
+const WEBPACK_REQUIRE_IDENTIFIER_REGEXP = new RegExp(RuntimeGlobals.require);
+
+class DefinePlugin {
+ /**
+ * Create a new define plugin
+ * @param {Record<string, CodeValue>} definitions A map of global object definitions
+ */
+ constructor(definitions) {
+ this.definitions = definitions;
+ }
+
+ /**
+ * @param {GeneratorFn} fn generator function
+ * @param {true | string[] | RuntimeValueOptions=} options options
+ * @returns {RuntimeValue} runtime value
+ */
+ static runtimeValue(fn, options) {
+ return new RuntimeValue(fn, options);
+ }
+
+ /**
+ * Apply the plugin
+ * @param {Compiler} compiler the compiler instance
+ * @returns {void}
+ */
+ apply(compiler) {
+ const definitions = this.definitions;
+ compiler.hooks.compilation.tap(
+ PLUGIN_NAME,
+ (compilation, { normalModuleFactory }) => {
+ const logger = compilation.getLogger("webpack.DefinePlugin");
+ compilation.dependencyTemplates.set(
+ ConstDependency,
+ new ConstDependency.Template()
+ );
+ const { runtimeTemplate } = compilation;
+
+ const mainHash = createHash(
+ /** @type {Algorithm} */
+ (compilation.outputOptions.hashFunction)
+ );
+ mainHash.update(
+ /** @type {string} */
+ (compilation.valueCacheVersions.get(VALUE_DEP_MAIN)) || ""
+ );
+
+ /**
+ * Handler
+ * @param {JavascriptParser} parser Parser
+ * @returns {void}
+ */
+ const handler = parser => {
+ const mainValue = compilation.valueCacheVersions.get(VALUE_DEP_MAIN);
+ parser.hooks.program.tap(PLUGIN_NAME, () => {
+ const buildInfo = /** @type {BuildInfo} */ (
+ parser.state.module.buildInfo
+ );
+ if (!buildInfo.valueDependencies)
+ buildInfo.valueDependencies = new Map();
+ buildInfo.valueDependencies.set(VALUE_DEP_MAIN, mainValue);
+ });
+
+ /**
+ * @param {string} key key
+ */
+ const addValueDependency = key => {
+ const buildInfo =
+ /** @type {BuildInfo} */
+ (parser.state.module.buildInfo);
+ /** @type {NonNullable<BuildInfo["valueDependencies"]>} */
+ (buildInfo.valueDependencies).set(
+ VALUE_DEP_PREFIX + key,
+ compilation.valueCacheVersions.get(VALUE_DEP_PREFIX + key)
+ );
+ };
+
+ /**
+ * @template {Function} T
+ * @param {string} key key
+ * @param {T} fn fn
+ * @returns {function(TODO): TODO} result
+ */
+ const withValueDependency =
+ (key, fn) =>
+ (...args) => {
+ addValueDependency(key);
+ return fn(...args);
+ };
+
+ /**
+ * Walk definitions
+ * @param {Record<string, CodeValue>} definitions Definitions map
+ * @param {string} prefix Prefix string
+ * @returns {void}
+ */
+ const walkDefinitions = (definitions, prefix) => {
+ for (const key of Object.keys(definitions)) {
+ const code = definitions[key];
+ if (
+ code &&
+ typeof code === "object" &&
+ !(code instanceof RuntimeValue) &&
+ !(code instanceof RegExp)
+ ) {
+ walkDefinitions(
+ /** @type {Record<string, CodeValue>} */ (code),
+ `${prefix + key}.`
+ );
+ applyObjectDefine(prefix + key, code);
+ continue;
+ }
+ applyDefineKey(prefix, key);
+ applyDefine(prefix + key, code);
+ }
+ };
+
+ /**
+ * Apply define key
+ * @param {string} prefix Prefix
+ * @param {string} key Key
+ * @returns {void}
+ */
+ const applyDefineKey = (prefix, key) => {
+ const splittedKey = key.split(".");
+ for (const [i, _] of splittedKey.slice(1).entries()) {
+ const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
+ parser.hooks.canRename.for(fullKey).tap(PLUGIN_NAME, () => {
+ addValueDependency(key);
+ return true;
+ });
+ }
+ };
+
+ /**
+ * Apply Code
+ * @param {string} key Key
+ * @param {CodeValue} code Code
+ * @returns {void}
+ */
+ const applyDefine = (key, code) => {
+ const originalKey = key;
+ const isTypeof = TYPEOF_OPERATOR_REGEXP.test(key);
+ if (isTypeof) key = key.replace(TYPEOF_OPERATOR_REGEXP, "");
+ let recurse = false;
+ let recurseTypeof = false;
+ if (!isTypeof) {
+ parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
+ addValueDependency(originalKey);
+ return true;
+ });
+ parser.hooks.evaluateIdentifier
+ .for(key)
+ .tap(PLUGIN_NAME, expr => {
+ /**
+ * this is needed in case there is a recursion in the DefinePlugin
+ * to prevent an endless recursion
+ * e.g.: new DefinePlugin({
+ * "a": "b",
+ * "b": "a"
+ * });
+ */
+ if (recurse) return;
+ addValueDependency(originalKey);
+ recurse = true;
+ const res = parser.evaluate(
+ toCode(
+ code,
+ parser,
+ compilation.valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ null
+ )
+ );
+ recurse = false;
+ res.setRange(/** @type {Range} */ (expr.range));
+ return res;
+ });
+ parser.hooks.expression.for(key).tap(PLUGIN_NAME, expr => {
+ addValueDependency(originalKey);
+ let strCode = toCode(
+ code,
+ parser,
+ compilation.valueCacheVersions,
+ originalKey,
+ runtimeTemplate,
+ logger,
+ !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
+ null
+ );
+
+ if (parser.scope.inShorthand) {
+ strCode = `${parser.scope.inShorthand}:${strCode}`;
+ }
+
+ if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
+ return toConstantDependency(parser, strCode, [
+ RuntimeGlobals.require
+ ])(expr);
+ } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
+ return toConstantDependency(parser, strCode, [
+ RuntimeGlobals.requireScope
+ ])(expr);
+ }
+ return toConstantDependency(parser, strCode)(expr);
+ });
+ }
+ parser.hooks.evaluateTypeof.for(key).tap(PLUGIN_NAME, expr => {
+ /**
+ * this is needed in case there is a recursion in the DefinePlugin
+ * to prevent an endless recursion
+ * e.g.: new DefinePlugin({
+ * "typeof a": "typeof b",
+ * "typeof b": "typeof a"
+ * });
+ */
+ if (recurseTypeof) return;
+ recurseTypeof = true;
+ addValueDependency(originalKey);
+ const codeCode = toCode(
+ code,
+ parser,
+ compilation.valueCacheVersions,
+ originalKey,
+ runtimeTemplate,
+ logger,
+ null
+ );
+ const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
+ const res = parser.evaluate(typeofCode);
+ recurseTypeof = false;
+ res.setRange(/** @type {Range} */ (expr.range));
+ return res;
+ });
+ parser.hooks.typeof.for(key).tap(PLUGIN_NAME, expr => {
+ addValueDependency(originalKey);
+ const codeCode = toCode(
+ code,
+ parser,
+ compilation.valueCacheVersions,
+ originalKey,
+ runtimeTemplate,
+ logger,
+ null
+ );
+ const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
+ const res = parser.evaluate(typeofCode);
+ if (!res.isString()) return;
+ return toConstantDependency(
+ parser,
+ JSON.stringify(res.string)
+ ).bind(parser)(expr);
+ });
+ };
+
+ /**
+ * Apply Object
+ * @param {string} key Key
+ * @param {object} obj Object
+ * @returns {void}
+ */
+ const applyObjectDefine = (key, obj) => {
+ parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
+ addValueDependency(key);
+ return true;
+ });
+ parser.hooks.evaluateIdentifier.for(key).tap(PLUGIN_NAME, expr => {
+ addValueDependency(key);
+ return new BasicEvaluatedExpression()
+ .setTruthy()
+ .setSideEffects(false)
+ .setRange(/** @type {Range} */ (expr.range));
+ });
+ parser.hooks.evaluateTypeof
+ .for(key)
+ .tap(
+ PLUGIN_NAME,
+ withValueDependency(key, evaluateToString("object"))
+ );
+ parser.hooks.expression.for(key).tap(PLUGIN_NAME, expr => {
+ addValueDependency(key);
+ let strCode = stringifyObj(
+ obj,
+ parser,
+ compilation.valueCacheVersions,
+ key,
+ runtimeTemplate,
+ logger,
+ !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
+ getObjKeys(parser.destructuringAssignmentPropertiesFor(expr))
+ );
+
+ if (parser.scope.inShorthand) {
+ strCode = `${parser.scope.inShorthand}:${strCode}`;
+ }
+
+ if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
+ return toConstantDependency(parser, strCode, [
+ RuntimeGlobals.require
+ ])(expr);
+ } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
+ return toConstantDependency(parser, strCode, [
+ RuntimeGlobals.requireScope
+ ])(expr);
+ }
+ return toConstantDependency(parser, strCode)(expr);
+ });
+ parser.hooks.typeof
+ .for(key)
+ .tap(
+ PLUGIN_NAME,
+ withValueDependency(
+ key,
+ toConstantDependency(parser, JSON.stringify("object"))
+ )
+ );
+ };
+
+ walkDefinitions(definitions, "");
+ };
+
+ normalModuleFactory.hooks.parser
+ .for(JAVASCRIPT_MODULE_TYPE_AUTO)
+ .tap(PLUGIN_NAME, handler);
+ normalModuleFactory.hooks.parser
+ .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
+ .tap(PLUGIN_NAME, handler);
+ normalModuleFactory.hooks.parser
+ .for(JAVASCRIPT_MODULE_TYPE_ESM)
+ .tap(PLUGIN_NAME, handler);
+
+ /**
+ * Walk definitions
+ * @param {Record<string, CodeValue>} definitions Definitions map
+ * @param {string} prefix Prefix string
+ * @returns {void}
+ */
+ const walkDefinitionsForValues = (definitions, prefix) => {
+ for (const key of Object.keys(definitions)) {
+ const code = definitions[key];
+ const version = toCacheVersion(code);
+ const name = VALUE_DEP_PREFIX + prefix + key;
+ mainHash.update(`|${prefix}${key}`);
+ const oldVersion = compilation.valueCacheVersions.get(name);
+ if (oldVersion === undefined) {
+ compilation.valueCacheVersions.set(name, version);
+ } else if (oldVersion !== version) {
+ const warning = new WebpackError(
+ `${PLUGIN_NAME}\nConflicting values for '${prefix + key}'`
+ );
+ warning.details = `'${oldVersion}' !== '${version}'`;
+ warning.hideStack = true;
+ compilation.warnings.push(warning);
+ }
+ if (
+ code &&
+ typeof code === "object" &&
+ !(code instanceof RuntimeValue) &&
+ !(code instanceof RegExp)
+ ) {
+ walkDefinitionsForValues(
+ /** @type {Record<string, CodeValue>} */ (code),
+ `${prefix + key}.`
+ );
+ }
+ }
+ };
+
+ walkDefinitionsForValues(definitions, "");
+
+ compilation.valueCacheVersions.set(
+ VALUE_DEP_MAIN,
+ /** @type {string} */ (mainHash.digest("hex").slice(0, 8))
+ );
+ }
+ );
+ }
+}
+module.exports = DefinePlugin;