AMDRequireDependenciesBlockParserPlugin.js 12 KB

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