APIPlugin.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const InitFragment = require("./InitFragment");
  7. const {
  8. JAVASCRIPT_MODULE_TYPE_AUTO,
  9. JAVASCRIPT_MODULE_TYPE_DYNAMIC,
  10. JAVASCRIPT_MODULE_TYPE_ESM
  11. } = require("./ModuleTypeConstants");
  12. const RuntimeGlobals = require("./RuntimeGlobals");
  13. const WebpackError = require("./WebpackError");
  14. const ConstDependency = require("./dependencies/ConstDependency");
  15. const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
  16. const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
  17. const {
  18. toConstantDependency,
  19. evaluateToString
  20. } = require("./javascript/JavascriptParserHelpers");
  21. const ChunkNameRuntimeModule = require("./runtime/ChunkNameRuntimeModule");
  22. const GetFullHashRuntimeModule = require("./runtime/GetFullHashRuntimeModule");
  23. /** @typedef {import("./Compiler")} Compiler */
  24. /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
  25. /**
  26. * @param {boolean} module true if ES module
  27. * @param {string} importMetaName `import.meta` name
  28. * @returns {Record<string, {expr: string, req: string[] | null, type?: string, assign: boolean}>} replacements
  29. */
  30. function getReplacements(module, importMetaName) {
  31. return {
  32. __webpack_require__: {
  33. expr: RuntimeGlobals.require,
  34. req: [RuntimeGlobals.require],
  35. type: "function",
  36. assign: false
  37. },
  38. __webpack_public_path__: {
  39. expr: RuntimeGlobals.publicPath,
  40. req: [RuntimeGlobals.publicPath],
  41. type: "string",
  42. assign: true
  43. },
  44. __webpack_base_uri__: {
  45. expr: RuntimeGlobals.baseURI,
  46. req: [RuntimeGlobals.baseURI],
  47. type: "string",
  48. assign: true
  49. },
  50. __webpack_modules__: {
  51. expr: RuntimeGlobals.moduleFactories,
  52. req: [RuntimeGlobals.moduleFactories],
  53. type: "object",
  54. assign: false
  55. },
  56. __webpack_chunk_load__: {
  57. expr: RuntimeGlobals.ensureChunk,
  58. req: [RuntimeGlobals.ensureChunk],
  59. type: "function",
  60. assign: true
  61. },
  62. __non_webpack_require__: {
  63. expr: module
  64. ? `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)`
  65. : "require",
  66. req: null,
  67. type: undefined, // type is not known, depends on environment
  68. assign: true
  69. },
  70. __webpack_nonce__: {
  71. expr: RuntimeGlobals.scriptNonce,
  72. req: [RuntimeGlobals.scriptNonce],
  73. type: "string",
  74. assign: true
  75. },
  76. __webpack_hash__: {
  77. expr: `${RuntimeGlobals.getFullHash}()`,
  78. req: [RuntimeGlobals.getFullHash],
  79. type: "string",
  80. assign: false
  81. },
  82. __webpack_chunkname__: {
  83. expr: RuntimeGlobals.chunkName,
  84. req: [RuntimeGlobals.chunkName],
  85. type: "string",
  86. assign: false
  87. },
  88. __webpack_get_script_filename__: {
  89. expr: RuntimeGlobals.getChunkScriptFilename,
  90. req: [RuntimeGlobals.getChunkScriptFilename],
  91. type: "function",
  92. assign: true
  93. },
  94. __webpack_runtime_id__: {
  95. expr: RuntimeGlobals.runtimeId,
  96. req: [RuntimeGlobals.runtimeId],
  97. assign: false
  98. },
  99. "require.onError": {
  100. expr: RuntimeGlobals.uncaughtErrorHandler,
  101. req: [RuntimeGlobals.uncaughtErrorHandler],
  102. type: undefined, // type is not known, could be function or undefined
  103. assign: true // is never a pattern
  104. },
  105. __system_context__: {
  106. expr: RuntimeGlobals.systemContext,
  107. req: [RuntimeGlobals.systemContext],
  108. type: "object",
  109. assign: false
  110. },
  111. __webpack_share_scopes__: {
  112. expr: RuntimeGlobals.shareScopeMap,
  113. req: [RuntimeGlobals.shareScopeMap],
  114. type: "object",
  115. assign: false
  116. },
  117. __webpack_init_sharing__: {
  118. expr: RuntimeGlobals.initializeSharing,
  119. req: [RuntimeGlobals.initializeSharing],
  120. type: "function",
  121. assign: true
  122. }
  123. };
  124. }
  125. const PLUGIN_NAME = "APIPlugin";
  126. /**
  127. * @typedef {Object} APIPluginOptions
  128. * @property {boolean} [module] the output filename
  129. */
  130. class APIPlugin {
  131. /**
  132. * @param {APIPluginOptions} [options] options
  133. */
  134. constructor(options = {}) {
  135. this.options = options;
  136. }
  137. /**
  138. * Apply the plugin
  139. * @param {Compiler} compiler the compiler instance
  140. * @returns {void}
  141. */
  142. apply(compiler) {
  143. compiler.hooks.compilation.tap(
  144. PLUGIN_NAME,
  145. (compilation, { normalModuleFactory }) => {
  146. const { importMetaName } = compilation.outputOptions;
  147. const REPLACEMENTS = getReplacements(
  148. this.options.module,
  149. importMetaName
  150. );
  151. compilation.dependencyTemplates.set(
  152. ConstDependency,
  153. new ConstDependency.Template()
  154. );
  155. compilation.hooks.runtimeRequirementInTree
  156. .for(RuntimeGlobals.chunkName)
  157. .tap(PLUGIN_NAME, chunk => {
  158. compilation.addRuntimeModule(
  159. chunk,
  160. new ChunkNameRuntimeModule(chunk.name)
  161. );
  162. return true;
  163. });
  164. compilation.hooks.runtimeRequirementInTree
  165. .for(RuntimeGlobals.getFullHash)
  166. .tap(PLUGIN_NAME, (chunk, set) => {
  167. compilation.addRuntimeModule(chunk, new GetFullHashRuntimeModule());
  168. return true;
  169. });
  170. const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
  171. hooks.renderModuleContent.tap(
  172. PLUGIN_NAME,
  173. (source, module, renderContext) => {
  174. if (module.buildInfo.needCreateRequire) {
  175. const chunkInitFragments = [
  176. new InitFragment(
  177. 'import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module";\n',
  178. InitFragment.STAGE_HARMONY_IMPORTS,
  179. 0,
  180. "external module node-commonjs"
  181. )
  182. ];
  183. renderContext.chunkInitFragments.push(...chunkInitFragments);
  184. }
  185. return source;
  186. }
  187. );
  188. /**
  189. * @param {JavascriptParser} parser the parser
  190. */
  191. const handler = parser => {
  192. Object.keys(REPLACEMENTS).forEach(key => {
  193. const info = REPLACEMENTS[key];
  194. parser.hooks.expression.for(key).tap(PLUGIN_NAME, expression => {
  195. const dep = toConstantDependency(parser, info.expr, info.req);
  196. if (key === "__non_webpack_require__" && this.options.module) {
  197. parser.state.module.buildInfo.needCreateRequire = true;
  198. }
  199. return dep(expression);
  200. });
  201. if (info.assign === false) {
  202. parser.hooks.assign.for(key).tap(PLUGIN_NAME, expr => {
  203. const err = new WebpackError(`${key} must not be assigned`);
  204. err.loc = expr.loc;
  205. throw err;
  206. });
  207. }
  208. if (info.type) {
  209. parser.hooks.evaluateTypeof
  210. .for(key)
  211. .tap(PLUGIN_NAME, evaluateToString(info.type));
  212. }
  213. });
  214. parser.hooks.expression
  215. .for("__webpack_layer__")
  216. .tap(PLUGIN_NAME, expr => {
  217. const dep = new ConstDependency(
  218. JSON.stringify(parser.state.module.layer),
  219. expr.range
  220. );
  221. dep.loc = expr.loc;
  222. parser.state.module.addPresentationalDependency(dep);
  223. return true;
  224. });
  225. parser.hooks.evaluateIdentifier
  226. .for("__webpack_layer__")
  227. .tap(PLUGIN_NAME, expr =>
  228. (parser.state.module.layer === null
  229. ? new BasicEvaluatedExpression().setNull()
  230. : new BasicEvaluatedExpression().setString(
  231. parser.state.module.layer
  232. )
  233. ).setRange(expr.range)
  234. );
  235. parser.hooks.evaluateTypeof
  236. .for("__webpack_layer__")
  237. .tap(PLUGIN_NAME, expr =>
  238. new BasicEvaluatedExpression()
  239. .setString(
  240. parser.state.module.layer === null ? "object" : "string"
  241. )
  242. .setRange(expr.range)
  243. );
  244. parser.hooks.expression
  245. .for("__webpack_module__.id")
  246. .tap(PLUGIN_NAME, expr => {
  247. parser.state.module.buildInfo.moduleConcatenationBailout =
  248. "__webpack_module__.id";
  249. const dep = new ConstDependency(
  250. parser.state.module.moduleArgument + ".id",
  251. expr.range,
  252. [RuntimeGlobals.moduleId]
  253. );
  254. dep.loc = expr.loc;
  255. parser.state.module.addPresentationalDependency(dep);
  256. return true;
  257. });
  258. parser.hooks.expression
  259. .for("__webpack_module__")
  260. .tap(PLUGIN_NAME, expr => {
  261. parser.state.module.buildInfo.moduleConcatenationBailout =
  262. "__webpack_module__";
  263. const dep = new ConstDependency(
  264. parser.state.module.moduleArgument,
  265. expr.range,
  266. [RuntimeGlobals.module]
  267. );
  268. dep.loc = expr.loc;
  269. parser.state.module.addPresentationalDependency(dep);
  270. return true;
  271. });
  272. parser.hooks.evaluateTypeof
  273. .for("__webpack_module__")
  274. .tap(PLUGIN_NAME, evaluateToString("object"));
  275. };
  276. normalModuleFactory.hooks.parser
  277. .for(JAVASCRIPT_MODULE_TYPE_AUTO)
  278. .tap(PLUGIN_NAME, handler);
  279. normalModuleFactory.hooks.parser
  280. .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
  281. .tap(PLUGIN_NAME, handler);
  282. normalModuleFactory.hooks.parser
  283. .for(JAVASCRIPT_MODULE_TYPE_ESM)
  284. .tap(PLUGIN_NAME, handler);
  285. }
  286. );
  287. }
  288. }
  289. module.exports = APIPlugin;