RequireEnsureDependenciesBlockParserPlugin.js 4.6 KB

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