AMDDefineDependencyParserPlugin.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RuntimeGlobals = require("../RuntimeGlobals");
  7. const AMDDefineDependency = require("./AMDDefineDependency");
  8. const AMDRequireArrayDependency = require("./AMDRequireArrayDependency");
  9. const AMDRequireContextDependency = require("./AMDRequireContextDependency");
  10. const AMDRequireItemDependency = require("./AMDRequireItemDependency");
  11. const ConstDependency = require("./ConstDependency");
  12. const ContextDependencyHelpers = require("./ContextDependencyHelpers");
  13. const DynamicExports = require("./DynamicExports");
  14. const LocalModuleDependency = require("./LocalModuleDependency");
  15. const { addLocalModule, getLocalModule } = require("./LocalModulesHelpers");
  16. const isBoundFunctionExpression = expr => {
  17. if (expr.type !== "CallExpression") return false;
  18. if (expr.callee.type !== "MemberExpression") return false;
  19. if (expr.callee.computed) return false;
  20. if (expr.callee.object.type !== "FunctionExpression") return false;
  21. if (expr.callee.property.type !== "Identifier") return false;
  22. if (expr.callee.property.name !== "bind") return false;
  23. return true;
  24. };
  25. const isUnboundFunctionExpression = expr => {
  26. if (expr.type === "FunctionExpression") return true;
  27. if (expr.type === "ArrowFunctionExpression") return true;
  28. return false;
  29. };
  30. const isCallable = expr => {
  31. if (isUnboundFunctionExpression(expr)) return true;
  32. if (isBoundFunctionExpression(expr)) return true;
  33. return false;
  34. };
  35. class AMDDefineDependencyParserPlugin {
  36. constructor(options) {
  37. this.options = options;
  38. }
  39. apply(parser) {
  40. parser.hooks.call
  41. .for("define")
  42. .tap(
  43. "AMDDefineDependencyParserPlugin",
  44. this.processCallDefine.bind(this, parser)
  45. );
  46. }
  47. processArray(parser, expr, param, identifiers, namedModule) {
  48. if (param.isArray()) {
  49. param.items.forEach((param, idx) => {
  50. if (
  51. param.isString() &&
  52. ["require", "module", "exports"].includes(param.string)
  53. )
  54. identifiers[idx] = param.string;
  55. const result = this.processItem(parser, expr, param, namedModule);
  56. if (result === undefined) {
  57. this.processContext(parser, expr, param);
  58. }
  59. });
  60. return true;
  61. } else if (param.isConstArray()) {
  62. const deps = [];
  63. param.array.forEach((request, idx) => {
  64. let dep;
  65. let localModule;
  66. if (request === "require") {
  67. identifiers[idx] = request;
  68. dep = "__webpack_require__";
  69. } else if (["exports", "module"].includes(request)) {
  70. identifiers[idx] = request;
  71. dep = request;
  72. } else if ((localModule = getLocalModule(parser.state, request))) {
  73. localModule.flagUsed();
  74. dep = new LocalModuleDependency(localModule, undefined, false);
  75. dep.loc = expr.loc;
  76. parser.state.module.addPresentationalDependency(dep);
  77. } else {
  78. dep = this.newRequireItemDependency(request);
  79. dep.loc = expr.loc;
  80. dep.optional = !!parser.scope.inTry;
  81. parser.state.current.addDependency(dep);
  82. }
  83. deps.push(dep);
  84. });
  85. const dep = this.newRequireArrayDependency(deps, param.range);
  86. dep.loc = expr.loc;
  87. dep.optional = !!parser.scope.inTry;
  88. parser.state.module.addPresentationalDependency(dep);
  89. return true;
  90. }
  91. }
  92. processItem(parser, expr, param, namedModule) {
  93. if (param.isConditional()) {
  94. param.options.forEach(param => {
  95. const result = this.processItem(parser, expr, param);
  96. if (result === undefined) {
  97. this.processContext(parser, expr, param);
  98. }
  99. });
  100. return true;
  101. } else if (param.isString()) {
  102. let dep, localModule;
  103. if (param.string === "require") {
  104. dep = new ConstDependency("__webpack_require__", param.range, [
  105. RuntimeGlobals.require
  106. ]);
  107. } else if (param.string === "exports") {
  108. dep = new ConstDependency("exports", param.range, [
  109. RuntimeGlobals.exports
  110. ]);
  111. } else if (param.string === "module") {
  112. dep = new ConstDependency("module", param.range, [
  113. RuntimeGlobals.module
  114. ]);
  115. } else if (
  116. (localModule = getLocalModule(parser.state, param.string, namedModule))
  117. ) {
  118. localModule.flagUsed();
  119. dep = new LocalModuleDependency(localModule, param.range, false);
  120. } else {
  121. dep = this.newRequireItemDependency(param.string, param.range);
  122. dep.optional = !!parser.scope.inTry;
  123. parser.state.current.addDependency(dep);
  124. return true;
  125. }
  126. dep.loc = expr.loc;
  127. parser.state.module.addPresentationalDependency(dep);
  128. return true;
  129. }
  130. }
  131. processContext(parser, expr, param) {
  132. const dep = ContextDependencyHelpers.create(
  133. AMDRequireContextDependency,
  134. param.range,
  135. param,
  136. expr,
  137. this.options,
  138. {
  139. category: "amd"
  140. },
  141. parser
  142. );
  143. if (!dep) return;
  144. dep.loc = expr.loc;
  145. dep.optional = !!parser.scope.inTry;
  146. parser.state.current.addDependency(dep);
  147. return true;
  148. }
  149. processCallDefine(parser, expr) {
  150. let array, fn, obj, namedModule;
  151. switch (expr.arguments.length) {
  152. case 1:
  153. if (isCallable(expr.arguments[0])) {
  154. // define(f() {…})
  155. fn = expr.arguments[0];
  156. } else if (expr.arguments[0].type === "ObjectExpression") {
  157. // define({…})
  158. obj = expr.arguments[0];
  159. } else {
  160. // define(expr)
  161. // unclear if function or object
  162. obj = fn = expr.arguments[0];
  163. }
  164. break;
  165. case 2:
  166. if (expr.arguments[0].type === "Literal") {
  167. namedModule = expr.arguments[0].value;
  168. // define("…", …)
  169. if (isCallable(expr.arguments[1])) {
  170. // define("…", f() {…})
  171. fn = expr.arguments[1];
  172. } else if (expr.arguments[1].type === "ObjectExpression") {
  173. // define("…", {…})
  174. obj = expr.arguments[1];
  175. } else {
  176. // define("…", expr)
  177. // unclear if function or object
  178. obj = fn = expr.arguments[1];
  179. }
  180. } else {
  181. array = expr.arguments[0];
  182. if (isCallable(expr.arguments[1])) {
  183. // define([…], f() {})
  184. fn = expr.arguments[1];
  185. } else if (expr.arguments[1].type === "ObjectExpression") {
  186. // define([…], {…})
  187. obj = expr.arguments[1];
  188. } else {
  189. // define([…], expr)
  190. // unclear if function or object
  191. obj = fn = expr.arguments[1];
  192. }
  193. }
  194. break;
  195. case 3:
  196. // define("…", […], f() {…})
  197. namedModule = expr.arguments[0].value;
  198. array = expr.arguments[1];
  199. if (isCallable(expr.arguments[2])) {
  200. // define("…", […], f() {})
  201. fn = expr.arguments[2];
  202. } else if (expr.arguments[2].type === "ObjectExpression") {
  203. // define("…", […], {…})
  204. obj = expr.arguments[2];
  205. } else {
  206. // define("…", […], expr)
  207. // unclear if function or object
  208. obj = fn = expr.arguments[2];
  209. }
  210. break;
  211. default:
  212. return;
  213. }
  214. DynamicExports.bailout(parser.state);
  215. let fnParams = null;
  216. let fnParamsOffset = 0;
  217. if (fn) {
  218. if (isUnboundFunctionExpression(fn)) {
  219. fnParams = fn.params;
  220. } else if (isBoundFunctionExpression(fn)) {
  221. fnParams = fn.callee.object.params;
  222. fnParamsOffset = fn.arguments.length - 1;
  223. if (fnParamsOffset < 0) {
  224. fnParamsOffset = 0;
  225. }
  226. }
  227. }
  228. let fnRenames = new Map();
  229. if (array) {
  230. const identifiers = {};
  231. const param = parser.evaluateExpression(array);
  232. const result = this.processArray(
  233. parser,
  234. expr,
  235. param,
  236. identifiers,
  237. namedModule
  238. );
  239. if (!result) return;
  240. if (fnParams) {
  241. fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
  242. if (identifiers[idx]) {
  243. fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
  244. return false;
  245. }
  246. return true;
  247. });
  248. }
  249. } else {
  250. const identifiers = ["require", "exports", "module"];
  251. if (fnParams) {
  252. fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
  253. if (identifiers[idx]) {
  254. fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
  255. return false;
  256. }
  257. return true;
  258. });
  259. }
  260. }
  261. let inTry;
  262. if (fn && isUnboundFunctionExpression(fn)) {
  263. inTry = parser.scope.inTry;
  264. parser.inScope(fnParams, () => {
  265. for (const [name, varInfo] of fnRenames) {
  266. parser.setVariable(name, varInfo);
  267. }
  268. parser.scope.inTry = inTry;
  269. if (fn.body.type === "BlockStatement") {
  270. parser.detectMode(fn.body.body);
  271. const prev = parser.prevStatement;
  272. parser.preWalkStatement(fn.body);
  273. parser.prevStatement = prev;
  274. parser.walkStatement(fn.body);
  275. } else {
  276. parser.walkExpression(fn.body);
  277. }
  278. });
  279. } else if (fn && isBoundFunctionExpression(fn)) {
  280. inTry = parser.scope.inTry;
  281. parser.inScope(
  282. fn.callee.object.params.filter(
  283. i => !["require", "module", "exports"].includes(i.name)
  284. ),
  285. () => {
  286. for (const [name, varInfo] of fnRenames) {
  287. parser.setVariable(name, varInfo);
  288. }
  289. parser.scope.inTry = inTry;
  290. if (fn.callee.object.body.type === "BlockStatement") {
  291. parser.detectMode(fn.callee.object.body.body);
  292. const prev = parser.prevStatement;
  293. parser.preWalkStatement(fn.callee.object.body);
  294. parser.prevStatement = prev;
  295. parser.walkStatement(fn.callee.object.body);
  296. } else {
  297. parser.walkExpression(fn.callee.object.body);
  298. }
  299. }
  300. );
  301. if (fn.arguments) {
  302. parser.walkExpressions(fn.arguments);
  303. }
  304. } else if (fn || obj) {
  305. parser.walkExpression(fn || obj);
  306. }
  307. const dep = this.newDefineDependency(
  308. expr.range,
  309. array ? array.range : null,
  310. fn ? fn.range : null,
  311. obj ? obj.range : null,
  312. namedModule ? namedModule : null
  313. );
  314. dep.loc = expr.loc;
  315. if (namedModule) {
  316. dep.localModule = addLocalModule(parser.state, namedModule);
  317. }
  318. parser.state.module.addPresentationalDependency(dep);
  319. return true;
  320. }
  321. newDefineDependency(
  322. range,
  323. arrayRange,
  324. functionRange,
  325. objectRange,
  326. namedModule
  327. ) {
  328. return new AMDDefineDependency(
  329. range,
  330. arrayRange,
  331. functionRange,
  332. objectRange,
  333. namedModule
  334. );
  335. }
  336. newRequireArrayDependency(depsArray, range) {
  337. return new AMDRequireArrayDependency(depsArray, range);
  338. }
  339. newRequireItemDependency(request, range) {
  340. return new AMDRequireItemDependency(request, range);
  341. }
  342. }
  343. module.exports = AMDDefineDependencyParserPlugin;