123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const RuntimeGlobals = require("../RuntimeGlobals");
- const AMDDefineDependency = require("./AMDDefineDependency");
- const AMDRequireArrayDependency = require("./AMDRequireArrayDependency");
- const AMDRequireContextDependency = require("./AMDRequireContextDependency");
- const AMDRequireItemDependency = require("./AMDRequireItemDependency");
- const ConstDependency = require("./ConstDependency");
- const ContextDependencyHelpers = require("./ContextDependencyHelpers");
- const DynamicExports = require("./DynamicExports");
- const LocalModuleDependency = require("./LocalModuleDependency");
- const { addLocalModule, getLocalModule } = require("./LocalModulesHelpers");
- /** @typedef {import("estree").ArrowFunctionExpression} ArrowFunctionExpression */
- /** @typedef {import("estree").CallExpression} CallExpression */
- /** @typedef {import("estree").Expression} Expression */
- /** @typedef {import("estree").FunctionExpression} FunctionExpression */
- /** @typedef {import("estree").Literal} Literal */
- /** @typedef {import("estree").SpreadElement} SpreadElement */
- /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
- /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
- /** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
- /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
- /** @typedef {import("../javascript/JavascriptParser").Range} Range */
- /**
- * @param {Expression | SpreadElement} expr expression
- * @returns {boolean} true if it's a bound function expression
- */
- const isBoundFunctionExpression = expr => {
- if (expr.type !== "CallExpression") return false;
- if (expr.callee.type !== "MemberExpression") return false;
- if (expr.callee.computed) return false;
- if (expr.callee.object.type !== "FunctionExpression") return false;
- if (expr.callee.property.type !== "Identifier") return false;
- if (expr.callee.property.name !== "bind") return false;
- return true;
- };
- /** @typedef {FunctionExpression | ArrowFunctionExpression} UnboundFunctionExpression */
- /**
- * @param {Expression | SpreadElement} expr expression
- * @returns {boolean} true when unbound function expression
- */
- const isUnboundFunctionExpression = expr => {
- if (expr.type === "FunctionExpression") return true;
- if (expr.type === "ArrowFunctionExpression") return true;
- return false;
- };
- /**
- * @param {Expression | SpreadElement} expr expression
- * @returns {boolean} true when callable
- */
- const isCallable = expr => {
- if (isUnboundFunctionExpression(expr)) return true;
- if (isBoundFunctionExpression(expr)) return true;
- return false;
- };
- class AMDDefineDependencyParserPlugin {
- /**
- * @param {JavascriptParserOptions} options parserOptions
- */
- constructor(options) {
- this.options = options;
- }
- /**
- * @param {JavascriptParser} parser the parser
- * @returns {void}
- */
- apply(parser) {
- parser.hooks.call
- .for("define")
- .tap(
- "AMDDefineDependencyParserPlugin",
- this.processCallDefine.bind(this, parser)
- );
- }
- /**
- * @param {JavascriptParser} parser the parser
- * @param {CallExpression} expr call expression
- * @param {BasicEvaluatedExpression} param param
- * @param {Record<number, string>} identifiers identifiers
- * @param {string=} namedModule named module
- * @returns {boolean | undefined} result
- */
- processArray(parser, expr, param, identifiers, namedModule) {
- if (param.isArray()) {
- /** @type {BasicEvaluatedExpression[]} */
- (param.items).forEach((param, idx) => {
- if (
- param.isString() &&
- ["require", "module", "exports"].includes(
- /** @type {string} */ (param.string)
- )
- )
- identifiers[/** @type {number} */ (idx)] = param.string;
- const result = this.processItem(parser, expr, param, namedModule);
- if (result === undefined) {
- this.processContext(parser, expr, param);
- }
- });
- return true;
- } else if (param.isConstArray()) {
- /** @type {(string | LocalModuleDependency | AMDRequireItemDependency)[]} */
- const deps = [];
- /** @type {string[]} */
- (param.array).forEach((request, idx) => {
- let dep;
- let localModule;
- if (request === "require") {
- identifiers[idx] = request;
- dep = RuntimeGlobals.require;
- } else if (["exports", "module"].includes(request)) {
- identifiers[idx] = request;
- dep = request;
- } else if ((localModule = getLocalModule(parser.state, request))) {
- localModule.flagUsed();
- dep = new LocalModuleDependency(localModule, undefined, false);
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- parser.state.module.addPresentationalDependency(dep);
- } else {
- dep = this.newRequireItemDependency(request);
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- dep.optional = !!parser.scope.inTry;
- parser.state.current.addDependency(dep);
- }
- deps.push(dep);
- });
- const dep = this.newRequireArrayDependency(
- deps,
- /** @type {Range} */ (param.range)
- );
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- dep.optional = !!parser.scope.inTry;
- parser.state.module.addPresentationalDependency(dep);
- return true;
- }
- }
- /**
- * @param {JavascriptParser} parser the parser
- * @param {CallExpression} expr call expression
- * @param {BasicEvaluatedExpression} param param
- * @param {string=} namedModule named module
- * @returns {boolean | undefined} result
- */
- processItem(parser, expr, param, namedModule) {
- if (param.isConditional()) {
- /** @type {BasicEvaluatedExpression[]} */
- (param.options).forEach(param => {
- const result = this.processItem(parser, expr, param);
- if (result === undefined) {
- this.processContext(parser, expr, param);
- }
- });
- return true;
- } else if (param.isString()) {
- let dep, localModule;
- if (param.string === "require") {
- dep = new ConstDependency(
- RuntimeGlobals.require,
- /** @type {Range} */ (param.range),
- [RuntimeGlobals.require]
- );
- } else if (param.string === "exports") {
- dep = new ConstDependency(
- "exports",
- /** @type {Range} */ (param.range),
- [RuntimeGlobals.exports]
- );
- } else if (param.string === "module") {
- dep = new ConstDependency(
- "module",
- /** @type {Range} */ (param.range),
- [RuntimeGlobals.module]
- );
- } else if (
- (localModule = getLocalModule(
- parser.state,
- /** @type {string} */ (param.string),
- namedModule
- ))
- ) {
- localModule.flagUsed();
- dep = new LocalModuleDependency(localModule, param.range, false);
- } else {
- dep = this.newRequireItemDependency(
- /** @type {string} */ (param.string),
- param.range
- );
- dep.optional = !!parser.scope.inTry;
- parser.state.current.addDependency(dep);
- return true;
- }
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- }
- }
- /**
- * @param {JavascriptParser} parser the parser
- * @param {CallExpression} expr call expression
- * @param {BasicEvaluatedExpression} param param
- * @returns {boolean | undefined} result
- */
- processContext(parser, expr, param) {
- const dep = ContextDependencyHelpers.create(
- AMDRequireContextDependency,
- /** @type {Range} */ (param.range),
- param,
- expr,
- this.options,
- {
- category: "amd"
- },
- parser
- );
- if (!dep) return;
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- dep.optional = !!parser.scope.inTry;
- parser.state.current.addDependency(dep);
- return true;
- }
- /**
- * @param {JavascriptParser} parser the parser
- * @param {CallExpression} expr call expression
- * @returns {boolean | undefined} result
- */
- processCallDefine(parser, expr) {
- let array, fn, obj, namedModule;
- switch (expr.arguments.length) {
- case 1:
- if (isCallable(expr.arguments[0])) {
- // define(f() {…})
- fn = expr.arguments[0];
- } else if (expr.arguments[0].type === "ObjectExpression") {
- // define({…})
- obj = expr.arguments[0];
- } else {
- // define(expr)
- // unclear if function or object
- obj = fn = expr.arguments[0];
- }
- break;
- case 2:
- if (expr.arguments[0].type === "Literal") {
- namedModule = expr.arguments[0].value;
- // define("…", …)
- if (isCallable(expr.arguments[1])) {
- // define("…", f() {…})
- fn = expr.arguments[1];
- } else if (expr.arguments[1].type === "ObjectExpression") {
- // define("…", {…})
- obj = expr.arguments[1];
- } else {
- // define("…", expr)
- // unclear if function or object
- obj = fn = expr.arguments[1];
- }
- } else {
- array = expr.arguments[0];
- if (isCallable(expr.arguments[1])) {
- // define([…], f() {})
- fn = expr.arguments[1];
- } else if (expr.arguments[1].type === "ObjectExpression") {
- // define([…], {…})
- obj = expr.arguments[1];
- } else {
- // define([…], expr)
- // unclear if function or object
- obj = fn = expr.arguments[1];
- }
- }
- break;
- case 3:
- // define("…", […], f() {…})
- namedModule = /** @type {TODO} */ (expr).arguments[0].value;
- array = expr.arguments[1];
- if (isCallable(expr.arguments[2])) {
- // define("…", […], f() {})
- fn = expr.arguments[2];
- } else if (expr.arguments[2].type === "ObjectExpression") {
- // define("…", […], {…})
- obj = expr.arguments[2];
- } else {
- // define("…", […], expr)
- // unclear if function or object
- obj = fn = expr.arguments[2];
- }
- break;
- default:
- return;
- }
- DynamicExports.bailout(parser.state);
- let fnParams = null;
- let fnParamsOffset = 0;
- if (fn) {
- if (isUnboundFunctionExpression(fn)) {
- fnParams = /** @type {UnboundFunctionExpression} */ (fn).params;
- } else if (isBoundFunctionExpression(fn)) {
- fnParams = /** @type {TODO} */ (fn).callee.object.params;
- fnParamsOffset = /** @type {TODO} */ (fn).arguments.length - 1;
- if (fnParamsOffset < 0) {
- fnParamsOffset = 0;
- }
- }
- }
- let fnRenames = new Map();
- if (array) {
- /** @type {Record<number, string>} */
- const identifiers = {};
- const param = parser.evaluateExpression(array);
- const result = this.processArray(
- parser,
- expr,
- param,
- identifiers,
- namedModule
- );
- if (!result) return;
- if (fnParams) {
- fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
- if (identifiers[idx]) {
- fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
- return false;
- }
- return true;
- });
- }
- } else {
- const identifiers = ["require", "exports", "module"];
- if (fnParams) {
- fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
- if (identifiers[idx]) {
- fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
- return false;
- }
- return true;
- });
- }
- }
- /** @type {boolean | undefined} */
- let inTry;
- if (fn && isUnboundFunctionExpression(fn)) {
- inTry = parser.scope.inTry;
- parser.inScope(fnParams, () => {
- for (const [name, varInfo] of fnRenames) {
- parser.setVariable(name, varInfo);
- }
- parser.scope.inTry = /** @type {boolean} */ (inTry);
- if (fn.body.type === "BlockStatement") {
- parser.detectMode(fn.body.body);
- const prev = parser.prevStatement;
- parser.preWalkStatement(fn.body);
- parser.prevStatement = prev;
- parser.walkStatement(fn.body);
- } else {
- parser.walkExpression(fn.body);
- }
- });
- } else if (fn && isBoundFunctionExpression(fn)) {
- inTry = parser.scope.inTry;
- parser.inScope(
- /** @type {TODO} */
- (fn).callee.object.params.filter(
- i => !["require", "module", "exports"].includes(i.name)
- ),
- () => {
- for (const [name, varInfo] of fnRenames) {
- parser.setVariable(name, varInfo);
- }
- parser.scope.inTry = /** @type {boolean} */ (inTry);
- if (fn.callee.object.body.type === "BlockStatement") {
- parser.detectMode(fn.callee.object.body.body);
- const prev = parser.prevStatement;
- parser.preWalkStatement(fn.callee.object.body);
- parser.prevStatement = prev;
- parser.walkStatement(fn.callee.object.body);
- } else {
- parser.walkExpression(fn.callee.object.body);
- }
- }
- );
- if (/** @type {TODO} */ (fn).arguments) {
- parser.walkExpressions(/** @type {TODO} */ (fn).arguments);
- }
- } else if (fn || obj) {
- parser.walkExpression(fn || obj);
- }
- const dep = this.newDefineDependency(
- /** @type {Range} */ (expr.range),
- array ? /** @type {Range} */ (array.range) : null,
- fn ? /** @type {Range} */ (fn.range) : null,
- obj ? /** @type {Range} */ (obj.range) : null,
- namedModule ? namedModule : null
- );
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- if (namedModule) {
- dep.localModule = addLocalModule(parser.state, namedModule);
- }
- parser.state.module.addPresentationalDependency(dep);
- return true;
- }
- /**
- * @param {Range} range range
- * @param {Range | null} arrayRange array range
- * @param {Range | null} functionRange function range
- * @param {Range | null} objectRange object range
- * @param {boolean | null} namedModule true, when define is called with a name
- * @returns {AMDDefineDependency} AMDDefineDependency
- */
- newDefineDependency(
- range,
- arrayRange,
- functionRange,
- objectRange,
- namedModule
- ) {
- return new AMDDefineDependency(
- range,
- arrayRange,
- functionRange,
- objectRange,
- namedModule
- );
- }
- /**
- * @param {TODO[]} depsArray deps array
- * @param {Range} range range
- * @returns {AMDRequireArrayDependency} AMDRequireArrayDependency
- */
- newRequireArrayDependency(depsArray, range) {
- return new AMDRequireArrayDependency(depsArray, range);
- }
- /**
- * @param {string} request request
- * @param {Range=} range range
- * @returns {AMDRequireItemDependency} AMDRequireItemDependency
- */
- newRequireItemDependency(request, range) {
- return new AMDRequireItemDependency(request, range);
- }
- }
- module.exports = AMDDefineDependencyParserPlugin;
|