APIPlugin.js 9.0 KB

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