ImportMetaPlugin.js 8.1 KB

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