AMDRequireDependenciesBlockParserPlugin.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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 UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
  8. const AMDRequireArrayDependency = require("./AMDRequireArrayDependency");
  9. const AMDRequireContextDependency = require("./AMDRequireContextDependency");
  10. const AMDRequireDependenciesBlock = require("./AMDRequireDependenciesBlock");
  11. const AMDRequireDependency = require("./AMDRequireDependency");
  12. const AMDRequireItemDependency = require("./AMDRequireItemDependency");
  13. const ConstDependency = require("./ConstDependency");
  14. const ContextDependencyHelpers = require("./ContextDependencyHelpers");
  15. const LocalModuleDependency = require("./LocalModuleDependency");
  16. const { getLocalModule } = require("./LocalModulesHelpers");
  17. const UnsupportedDependency = require("./UnsupportedDependency");
  18. const getFunctionExpression = require("./getFunctionExpression");
  19. /** @typedef {import("estree").CallExpression} CallExpression */
  20. /** @typedef {import("estree").Expression} Expression */
  21. /** @typedef {import("estree").Identifier} Identifier */
  22. /** @typedef {import("estree").SourceLocation} SourceLocation */
  23. /** @typedef {import("estree").SpreadElement} SpreadElement */
  24. /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
  25. /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
  26. /** @typedef {import("../Module").BuildInfo} BuildInfo */
  27. /** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
  28. /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
  29. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  30. class AMDRequireDependenciesBlockParserPlugin {
  31. /**
  32. * @param {JavascriptParserOptions} options parserOptions
  33. */
  34. constructor(options) {
  35. this.options = options;
  36. }
  37. /**
  38. * @param {JavascriptParser} parser the parser
  39. * @param {Expression | SpreadElement} expression expression
  40. * @returns {boolean} need bind this
  41. */
  42. processFunctionArgument(parser, expression) {
  43. let bindThis = true;
  44. const fnData = getFunctionExpression(expression);
  45. if (fnData) {
  46. parser.inScope(
  47. fnData.fn.params.filter(i => {
  48. return !["require", "module", "exports"].includes(
  49. /** @type {Identifier} */ (i).name
  50. );
  51. }),
  52. () => {
  53. if (fnData.fn.body.type === "BlockStatement") {
  54. parser.walkStatement(fnData.fn.body);
  55. } else {
  56. parser.walkExpression(fnData.fn.body);
  57. }
  58. }
  59. );
  60. parser.walkExpressions(fnData.expressions);
  61. if (fnData.needThis === false) {
  62. bindThis = false;
  63. }
  64. } else {
  65. parser.walkExpression(expression);
  66. }
  67. return bindThis;
  68. }
  69. /**
  70. * @param {JavascriptParser} parser the parser
  71. * @returns {void}
  72. */
  73. apply(parser) {
  74. parser.hooks.call
  75. .for("require")
  76. .tap(
  77. "AMDRequireDependenciesBlockParserPlugin",
  78. this.processCallRequire.bind(this, parser)
  79. );
  80. }
  81. /**
  82. * @param {JavascriptParser} parser the parser
  83. * @param {CallExpression} expr call expression
  84. * @param {BasicEvaluatedExpression} param param
  85. * @returns {boolean | undefined} result
  86. */
  87. processArray(parser, expr, param) {
  88. if (param.isArray()) {
  89. for (const p of /** @type {BasicEvaluatedExpression[]} */ (param.items)) {
  90. const result = this.processItem(parser, expr, p);
  91. if (result === undefined) {
  92. this.processContext(parser, expr, p);
  93. }
  94. }
  95. return true;
  96. } else if (param.isConstArray()) {
  97. /** @type {(string | LocalModuleDependency | AMDRequireItemDependency)[]} */
  98. const deps = [];
  99. for (const request of /** @type {any[]} */ (param.array)) {
  100. let dep, localModule;
  101. if (request === "require") {
  102. dep = RuntimeGlobals.require;
  103. } else if (["exports", "module"].includes(request)) {
  104. dep = request;
  105. } else if ((localModule = getLocalModule(parser.state, request))) {
  106. localModule.flagUsed();
  107. dep = new LocalModuleDependency(localModule, undefined, false);
  108. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  109. parser.state.module.addPresentationalDependency(dep);
  110. } else {
  111. dep = this.newRequireItemDependency(request);
  112. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  113. dep.optional = !!parser.scope.inTry;
  114. parser.state.current.addDependency(dep);
  115. }
  116. deps.push(dep);
  117. }
  118. const dep = this.newRequireArrayDependency(
  119. deps,
  120. /** @type {Range} */ (param.range)
  121. );
  122. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  123. dep.optional = !!parser.scope.inTry;
  124. parser.state.module.addPresentationalDependency(dep);
  125. return true;
  126. }
  127. }
  128. /**
  129. * @param {JavascriptParser} parser the parser
  130. * @param {CallExpression} expr call expression
  131. * @param {BasicEvaluatedExpression} param param
  132. * @returns {boolean | undefined} result
  133. */
  134. processItem(parser, expr, param) {
  135. if (param.isConditional()) {
  136. for (const p of /** @type {BasicEvaluatedExpression[]} */ (
  137. param.options
  138. )) {
  139. const result = this.processItem(parser, expr, p);
  140. if (result === undefined) {
  141. this.processContext(parser, expr, p);
  142. }
  143. }
  144. return true;
  145. } else if (param.isString()) {
  146. let dep, localModule;
  147. if (param.string === "require") {
  148. dep = new ConstDependency(
  149. RuntimeGlobals.require,
  150. /** @type {TODO} */ (param.string),
  151. [RuntimeGlobals.require]
  152. );
  153. } else if (param.string === "module") {
  154. dep = new ConstDependency(
  155. /** @type {BuildInfo} */
  156. (parser.state.module.buildInfo).moduleArgument,
  157. /** @type {Range} */ (param.range),
  158. [RuntimeGlobals.module]
  159. );
  160. } else if (param.string === "exports") {
  161. dep = new ConstDependency(
  162. /** @type {BuildInfo} */
  163. (parser.state.module.buildInfo).exportsArgument,
  164. /** @type {Range} */ (param.range),
  165. [RuntimeGlobals.exports]
  166. );
  167. } else if (
  168. (localModule = getLocalModule(
  169. parser.state,
  170. /** @type {string} */ (param.string)
  171. ))
  172. ) {
  173. localModule.flagUsed();
  174. dep = new LocalModuleDependency(localModule, param.range, false);
  175. } else {
  176. dep = this.newRequireItemDependency(
  177. /** @type {string} */ (param.string),
  178. param.range
  179. );
  180. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  181. dep.optional = !!parser.scope.inTry;
  182. parser.state.current.addDependency(dep);
  183. return true;
  184. }
  185. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  186. parser.state.module.addPresentationalDependency(dep);
  187. return true;
  188. }
  189. }
  190. /**
  191. * @param {JavascriptParser} parser the parser
  192. * @param {CallExpression} expr call expression
  193. * @param {BasicEvaluatedExpression} param param
  194. * @returns {boolean | undefined} result
  195. */
  196. processContext(parser, expr, param) {
  197. const dep = ContextDependencyHelpers.create(
  198. AMDRequireContextDependency,
  199. /** @type {Range} */ (param.range),
  200. param,
  201. expr,
  202. this.options,
  203. {
  204. category: "amd"
  205. },
  206. parser
  207. );
  208. if (!dep) return;
  209. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  210. dep.optional = !!parser.scope.inTry;
  211. parser.state.current.addDependency(dep);
  212. return true;
  213. }
  214. /**
  215. * @param {BasicEvaluatedExpression} param param
  216. * @returns {string | undefined} result
  217. */
  218. processArrayForRequestString(param) {
  219. if (param.isArray()) {
  220. const result =
  221. /** @type {BasicEvaluatedExpression[]} */
  222. (param.items).map(item => this.processItemForRequestString(item));
  223. if (result.every(Boolean)) return result.join(" ");
  224. } else if (param.isConstArray()) {
  225. return /** @type {string[]} */ (param.array).join(" ");
  226. }
  227. }
  228. /**
  229. * @param {BasicEvaluatedExpression} param param
  230. * @returns {string | undefined} result
  231. */
  232. processItemForRequestString(param) {
  233. if (param.isConditional()) {
  234. const result =
  235. /** @type {BasicEvaluatedExpression[]} */
  236. (param.options).map(item => this.processItemForRequestString(item));
  237. if (result.every(Boolean)) return result.join("|");
  238. } else if (param.isString()) {
  239. return param.string;
  240. }
  241. }
  242. /**
  243. * @param {JavascriptParser} parser the parser
  244. * @param {CallExpression} expr call expression
  245. * @returns {boolean | undefined} result
  246. */
  247. processCallRequire(parser, expr) {
  248. /** @type {BasicEvaluatedExpression | undefined} */
  249. let param;
  250. /** @type {AMDRequireDependenciesBlock | undefined | null} */
  251. let depBlock;
  252. /** @type {AMDRequireDependency | undefined} */
  253. let dep;
  254. /** @type {boolean | undefined} */
  255. let result;
  256. const old = parser.state.current;
  257. if (expr.arguments.length >= 1) {
  258. param = parser.evaluateExpression(expr.arguments[0]);
  259. depBlock = this.newRequireDependenciesBlock(
  260. /** @type {DependencyLocation} */ (expr.loc),
  261. /** @type {string} */ (this.processArrayForRequestString(param))
  262. );
  263. dep = this.newRequireDependency(
  264. /** @type {Range} */ (expr.range),
  265. /** @type {Range} */ (param.range),
  266. expr.arguments.length > 1
  267. ? /** @type {Range} */ (expr.arguments[1].range)
  268. : null,
  269. expr.arguments.length > 2
  270. ? /** @type {Range} */ (expr.arguments[2].range)
  271. : null
  272. );
  273. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  274. depBlock.addDependency(dep);
  275. parser.state.current = /** @type {TODO} */ (depBlock);
  276. }
  277. if (expr.arguments.length === 1) {
  278. parser.inScope([], () => {
  279. result = this.processArray(
  280. parser,
  281. expr,
  282. /** @type {BasicEvaluatedExpression} */ (param)
  283. );
  284. });
  285. parser.state.current = old;
  286. if (!result) return;
  287. parser.state.current.addBlock(
  288. /** @type {AMDRequireDependenciesBlock} */ (depBlock)
  289. );
  290. return true;
  291. }
  292. if (expr.arguments.length === 2 || expr.arguments.length === 3) {
  293. try {
  294. parser.inScope([], () => {
  295. result = this.processArray(
  296. parser,
  297. expr,
  298. /** @type {BasicEvaluatedExpression} */ (param)
  299. );
  300. });
  301. if (!result) {
  302. const dep = new UnsupportedDependency(
  303. "unsupported",
  304. /** @type {Range} */ (expr.range)
  305. );
  306. old.addPresentationalDependency(dep);
  307. if (parser.state.module) {
  308. parser.state.module.addError(
  309. new UnsupportedFeatureWarning(
  310. "Cannot statically analyse 'require(…, …)' in line " +
  311. /** @type {SourceLocation} */ (expr.loc).start.line,
  312. /** @type {DependencyLocation} */ (expr.loc)
  313. )
  314. );
  315. }
  316. depBlock = null;
  317. return true;
  318. }
  319. /** @type {AMDRequireDependency} */
  320. (dep).functionBindThis = this.processFunctionArgument(
  321. parser,
  322. expr.arguments[1]
  323. );
  324. if (expr.arguments.length === 3) {
  325. /** @type {AMDRequireDependency} */
  326. (dep).errorCallbackBindThis = this.processFunctionArgument(
  327. parser,
  328. expr.arguments[2]
  329. );
  330. }
  331. } finally {
  332. parser.state.current = old;
  333. if (depBlock) parser.state.current.addBlock(depBlock);
  334. }
  335. return true;
  336. }
  337. }
  338. /**
  339. * @param {DependencyLocation} loc location
  340. * @param {string} request request
  341. * @returns {AMDRequireDependenciesBlock} AMDRequireDependenciesBlock
  342. */
  343. newRequireDependenciesBlock(loc, request) {
  344. return new AMDRequireDependenciesBlock(loc, request);
  345. }
  346. /**
  347. * @param {Range} outerRange outer range
  348. * @param {Range} arrayRange array range
  349. * @param {Range | null} functionRange function range
  350. * @param {Range | null} errorCallbackRange error callback range
  351. * @returns {AMDRequireDependency} dependency
  352. */
  353. newRequireDependency(
  354. outerRange,
  355. arrayRange,
  356. functionRange,
  357. errorCallbackRange
  358. ) {
  359. return new AMDRequireDependency(
  360. outerRange,
  361. arrayRange,
  362. functionRange,
  363. errorCallbackRange
  364. );
  365. }
  366. /**
  367. * @param {string} request request
  368. * @param {Range=} range range
  369. * @returns {AMDRequireItemDependency} AMDRequireItemDependency
  370. */
  371. newRequireItemDependency(request, range) {
  372. return new AMDRequireItemDependency(request, range);
  373. }
  374. /**
  375. * @param {(string | LocalModuleDependency | AMDRequireItemDependency)[]} depsArray deps array
  376. * @param {Range} range range
  377. * @returns {AMDRequireArrayDependency} AMDRequireArrayDependency
  378. */
  379. newRequireArrayDependency(depsArray, range) {
  380. return new AMDRequireArrayDependency(depsArray, range);
  381. }
  382. }
  383. module.exports = AMDRequireDependenciesBlockParserPlugin;