HarmonyDetectionParserPlugin.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const EnvironmentNotSupportAsyncWarning = require("../EnvironmentNotSupportAsyncWarning");
  7. const { JAVASCRIPT_MODULE_TYPE_ESM } = require("../ModuleTypeConstants");
  8. const DynamicExports = require("./DynamicExports");
  9. const HarmonyCompatibilityDependency = require("./HarmonyCompatibilityDependency");
  10. const HarmonyExports = require("./HarmonyExports");
  11. /** @typedef {import("../Module").BuildMeta} BuildMeta */
  12. /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
  13. const PLUGIN_NAME = "HarmonyDetectionParserPlugin";
  14. module.exports = class HarmonyDetectionParserPlugin {
  15. /**
  16. * Applies the plugin by registering its hooks on the compiler.
  17. * @param {JavascriptParser} parser the parser
  18. * @returns {void}
  19. */
  20. apply(parser) {
  21. parser.hooks.program.tap(PLUGIN_NAME, (ast) => {
  22. const isStrictHarmony =
  23. parser.state.module.type === JAVASCRIPT_MODULE_TYPE_ESM;
  24. const isHarmony =
  25. isStrictHarmony ||
  26. ast.body.some(
  27. (statement) =>
  28. statement.type === "ImportDeclaration" ||
  29. statement.type === "ExportDefaultDeclaration" ||
  30. statement.type === "ExportNamedDeclaration" ||
  31. statement.type === "ExportAllDeclaration"
  32. );
  33. if (isHarmony) {
  34. const module = parser.state.module;
  35. const compatDep = new HarmonyCompatibilityDependency();
  36. compatDep.loc = {
  37. start: {
  38. line: -1,
  39. column: 0
  40. },
  41. end: {
  42. line: -1,
  43. column: 0
  44. },
  45. index: -3
  46. };
  47. module.addPresentationalDependency(compatDep);
  48. DynamicExports.bailout(parser.state);
  49. HarmonyExports.enable(parser.state, isStrictHarmony);
  50. parser.scope.isStrict = true;
  51. }
  52. });
  53. parser.hooks.topLevelAwait.tap(PLUGIN_NAME, () => {
  54. const module = parser.state.module;
  55. if (!HarmonyExports.isEnabled(parser.state)) {
  56. throw new Error(
  57. "Top-level-await is only supported in EcmaScript Modules"
  58. );
  59. }
  60. /** @type {BuildMeta} */
  61. (module.buildMeta).async = true;
  62. EnvironmentNotSupportAsyncWarning.check(
  63. module,
  64. parser.state.compilation.runtimeTemplate,
  65. "topLevelAwait"
  66. );
  67. });
  68. /**
  69. * Returns true if in harmony.
  70. * @returns {boolean | undefined} true if in harmony
  71. */
  72. const skipInHarmony = () => {
  73. if (HarmonyExports.isEnabled(parser.state)) {
  74. return true;
  75. }
  76. };
  77. /**
  78. * Returns null if in harmony.
  79. * @returns {null | undefined} null if in harmony
  80. */
  81. const nullInHarmony = () => {
  82. if (HarmonyExports.isEnabled(parser.state)) {
  83. return null;
  84. }
  85. };
  86. const nonHarmonyIdentifiers = ["define", "exports"];
  87. for (const identifier of nonHarmonyIdentifiers) {
  88. parser.hooks.evaluateTypeof
  89. .for(identifier)
  90. .tap(PLUGIN_NAME, nullInHarmony);
  91. parser.hooks.typeof.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
  92. parser.hooks.evaluate.for(identifier).tap(PLUGIN_NAME, nullInHarmony);
  93. parser.hooks.expression.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
  94. parser.hooks.call.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
  95. }
  96. }
  97. };