AMDDefineDependencyParserPlugin.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. function 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. function isUnboundFunctionExpression(expr) {
  24. if(expr.type === "FunctionExpression") return true;
  25. if(expr.type === "ArrowFunctionExpression") return true;
  26. return false;
  27. }
  28. function 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. newDefineDependency(range, arrayRange, functionRange, objectRange, namedModule) {
  38. return new AMDDefineDependency(range, arrayRange, functionRange, objectRange, namedModule);
  39. }
  40. apply(parser) {
  41. const options = this.options;
  42. parser.plugin("call define", (expr) => {
  43. let array, fn, obj, namedModule;
  44. switch(expr.arguments.length) {
  45. case 1:
  46. if(isCallable(expr.arguments[0])) {
  47. // define(f() {...})
  48. fn = expr.arguments[0];
  49. } else if(expr.arguments[0].type === "ObjectExpression") {
  50. // define({...})
  51. obj = expr.arguments[0];
  52. } else {
  53. // define(expr)
  54. // unclear if function or object
  55. obj = fn = expr.arguments[0];
  56. }
  57. break;
  58. case 2:
  59. if(expr.arguments[0].type === "Literal") {
  60. namedModule = expr.arguments[0].value;
  61. // define("...", ...)
  62. if(isCallable(expr.arguments[1])) {
  63. // define("...", f() {...})
  64. fn = expr.arguments[1];
  65. } else if(expr.arguments[1].type === "ObjectExpression") {
  66. // define("...", {...})
  67. obj = expr.arguments[1];
  68. } else {
  69. // define("...", expr)
  70. // unclear if function or object
  71. obj = fn = expr.arguments[1];
  72. }
  73. } else {
  74. array = expr.arguments[0];
  75. if(isCallable(expr.arguments[1])) {
  76. // define([...], f() {})
  77. fn = expr.arguments[1];
  78. } else if(expr.arguments[1].type === "ObjectExpression") {
  79. // define([...], {...})
  80. obj = expr.arguments[1];
  81. } else {
  82. // define([...], expr)
  83. // unclear if function or object
  84. obj = fn = expr.arguments[1];
  85. }
  86. }
  87. break;
  88. case 3:
  89. // define("...", [...], f() {...})
  90. namedModule = expr.arguments[0].value;
  91. array = expr.arguments[1];
  92. if(isCallable(expr.arguments[2])) {
  93. // define("...", [...], f() {})
  94. fn = expr.arguments[2];
  95. } else if(expr.arguments[2].type === "ObjectExpression") {
  96. // define("...", [...], {...})
  97. obj = expr.arguments[2];
  98. } else {
  99. // define("...", [...], expr)
  100. // unclear if function or object
  101. obj = fn = expr.arguments[2];
  102. }
  103. break;
  104. default:
  105. return;
  106. }
  107. let fnParams = null;
  108. let fnParamsOffset = 0;
  109. if(fn) {
  110. if(isUnboundFunctionExpression(fn)) fnParams = fn.params;
  111. else if(isBoundFunctionExpression(fn)) {
  112. fnParams = fn.callee.object.params;
  113. fnParamsOffset = fn.arguments.length - 1;
  114. if(fnParamsOffset < 0) fnParamsOffset = 0;
  115. }
  116. }
  117. let fnRenames = Object.create(parser.scope.renames);
  118. let identifiers;
  119. if(array) {
  120. identifiers = {};
  121. const param = parser.evaluateExpression(array);
  122. const result = parser.applyPluginsBailResult("call define:amd:array", expr, param, identifiers, namedModule);
  123. if(!result) return;
  124. if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
  125. if(identifiers[idx]) {
  126. fnRenames["$" + param.name] = identifiers[idx];
  127. return false;
  128. }
  129. return true;
  130. });
  131. } else {
  132. identifiers = ["require", "exports", "module"];
  133. if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
  134. if(identifiers[idx]) {
  135. fnRenames["$" + param.name] = identifiers[idx];
  136. return false;
  137. }
  138. return true;
  139. });
  140. }
  141. let inTry;
  142. if(fn && isUnboundFunctionExpression(fn)) {
  143. inTry = parser.scope.inTry;
  144. parser.inScope(fnParams, () => {
  145. parser.scope.renames = fnRenames;
  146. parser.scope.inTry = inTry;
  147. if(fn.body.type === "BlockStatement")
  148. parser.walkStatement(fn.body);
  149. else
  150. parser.walkExpression(fn.body);
  151. });
  152. } else if(fn && isBoundFunctionExpression(fn)) {
  153. inTry = parser.scope.inTry;
  154. parser.inScope(fn.callee.object.params.filter((i) => ["require", "module", "exports"].indexOf(i.name) < 0), () => {
  155. parser.scope.renames = fnRenames;
  156. parser.scope.inTry = inTry;
  157. if(fn.callee.object.body.type === "BlockStatement")
  158. parser.walkStatement(fn.callee.object.body);
  159. else
  160. parser.walkExpression(fn.callee.object.body);
  161. });
  162. if(fn.arguments)
  163. parser.walkExpressions(fn.arguments);
  164. } else if(fn || obj) {
  165. parser.walkExpression(fn || obj);
  166. }
  167. const dep = this.newDefineDependency(
  168. expr.range,
  169. array ? array.range : null,
  170. fn ? fn.range : null,
  171. obj ? obj.range : null,
  172. namedModule ? namedModule : null
  173. );
  174. dep.loc = expr.loc;
  175. if(namedModule) {
  176. dep.localModule = LocalModulesHelpers.addLocalModule(parser.state, namedModule);
  177. }
  178. parser.state.current.addDependency(dep);
  179. return true;
  180. });
  181. parser.plugin("call define:amd:array", (expr, param, identifiers, namedModule) => {
  182. if(param.isArray()) {
  183. param.items.forEach((param, idx) => {
  184. if(param.isString() && ["require", "module", "exports"].indexOf(param.string) >= 0)
  185. identifiers[idx] = param.string;
  186. const result = parser.applyPluginsBailResult("call define:amd:item", expr, param, namedModule);
  187. if(result === undefined) {
  188. parser.applyPluginsBailResult("call define:amd:context", expr, param);
  189. }
  190. });
  191. return true;
  192. } else if(param.isConstArray()) {
  193. const deps = [];
  194. param.array.forEach((request, idx) => {
  195. let dep;
  196. let localModule;
  197. if(request === "require") {
  198. identifiers[idx] = request;
  199. dep = "__webpack_require__";
  200. } else if(["exports", "module"].indexOf(request) >= 0) {
  201. identifiers[idx] = request;
  202. dep = request;
  203. } else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, request)) { // eslint-disable-line no-cond-assign
  204. dep = new LocalModuleDependency(localModule);
  205. dep.loc = expr.loc;
  206. parser.state.current.addDependency(dep);
  207. } else {
  208. dep = new AMDRequireItemDependency(request);
  209. dep.loc = expr.loc;
  210. dep.optional = !!parser.scope.inTry;
  211. parser.state.current.addDependency(dep);
  212. }
  213. deps.push(dep);
  214. });
  215. const dep = new AMDRequireArrayDependency(deps, param.range);
  216. dep.loc = expr.loc;
  217. dep.optional = !!parser.scope.inTry;
  218. parser.state.current.addDependency(dep);
  219. return true;
  220. }
  221. });
  222. parser.plugin("call define:amd:item", (expr, param, namedModule) => {
  223. if(param.isConditional()) {
  224. param.options.forEach((param) => {
  225. const result = parser.applyPluginsBailResult("call define:amd:item", expr, param);
  226. if(result === undefined) {
  227. parser.applyPluginsBailResult("call define:amd:context", expr, param);
  228. }
  229. });
  230. return true;
  231. } else if(param.isString()) {
  232. let dep, localModule;
  233. if(param.string === "require") {
  234. dep = new ConstDependency("__webpack_require__", param.range);
  235. } else if(["require", "exports", "module"].indexOf(param.string) >= 0) {
  236. dep = new ConstDependency(param.string, param.range);
  237. } else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, param.string, namedModule)) { // eslint-disable-line no-cond-assign
  238. dep = new LocalModuleDependency(localModule, param.range);
  239. } else {
  240. dep = new AMDRequireItemDependency(param.string, param.range);
  241. }
  242. dep.loc = expr.loc;
  243. dep.optional = !!parser.scope.inTry;
  244. parser.state.current.addDependency(dep);
  245. return true;
  246. }
  247. });
  248. parser.plugin("call define:amd:context", (expr, param) => {
  249. const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options);
  250. if(!dep) return;
  251. dep.loc = expr.loc;
  252. dep.optional = !!parser.scope.inTry;
  253. parser.state.current.addDependency(dep);
  254. return true;
  255. });
  256. }
  257. }
  258. module.exports = AMDDefineDependencyParserPlugin;