You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AMDDefineDependencyParserPlugin.js 9.1KB

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