ImportPhase.js 4.8 KB

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