ImportPhase.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Haijie Xie @hai-x
  4. */
  5. "use strict";
  6. const memoize = require("../util/memoize");
  7. const getCommentCompilationWarning = memoize(() =>
  8. require("../CommentCompilationWarning")
  9. );
  10. /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
  11. /** @typedef {import("../javascript/JavascriptParser").ExportAllDeclaration} ExportAllDeclaration */
  12. /** @typedef {import("../javascript/JavascriptParser").ExportNamedDeclaration} ExportNamedDeclaration */
  13. /** @typedef {import("../javascript/JavascriptParser").ImportDeclaration} ImportDeclaration */
  14. /** @typedef {import("../javascript/JavascriptParser").ImportExpression} ImportExpression */
  15. /** @typedef {typeof ImportPhase.Evaluation | typeof ImportPhase.Defer | typeof ImportPhase.Source} ImportPhaseType */
  16. const ImportPhase = Object.freeze({
  17. Evaluation: 0b00,
  18. Defer: 0b01,
  19. Source: 0b10
  20. });
  21. /** @typedef {"defer" | "source" | "evaluation"} ImportPhaseName */
  22. /**
  23. * Defines the import phase utils type used by this module.
  24. * @typedef {object} ImportPhaseUtils
  25. * @property {(phase: ImportPhaseType | undefined) => boolean} isEvaluation true if phase is evaluation
  26. * @property {(phase: ImportPhaseType | undefined) => boolean} isDefer true if phase is defer
  27. * @property {(phase: ImportPhaseType | undefined) => boolean} isSource true if phase is source
  28. * @property {(phase: ImportPhaseType) => ImportPhaseName} stringify return stringified name of phase
  29. */
  30. /** @type {ImportPhaseUtils} */
  31. const ImportPhaseUtils = {
  32. isEvaluation(phase) {
  33. return phase === ImportPhase.Evaluation;
  34. },
  35. isDefer(phase) {
  36. return phase === ImportPhase.Defer;
  37. },
  38. isSource(phase) {
  39. return phase === ImportPhase.Source;
  40. },
  41. stringify(phase) {
  42. switch (phase) {
  43. case ImportPhase.Defer:
  44. return "defer";
  45. case ImportPhase.Source:
  46. return "source";
  47. default:
  48. return "evaluation";
  49. }
  50. }
  51. };
  52. /**
  53. * Defines the get comment options type used by this module.
  54. * @typedef {() => Record<string, EXPECTED_ANY> | null} GetCommentOptions
  55. */
  56. /**
  57. * Defines the get import phase callback.
  58. * @callback GetImportPhase
  59. * @param {JavascriptParser} parser parser
  60. * @param {ExportNamedDeclaration | ExportAllDeclaration | ImportDeclaration | ImportExpression} node node
  61. * @param {GetCommentOptions=} getCommentOptions optional function that returns the comment options object.
  62. * @returns {ImportPhaseType} import phase
  63. */
  64. /**
  65. * Creates an import phase resolver.
  66. * @param {boolean=} enableDeferPhase enable defer phase detection
  67. * @param {boolean=} enableSourcePhase enable source phase detection
  68. * @returns {GetImportPhase} evaluates the import phase for ast node
  69. */
  70. function createGetImportPhase(enableDeferPhase, enableSourcePhase) {
  71. return (parser, node, getCommentOptions) => {
  72. if (!enableDeferPhase && !enableSourcePhase) return ImportPhase.Evaluation;
  73. // We now only support `defer import` and `source import` syntax
  74. const phaseBySyntax =
  75. "phase" in node
  76. ? node.phase === "defer" && enableDeferPhase
  77. ? ImportPhase.Defer
  78. : node.phase === "source" && enableSourcePhase
  79. ? ImportPhase.Source
  80. : ImportPhase.Evaluation
  81. : ImportPhase.Evaluation;
  82. if (!node.range) {
  83. return phaseBySyntax;
  84. }
  85. getCommentOptions =
  86. getCommentOptions ||
  87. (() => {
  88. if (!node.range) return null;
  89. const { options, errors } = parser.parseCommentOptions(node.range);
  90. if (errors) {
  91. for (const e of errors) {
  92. const { comment } = e;
  93. if (!comment.loc) continue;
  94. const CommentCompilationWarning = getCommentCompilationWarning();
  95. parser.state.module.addWarning(
  96. new CommentCompilationWarning(
  97. `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
  98. comment.loc
  99. )
  100. );
  101. }
  102. }
  103. return options;
  104. });
  105. const options = getCommentOptions();
  106. if (!options) {
  107. return phaseBySyntax;
  108. }
  109. if (!options.webpackDefer && !options.webpackSource) {
  110. return phaseBySyntax;
  111. }
  112. const { webpackDefer, webpackSource } = options;
  113. if (enableDeferPhase && typeof options.webpackDefer !== "undefined") {
  114. if (typeof webpackDefer === "boolean") {
  115. return webpackDefer ? ImportPhase.Defer : phaseBySyntax;
  116. } else if (node.loc) {
  117. const CommentCompilationWarning = getCommentCompilationWarning();
  118. parser.state.module.addWarning(
  119. new CommentCompilationWarning(
  120. "webpackDefer magic comment expected a boolean value.",
  121. node.loc
  122. )
  123. );
  124. }
  125. }
  126. if (enableSourcePhase && typeof options.webpackSource !== "undefined") {
  127. if (typeof webpackSource === "boolean") {
  128. return webpackSource ? ImportPhase.Source : phaseBySyntax;
  129. } else if (node.loc) {
  130. const CommentCompilationWarning = getCommentCompilationWarning();
  131. parser.state.module.addWarning(
  132. new CommentCompilationWarning(
  133. "webpackSource magic comment expected a boolean value.",
  134. node.loc
  135. )
  136. );
  137. }
  138. }
  139. return phaseBySyntax;
  140. };
  141. }
  142. module.exports = {
  143. ImportPhase,
  144. ImportPhaseUtils,
  145. createGetImportPhase
  146. };