AMDDefineDependencyParserPlugin.js 8.9 KB

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