AMDRequireDependenciesBlockParserPlugin.js 12 KB

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