ImportMetaPlugin.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 {
  9. JAVASCRIPT_MODULE_TYPE_AUTO,
  10. JAVASCRIPT_MODULE_TYPE_ESM
  11. } = require("../ModuleTypeConstants");
  12. const Template = require("../Template");
  13. const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression");
  14. const {
  15. evaluateToIdentifier,
  16. toConstantDependency,
  17. evaluateToString,
  18. evaluateToNumber
  19. } = require("../javascript/JavascriptParserHelpers");
  20. const memoize = require("../util/memoize");
  21. const propertyAccess = require("../util/propertyAccess");
  22. const ConstDependency = require("./ConstDependency");
  23. /** @typedef {import("estree").MemberExpression} MemberExpression */
  24. /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
  25. /** @typedef {import("../Compiler")} Compiler */
  26. /** @typedef {import("../NormalModule")} NormalModule */
  27. /** @typedef {import("../javascript/JavascriptParser")} Parser */
  28. const getCriticalDependencyWarning = memoize(() =>
  29. require("./CriticalDependencyWarning")
  30. );
  31. const PLUGIN_NAME = "ImportMetaPlugin";
  32. class ImportMetaPlugin {
  33. /**
  34. * @param {Compiler} compiler compiler
  35. */
  36. apply(compiler) {
  37. compiler.hooks.compilation.tap(
  38. PLUGIN_NAME,
  39. (compilation, { normalModuleFactory }) => {
  40. /**
  41. * @param {NormalModule} module module
  42. * @returns {string} file url
  43. */
  44. const getUrl = module => {
  45. return pathToFileURL(module.resource).toString();
  46. };
  47. /**
  48. * @param {Parser} parser parser parser
  49. * @param {JavascriptParserOptions} parserOptions parserOptions
  50. * @returns {void}
  51. */
  52. const parserHandler = (parser, { importMeta }) => {
  53. if (importMeta === false) {
  54. const { importMetaName } = compilation.outputOptions;
  55. if (importMetaName === "import.meta") return;
  56. parser.hooks.expression
  57. .for("import.meta")
  58. .tap(PLUGIN_NAME, metaProperty => {
  59. const dep = new ConstDependency(
  60. importMetaName,
  61. metaProperty.range
  62. );
  63. dep.loc = metaProperty.loc;
  64. parser.state.module.addPresentationalDependency(dep);
  65. return true;
  66. });
  67. return;
  68. }
  69. /// import.meta direct ///
  70. const webpackVersion = parseInt(
  71. require("../../package.json").version,
  72. 10
  73. );
  74. const importMetaUrl = () =>
  75. JSON.stringify(getUrl(parser.state.module));
  76. const importMetaWebpackVersion = () => JSON.stringify(webpackVersion);
  77. const importMetaUnknownProperty = members =>
  78. `${Template.toNormalComment(
  79. "unsupported import.meta." + members.join(".")
  80. )} undefined${propertyAccess(members, 1)}`;
  81. parser.hooks.typeof
  82. .for("import.meta")
  83. .tap(
  84. PLUGIN_NAME,
  85. toConstantDependency(parser, JSON.stringify("object"))
  86. );
  87. parser.hooks.expression
  88. .for("import.meta")
  89. .tap(PLUGIN_NAME, metaProperty => {
  90. const referencedPropertiesInDestructuring =
  91. parser.destructuringAssignmentPropertiesFor(metaProperty);
  92. if (!referencedPropertiesInDestructuring) {
  93. const CriticalDependencyWarning =
  94. getCriticalDependencyWarning();
  95. parser.state.module.addWarning(
  96. new ModuleDependencyWarning(
  97. parser.state.module,
  98. new CriticalDependencyWarning(
  99. "Accessing import.meta directly is unsupported (only property access or destructuring is supported)"
  100. ),
  101. metaProperty.loc
  102. )
  103. );
  104. const dep = new ConstDependency(
  105. `${
  106. parser.isAsiPosition(metaProperty.range[0]) ? ";" : ""
  107. }({})`,
  108. metaProperty.range
  109. );
  110. dep.loc = metaProperty.loc;
  111. parser.state.module.addPresentationalDependency(dep);
  112. return true;
  113. }
  114. let str = "";
  115. for (const prop of referencedPropertiesInDestructuring) {
  116. switch (prop) {
  117. case "url":
  118. str += `url: ${importMetaUrl()},`;
  119. break;
  120. case "webpack":
  121. str += `webpack: ${importMetaWebpackVersion()},`;
  122. break;
  123. default:
  124. str += `[${JSON.stringify(
  125. prop
  126. )}]: ${importMetaUnknownProperty([prop])},`;
  127. break;
  128. }
  129. }
  130. const dep = new ConstDependency(`({${str}})`, metaProperty.range);
  131. dep.loc = metaProperty.loc;
  132. parser.state.module.addPresentationalDependency(dep);
  133. return true;
  134. });
  135. parser.hooks.evaluateTypeof
  136. .for("import.meta")
  137. .tap(PLUGIN_NAME, evaluateToString("object"));
  138. parser.hooks.evaluateIdentifier.for("import.meta").tap(
  139. PLUGIN_NAME,
  140. evaluateToIdentifier("import.meta", "import.meta", () => [], true)
  141. );
  142. /// import.meta.url ///
  143. parser.hooks.typeof
  144. .for("import.meta.url")
  145. .tap(
  146. PLUGIN_NAME,
  147. toConstantDependency(parser, JSON.stringify("string"))
  148. );
  149. parser.hooks.expression
  150. .for("import.meta.url")
  151. .tap(PLUGIN_NAME, expr => {
  152. const dep = new ConstDependency(importMetaUrl(), expr.range);
  153. dep.loc = expr.loc;
  154. parser.state.module.addPresentationalDependency(dep);
  155. return true;
  156. });
  157. parser.hooks.evaluateTypeof
  158. .for("import.meta.url")
  159. .tap(PLUGIN_NAME, evaluateToString("string"));
  160. parser.hooks.evaluateIdentifier
  161. .for("import.meta.url")
  162. .tap(PLUGIN_NAME, expr => {
  163. return new BasicEvaluatedExpression()
  164. .setString(getUrl(parser.state.module))
  165. .setRange(expr.range);
  166. });
  167. /// import.meta.webpack ///
  168. parser.hooks.typeof
  169. .for("import.meta.webpack")
  170. .tap(
  171. PLUGIN_NAME,
  172. toConstantDependency(parser, JSON.stringify("number"))
  173. );
  174. parser.hooks.expression
  175. .for("import.meta.webpack")
  176. .tap(
  177. PLUGIN_NAME,
  178. toConstantDependency(parser, importMetaWebpackVersion())
  179. );
  180. parser.hooks.evaluateTypeof
  181. .for("import.meta.webpack")
  182. .tap(PLUGIN_NAME, evaluateToString("number"));
  183. parser.hooks.evaluateIdentifier
  184. .for("import.meta.webpack")
  185. .tap(PLUGIN_NAME, evaluateToNumber(webpackVersion));
  186. /// Unknown properties ///
  187. parser.hooks.unhandledExpressionMemberChain
  188. .for("import.meta")
  189. .tap(PLUGIN_NAME, (expr, members) => {
  190. const dep = new ConstDependency(
  191. importMetaUnknownProperty(members),
  192. expr.range
  193. );
  194. dep.loc = expr.loc;
  195. parser.state.module.addPresentationalDependency(dep);
  196. return true;
  197. });
  198. parser.hooks.evaluate
  199. .for("MemberExpression")
  200. .tap(PLUGIN_NAME, expression => {
  201. const expr = /** @type {MemberExpression} */ (expression);
  202. if (
  203. expr.object.type === "MetaProperty" &&
  204. expr.object.meta.name === "import" &&
  205. expr.object.property.name === "meta" &&
  206. expr.property.type ===
  207. (expr.computed ? "Literal" : "Identifier")
  208. ) {
  209. return new BasicEvaluatedExpression()
  210. .setUndefined()
  211. .setRange(expr.range);
  212. }
  213. });
  214. };
  215. normalModuleFactory.hooks.parser
  216. .for(JAVASCRIPT_MODULE_TYPE_AUTO)
  217. .tap(PLUGIN_NAME, parserHandler);
  218. normalModuleFactory.hooks.parser
  219. .for(JAVASCRIPT_MODULE_TYPE_ESM)
  220. .tap(PLUGIN_NAME, parserHandler);
  221. }
  222. );
  223. }
  224. }
  225. module.exports = ImportMetaPlugin;