APIPlugin.js 9.3 KB

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