RequireEnsureDependenciesBlockParserPlugin.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RequireEnsureDependenciesBlock = require("./RequireEnsureDependenciesBlock");
  7. const RequireEnsureDependency = require("./RequireEnsureDependency");
  8. const RequireEnsureItemDependency = require("./RequireEnsureItemDependency");
  9. const getFunctionExpression = require("./getFunctionExpression");
  10. /** @typedef {import("../AsyncDependenciesBlock").GroupOptions} GroupOptions */
  11. /** @typedef {import("../ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
  12. /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
  13. /** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
  14. /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
  15. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  16. const PLUGIN_NAME = "RequireEnsureDependenciesBlockParserPlugin";
  17. module.exports = class RequireEnsureDependenciesBlockParserPlugin {
  18. /**
  19. * @param {JavascriptParser} parser the parser
  20. * @returns {void}
  21. */
  22. apply(parser) {
  23. parser.hooks.call.for("require.ensure").tap(PLUGIN_NAME, expr => {
  24. /** @type {string | GroupOptions | null} */
  25. let chunkName = null;
  26. let errorExpressionArg = null;
  27. let errorExpression = null;
  28. switch (expr.arguments.length) {
  29. case 4: {
  30. const chunkNameExpr = parser.evaluateExpression(expr.arguments[3]);
  31. if (!chunkNameExpr.isString()) return;
  32. chunkName =
  33. /** @type {string} */
  34. (chunkNameExpr.string);
  35. }
  36. // falls through
  37. case 3: {
  38. errorExpressionArg = expr.arguments[2];
  39. errorExpression = getFunctionExpression(errorExpressionArg);
  40. if (!errorExpression && !chunkName) {
  41. const chunkNameExpr = parser.evaluateExpression(expr.arguments[2]);
  42. if (!chunkNameExpr.isString()) return;
  43. chunkName =
  44. /** @type {string} */
  45. (chunkNameExpr.string);
  46. }
  47. }
  48. // falls through
  49. case 2: {
  50. const dependenciesExpr = parser.evaluateExpression(expr.arguments[0]);
  51. const dependenciesItems = /** @type {BasicEvaluatedExpression[]} */ (
  52. dependenciesExpr.isArray()
  53. ? dependenciesExpr.items
  54. : [dependenciesExpr]
  55. );
  56. const successExpressionArg = expr.arguments[1];
  57. const successExpression = getFunctionExpression(successExpressionArg);
  58. if (successExpression) {
  59. parser.walkExpressions(successExpression.expressions);
  60. }
  61. if (errorExpression) {
  62. parser.walkExpressions(errorExpression.expressions);
  63. }
  64. const depBlock = new RequireEnsureDependenciesBlock(
  65. chunkName,
  66. /** @type {DependencyLocation} */
  67. (expr.loc)
  68. );
  69. const errorCallbackExists =
  70. expr.arguments.length === 4 ||
  71. (!chunkName && expr.arguments.length === 3);
  72. const dep = new RequireEnsureDependency(
  73. /** @type {Range} */ (expr.range),
  74. /** @type {Range} */ (expr.arguments[1].range),
  75. errorCallbackExists &&
  76. /** @type {Range} */ (expr.arguments[2].range)
  77. );
  78. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  79. depBlock.addDependency(dep);
  80. const old = parser.state.current;
  81. parser.state.current = /** @type {TODO} */ (depBlock);
  82. try {
  83. let failed = false;
  84. parser.inScope([], () => {
  85. for (const ee of dependenciesItems) {
  86. if (ee.isString()) {
  87. const ensureDependency = new RequireEnsureItemDependency(
  88. /** @type {string} */ (ee.string)
  89. );
  90. ensureDependency.loc =
  91. /** @type {DependencyLocation} */
  92. (expr.loc);
  93. depBlock.addDependency(ensureDependency);
  94. } else {
  95. failed = true;
  96. }
  97. }
  98. });
  99. if (failed) {
  100. return;
  101. }
  102. if (successExpression) {
  103. if (successExpression.fn.body.type === "BlockStatement") {
  104. parser.walkStatement(successExpression.fn.body);
  105. } else {
  106. parser.walkExpression(successExpression.fn.body);
  107. }
  108. }
  109. old.addBlock(depBlock);
  110. } finally {
  111. parser.state.current = old;
  112. }
  113. if (!successExpression) {
  114. parser.walkExpression(successExpressionArg);
  115. }
  116. if (errorExpression) {
  117. if (errorExpression.fn.body.type === "BlockStatement") {
  118. parser.walkStatement(errorExpression.fn.body);
  119. } else {
  120. parser.walkExpression(errorExpression.fn.body);
  121. }
  122. } else if (errorExpressionArg) {
  123. parser.walkExpression(errorExpressionArg);
  124. }
  125. return true;
  126. }
  127. }
  128. });
  129. }
  130. };