HarmonyExportDependencyParserPlugin.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { getImportAttributes } = require("../javascript/JavascriptParser");
  7. const InnerGraph = require("../optimize/InnerGraph");
  8. const ConstDependency = require("./ConstDependency");
  9. const HarmonyExportExpressionDependency = require("./HarmonyExportExpressionDependency");
  10. const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency");
  11. const HarmonyExportImportedSpecifierDependency = require("./HarmonyExportImportedSpecifierDependency");
  12. const HarmonyExportSpecifierDependency = require("./HarmonyExportSpecifierDependency");
  13. const { ExportPresenceModes } = require("./HarmonyImportDependency");
  14. const {
  15. harmonySpecifierTag
  16. } = require("./HarmonyImportDependencyParserPlugin");
  17. const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
  18. /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
  19. /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
  20. /** @typedef {import("../javascript/JavascriptParser").ClassDeclaration} ClassDeclaration */
  21. /** @typedef {import("../javascript/JavascriptParser").FunctionDeclaration} FunctionDeclaration */
  22. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  23. const { HarmonyStarExportsList } = HarmonyExportImportedSpecifierDependency;
  24. module.exports = class HarmonyExportDependencyParserPlugin {
  25. /**
  26. * @param {import("../../declarations/WebpackOptions").JavascriptParserOptions} options options
  27. */
  28. constructor(options) {
  29. this.exportPresenceMode =
  30. options.reexportExportsPresence !== undefined
  31. ? ExportPresenceModes.fromUserOption(options.reexportExportsPresence)
  32. : options.exportsPresence !== undefined
  33. ? ExportPresenceModes.fromUserOption(options.exportsPresence)
  34. : options.strictExportPresence
  35. ? ExportPresenceModes.ERROR
  36. : ExportPresenceModes.AUTO;
  37. }
  38. /**
  39. * @param {JavascriptParser} parser the parser
  40. * @returns {void}
  41. */
  42. apply(parser) {
  43. const { exportPresenceMode } = this;
  44. parser.hooks.export.tap(
  45. "HarmonyExportDependencyParserPlugin",
  46. statement => {
  47. const dep = new HarmonyExportHeaderDependency(
  48. /** @type {Range | false} */ (
  49. statement.declaration && statement.declaration.range
  50. ),
  51. /** @type {Range} */ (statement.range)
  52. );
  53. dep.loc = Object.create(
  54. /** @type {DependencyLocation} */ (statement.loc)
  55. );
  56. dep.loc.index = -1;
  57. parser.state.module.addPresentationalDependency(dep);
  58. return true;
  59. }
  60. );
  61. parser.hooks.exportImport.tap(
  62. "HarmonyExportDependencyParserPlugin",
  63. (statement, source) => {
  64. parser.state.lastHarmonyImportOrder =
  65. (parser.state.lastHarmonyImportOrder || 0) + 1;
  66. const clearDep = new ConstDependency(
  67. "",
  68. /** @type {Range} */ (statement.range)
  69. );
  70. clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
  71. clearDep.loc.index = -1;
  72. parser.state.module.addPresentationalDependency(clearDep);
  73. const sideEffectDep = new HarmonyImportSideEffectDependency(
  74. /** @type {string} */ (source),
  75. parser.state.lastHarmonyImportOrder,
  76. getImportAttributes(statement)
  77. );
  78. sideEffectDep.loc = Object.create(
  79. /** @type {DependencyLocation} */ (statement.loc)
  80. );
  81. sideEffectDep.loc.index = -1;
  82. parser.state.current.addDependency(sideEffectDep);
  83. return true;
  84. }
  85. );
  86. parser.hooks.exportExpression.tap(
  87. "HarmonyExportDependencyParserPlugin",
  88. (statement, node) => {
  89. const isFunctionDeclaration = node.type === "FunctionDeclaration";
  90. const exprRange = /** @type {Range} */ (node.range);
  91. const statementRange = /** @type {Range} */ (statement.range);
  92. const comments = parser.getComments([statementRange[0], exprRange[0]]);
  93. const dep = new HarmonyExportExpressionDependency(
  94. exprRange,
  95. statementRange,
  96. comments
  97. .map(c => {
  98. switch (c.type) {
  99. case "Block":
  100. return `/*${c.value}*/`;
  101. case "Line":
  102. return `//${c.value}\n`;
  103. }
  104. return "";
  105. })
  106. .join(""),
  107. node.type.endsWith("Declaration") &&
  108. /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id
  109. ? /** @type {FunctionDeclaration | ClassDeclaration} */
  110. (node).id.name
  111. : isFunctionDeclaration
  112. ? {
  113. range: [
  114. exprRange[0],
  115. node.params.length > 0
  116. ? /** @type {Range} */ (node.params[0].range)[0]
  117. : /** @type {Range} */ (node.body.range)[0]
  118. ],
  119. prefix: `${node.async ? "async " : ""}function${
  120. node.generator ? "*" : ""
  121. } `,
  122. suffix: `(${node.params.length > 0 ? "" : ") "}`
  123. }
  124. : undefined
  125. );
  126. dep.loc = Object.create(
  127. /** @type {DependencyLocation} */ (statement.loc)
  128. );
  129. dep.loc.index = -1;
  130. parser.state.current.addDependency(dep);
  131. InnerGraph.addVariableUsage(
  132. parser,
  133. node.type.endsWith("Declaration") &&
  134. /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id
  135. ? /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id
  136. .name
  137. : "*default*",
  138. "default"
  139. );
  140. return true;
  141. }
  142. );
  143. parser.hooks.exportSpecifier.tap(
  144. "HarmonyExportDependencyParserPlugin",
  145. (statement, id, name, idx) => {
  146. const settings = parser.getTagData(id, harmonySpecifierTag);
  147. const harmonyNamedExports = (parser.state.harmonyNamedExports =
  148. parser.state.harmonyNamedExports || new Set());
  149. harmonyNamedExports.add(name);
  150. InnerGraph.addVariableUsage(parser, id, name);
  151. const dep = settings
  152. ? new HarmonyExportImportedSpecifierDependency(
  153. settings.source,
  154. settings.sourceOrder,
  155. settings.ids,
  156. name,
  157. harmonyNamedExports,
  158. null,
  159. exportPresenceMode,
  160. null,
  161. settings.attributes
  162. )
  163. : new HarmonyExportSpecifierDependency(id, name);
  164. dep.loc = Object.create(
  165. /** @type {DependencyLocation} */ (statement.loc)
  166. );
  167. dep.loc.index = idx;
  168. const isAsiSafe = !parser.isAsiPosition(
  169. /** @type {Range} */
  170. (statement.range)[0]
  171. );
  172. if (!isAsiSafe) {
  173. parser.setAsiPosition(/** @type {Range} */ (statement.range)[1]);
  174. }
  175. parser.state.current.addDependency(dep);
  176. return true;
  177. }
  178. );
  179. parser.hooks.exportImportSpecifier.tap(
  180. "HarmonyExportDependencyParserPlugin",
  181. (statement, source, id, name, idx) => {
  182. const harmonyNamedExports = (parser.state.harmonyNamedExports =
  183. parser.state.harmonyNamedExports || new Set());
  184. let harmonyStarExports = null;
  185. if (name) {
  186. harmonyNamedExports.add(name);
  187. } else {
  188. harmonyStarExports = parser.state.harmonyStarExports =
  189. parser.state.harmonyStarExports || new HarmonyStarExportsList();
  190. }
  191. const attributes = getImportAttributes(statement);
  192. const dep = new HarmonyExportImportedSpecifierDependency(
  193. /** @type {string} */ (source),
  194. parser.state.lastHarmonyImportOrder,
  195. id ? [id] : [],
  196. name,
  197. harmonyNamedExports,
  198. harmonyStarExports && harmonyStarExports.slice(),
  199. exportPresenceMode,
  200. harmonyStarExports,
  201. attributes
  202. );
  203. if (harmonyStarExports) {
  204. harmonyStarExports.push(dep);
  205. }
  206. dep.loc = Object.create(
  207. /** @type {DependencyLocation} */ (statement.loc)
  208. );
  209. dep.loc.index = idx;
  210. const isAsiSafe = !parser.isAsiPosition(
  211. /** @type {Range} */
  212. (statement.range)[0]
  213. );
  214. if (!isAsiSafe) {
  215. parser.setAsiPosition(/** @type {Range} */ (statement.range)[1]);
  216. }
  217. parser.state.current.addDependency(dep);
  218. return true;
  219. }
  220. );
  221. }
  222. };