Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F92223150
DefinePlugin.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Nov 18, 12:18
Size
9 KB
Mime Type
text/x-c++
Expires
Wed, Nov 20, 12:18 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
22393412
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
DefinePlugin.js
View Options
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("./RuntimeGlobals");
const ConstDependency = require("./dependencies/ConstDependency");
const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
const {
approve,
evaluateToString,
toConstantDependency
} = require("./javascript/JavascriptParserHelpers");
/** @typedef {import("estree").Expression} Expression */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
/** @typedef {null|undefined|RegExp|Function|string|number|boolean|bigint|undefined} CodeValuePrimitive */
/** @typedef {RecursiveArrayOrRecord<CodeValuePrimitive|RuntimeValue>} CodeValue */
class RuntimeValue {
constructor(fn, fileDependencies) {
this.fn = fn;
this.fileDependencies = fileDependencies || [];
}
exec(parser) {
const buildInfo = parser.state.module.buildInfo;
if (this.fileDependencies === true) {
buildInfo.cacheable = false;
} else {
for (const fileDependency of this.fileDependencies) {
buildInfo.fileDependencies.add(fileDependency);
}
}
return this.fn({ module: parser.state.module });
}
}
/**
* @param {any[]|{[k: string]: any}} obj obj
* @param {JavascriptParser} parser Parser
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded)
* @returns {string} code converted to string that evaluates
*/
const stringifyObj = (obj, parser, runtimeTemplate, asiSafe) => {
let code;
let arr = Array.isArray(obj);
if (arr) {
code = `[${obj
.map(code => toCode(code, parser, runtimeTemplate, null))
.join(",")}]`;
} else {
code = `{${Object.keys(obj)
.map(key => {
const code = obj[key];
return (
JSON.stringify(key) +
":" +
toCode(code, parser, runtimeTemplate, null)
);
})
.join(",")}}`;
}
switch (asiSafe) {
case null:
return code;
case true:
return arr ? code : `(${code})`;
case false:
return arr ? `;${code}` : `;(${code})`;
default:
return `Object(${code})`;
}
};
/**
* Convert code to a string that evaluates
* @param {CodeValue} code Code to evaluate
* @param {JavascriptParser} parser Parser
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded)
* @returns {string} code converted to string that evaluates
*/
const toCode = (code, parser, runtimeTemplate, asiSafe) => {
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), parser, runtimeTemplate, 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, runtimeTemplate, asiSafe);
}
if (typeof code === "bigint") {
return runtimeTemplate.supportsBigIntLiteral()
? `${code}n`
: `BigInt("${code}")`;
}
return code + "";
};
class DefinePlugin {
/**
* Create a new define plugin
* @param {Record<string, CodeValue>} definitions A map of global object definitions
*/
constructor(definitions) {
this.definitions = definitions;
}
static runtimeValue(fn, fileDependencies) {
return new RuntimeValue(fn, fileDependencies);
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
const definitions = this.definitions;
compiler.hooks.compilation.tap(
"DefinePlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
const { runtimeTemplate } = compilation;
/**
* Handler
* @param {JavascriptParser} parser Parser
* @returns {void}
*/
const handler = parser => {
/**
* Walk definitions
* @param {Object} definitions Definitions map
* @param {string} prefix Prefix string
* @returns {void}
*/
const walkDefinitions = (definitions, prefix) => {
Object.keys(definitions).forEach(key => {
const code = definitions[key];
if (
code &&
typeof code === "object" &&
!(code instanceof RuntimeValue) &&
!(code instanceof RegExp)
) {
walkDefinitions(code, prefix + key + ".");
applyObjectDefine(prefix + key, code);
return;
}
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(".");
splittedKey.slice(1).forEach((_, i) => {
const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
parser.hooks.canRename.for(fullKey).tap("DefinePlugin", approve);
});
};
/**
* Apply Code
* @param {string} key Key
* @param {CodeValue} code Code
* @returns {void}
*/
const applyDefine = (key, code) => {
const isTypeof = /^typeof\s+/.test(key);
if (isTypeof) key = key.replace(/^typeof\s+/, "");
let recurse = false;
let recurseTypeof = false;
if (!isTypeof) {
parser.hooks.canRename.for(key).tap("DefinePlugin", approve);
parser.hooks.evaluateIdentifier
.for(key)
.tap("DefinePlugin", 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;
recurse = true;
const res = parser.evaluate(
toCode(code, parser, runtimeTemplate, null)
);
recurse = false;
res.setRange(expr.range);
return res;
});
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
const strCode = toCode(
code,
parser,
runtimeTemplate,
!parser.isAsiPosition(expr.range[0])
);
if (/__webpack_require__\s*(!?\.)/.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.require
])(expr);
} else if (/__webpack_require__/.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.requireScope
])(expr);
} else {
return toConstantDependency(parser, strCode)(expr);
}
});
}
parser.hooks.evaluateTypeof.for(key).tap("DefinePlugin", 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;
const typeofCode = isTypeof
? toCode(code, parser, runtimeTemplate, null)
: "typeof (" +
toCode(code, parser, runtimeTemplate, null) +
")";
const res = parser.evaluate(typeofCode);
recurseTypeof = false;
res.setRange(expr.range);
return res;
});
parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
const typeofCode = isTypeof
? toCode(code, parser, runtimeTemplate, null)
: "typeof (" +
toCode(code, parser, runtimeTemplate, null) +
")";
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("DefinePlugin", approve);
parser.hooks.evaluateIdentifier
.for(key)
.tap("DefinePlugin", expr =>
new BasicEvaluatedExpression()
.setTruthy()
.setSideEffects(false)
.setRange(expr.range)
);
parser.hooks.evaluateTypeof
.for(key)
.tap("DefinePlugin", evaluateToString("object"));
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
const strCode = stringifyObj(
obj,
parser,
runtimeTemplate,
!parser.isAsiPosition(expr.range[0])
);
if (/__webpack_require__\s*(!?\.)/.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.require
])(expr);
} else if (/__webpack_require__/.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.requireScope
])(expr);
} else {
return toConstantDependency(parser, strCode)(expr);
}
});
parser.hooks.typeof
.for(key)
.tap(
"DefinePlugin",
toConstantDependency(parser, JSON.stringify("object"))
);
};
walkDefinitions(definitions, "");
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("DefinePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("DefinePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("DefinePlugin", handler);
}
);
}
}
module.exports = DefinePlugin;
Event Timeline
Log In to Comment