AMDRequireDependenciesBlockParserPlugin.js 13 KB

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