ImportMetaPlugin.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Ivan Kopeykin @vankop
  4. */
  5. "use strict";
  6. const { pathToFileURL } = require("url");
  7. const ModuleDependencyWarning = require("../ModuleDependencyWarning");
  8. const Template = require("../Template");
  9. const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression");
  10. const {
  11. evaluateToIdentifier,
  12. toConstantDependency,
  13. evaluateToString,
  14. evaluateToNumber
  15. } = require("../javascript/JavascriptParserHelpers");
  16. const memoize = require("../util/memoize");
  17. const propertyAccess = require("../util/propertyAccess");
  18. const ConstDependency = require("./ConstDependency");
  19. /** @typedef {import("estree").MemberExpression} MemberExpression */
  20. /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
  21. /** @typedef {import("../Compiler")} Compiler */
  22. /** @typedef {import("../NormalModule")} NormalModule */
  23. /** @typedef {import("../javascript/JavascriptParser")} Parser */
  24. const getCriticalDependencyWarning = memoize(() =>
  25. require("./CriticalDependencyWarning")
  26. );
  27. class ImportMetaPlugin {
  28. /**
  29. * @param {Compiler} compiler compiler
  30. */
  31. apply(compiler) {
  32. compiler.hooks.compilation.tap(
  33. "ImportMetaPlugin",
  34. (compilation, { normalModuleFactory }) => {
  35. /**
  36. * @param {NormalModule} module module
  37. * @returns {string} file url
  38. */
  39. const getUrl = module => {
  40. return pathToFileURL(module.resource).toString();
  41. };
  42. /**
  43. * @param {Parser} parser parser parser
  44. * @param {JavascriptParserOptions} parserOptions parserOptions
  45. * @returns {void}
  46. */
  47. const parserHandler = (parser, { importMeta }) => {
  48. if (importMeta === false) {
  49. const { importMetaName } = compilation.outputOptions;
  50. if (importMetaName === "import.meta") return;
  51. parser.hooks.expression
  52. .for("import.meta")
  53. .tap("ImportMetaPlugin", metaProperty => {
  54. const dep = new ConstDependency(
  55. importMetaName,
  56. metaProperty.range
  57. );
  58. dep.loc = metaProperty.loc;
  59. parser.state.module.addPresentationalDependency(dep);
  60. return true;
  61. });
  62. return;
  63. }
  64. /// import.meta direct ///
  65. parser.hooks.typeof
  66. .for("import.meta")
  67. .tap(
  68. "ImportMetaPlugin",
  69. toConstantDependency(parser, JSON.stringify("object"))
  70. );
  71. parser.hooks.expression
  72. .for("import.meta")
  73. .tap("ImportMetaPlugin", metaProperty => {
  74. const CriticalDependencyWarning = getCriticalDependencyWarning();
  75. parser.state.module.addWarning(
  76. new ModuleDependencyWarning(
  77. parser.state.module,
  78. new CriticalDependencyWarning(
  79. "Accessing import.meta directly is unsupported (only property access is supported)"
  80. ),
  81. metaProperty.loc
  82. )
  83. );
  84. const dep = new ConstDependency(
  85. `${parser.isAsiPosition(metaProperty.range[0]) ? ";" : ""}({})`,
  86. metaProperty.range
  87. );
  88. dep.loc = metaProperty.loc;
  89. parser.state.module.addPresentationalDependency(dep);
  90. return true;
  91. });
  92. parser.hooks.evaluateTypeof
  93. .for("import.meta")
  94. .tap("ImportMetaPlugin", evaluateToString("object"));
  95. parser.hooks.evaluateIdentifier.for("import.meta").tap(
  96. "ImportMetaPlugin",
  97. evaluateToIdentifier("import.meta", "import.meta", () => [], true)
  98. );
  99. /// import.meta.url ///
  100. parser.hooks.typeof
  101. .for("import.meta.url")
  102. .tap(
  103. "ImportMetaPlugin",
  104. toConstantDependency(parser, JSON.stringify("string"))
  105. );
  106. parser.hooks.expression
  107. .for("import.meta.url")
  108. .tap("ImportMetaPlugin", expr => {
  109. const dep = new ConstDependency(
  110. JSON.stringify(getUrl(parser.state.module)),
  111. expr.range
  112. );
  113. dep.loc = expr.loc;
  114. parser.state.module.addPresentationalDependency(dep);
  115. return true;
  116. });
  117. parser.hooks.evaluateTypeof
  118. .for("import.meta.url")
  119. .tap("ImportMetaPlugin", evaluateToString("string"));
  120. parser.hooks.evaluateIdentifier
  121. .for("import.meta.url")
  122. .tap("ImportMetaPlugin", expr => {
  123. return new BasicEvaluatedExpression()
  124. .setString(getUrl(parser.state.module))
  125. .setRange(expr.range);
  126. });
  127. /// import.meta.webpack ///
  128. const webpackVersion = parseInt(
  129. require("../../package.json").version,
  130. 10
  131. );
  132. parser.hooks.typeof
  133. .for("import.meta.webpack")
  134. .tap(
  135. "ImportMetaPlugin",
  136. toConstantDependency(parser, JSON.stringify("number"))
  137. );
  138. parser.hooks.expression
  139. .for("import.meta.webpack")
  140. .tap(
  141. "ImportMetaPlugin",
  142. toConstantDependency(parser, JSON.stringify(webpackVersion))
  143. );
  144. parser.hooks.evaluateTypeof
  145. .for("import.meta.webpack")
  146. .tap("ImportMetaPlugin", evaluateToString("number"));
  147. parser.hooks.evaluateIdentifier
  148. .for("import.meta.webpack")
  149. .tap("ImportMetaPlugin", evaluateToNumber(webpackVersion));
  150. /// Unknown properties ///
  151. parser.hooks.unhandledExpressionMemberChain
  152. .for("import.meta")
  153. .tap("ImportMetaPlugin", (expr, members) => {
  154. const dep = new ConstDependency(
  155. `${Template.toNormalComment(
  156. "unsupported import.meta." + members.join(".")
  157. )} undefined${propertyAccess(members, 1)}`,
  158. expr.range
  159. );
  160. dep.loc = expr.loc;
  161. parser.state.module.addPresentationalDependency(dep);
  162. return true;
  163. });
  164. parser.hooks.evaluate
  165. .for("MemberExpression")
  166. .tap("ImportMetaPlugin", expression => {
  167. const expr = /** @type {MemberExpression} */ (expression);
  168. if (
  169. expr.object.type === "MetaProperty" &&
  170. expr.object.meta.name === "import" &&
  171. expr.object.property.name === "meta" &&
  172. expr.property.type ===
  173. (expr.computed ? "Literal" : "Identifier")
  174. ) {
  175. return new BasicEvaluatedExpression()
  176. .setUndefined()
  177. .setRange(expr.range);
  178. }
  179. });
  180. };
  181. normalModuleFactory.hooks.parser
  182. .for("javascript/auto")
  183. .tap("ImportMetaPlugin", parserHandler);
  184. normalModuleFactory.hooks.parser
  185. .for("javascript/esm")
  186. .tap("ImportMetaPlugin", parserHandler);
  187. }
  188. );
  189. }
  190. }
  191. module.exports = ImportMetaPlugin;