APIPlugin.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const {
  7. getExternalModuleNodeCommonjsInitFragment
  8. } = require("./ExternalModule");
  9. const {
  10. JAVASCRIPT_MODULE_TYPE_AUTO,
  11. JAVASCRIPT_MODULE_TYPE_DYNAMIC,
  12. JAVASCRIPT_MODULE_TYPE_ESM
  13. } = require("./ModuleTypeConstants");
  14. const RuntimeGlobals = require("./RuntimeGlobals");
  15. const WebpackError = require("./WebpackError");
  16. const ConstDependency = require("./dependencies/ConstDependency");
  17. const ModuleInitFragmentDependency = require("./dependencies/ModuleInitFragmentDependency");
  18. const RuntimeRequirementsDependency = require("./dependencies/RuntimeRequirementsDependency");
  19. const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
  20. const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
  21. const {
  22. evaluateToString,
  23. toConstantDependency
  24. } = require("./javascript/JavascriptParserHelpers");
  25. const ChunkNameRuntimeModule = require("./runtime/ChunkNameRuntimeModule");
  26. const GetFullHashRuntimeModule = require("./runtime/GetFullHashRuntimeModule");
  27. /** @typedef {import("./Compiler")} Compiler */
  28. /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
  29. /** @typedef {import("./Module").BuildInfo} BuildInfo */
  30. /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
  31. /** @typedef {import("./javascript/JavascriptParser").Range} Range */
  32. /**
  33. * Returns the replacement definitions used for webpack API identifiers.
  34. * @returns {Record<string, { expr: string, req: string[] | null, type?: string, assign: boolean }>} replacements
  35. */
  36. function getReplacements() {
  37. return {
  38. __webpack_require__: {
  39. expr: RuntimeGlobals.require,
  40. req: [RuntimeGlobals.require],
  41. type: "function",
  42. assign: false
  43. },
  44. __webpack_global__: {
  45. expr: RuntimeGlobals.require,
  46. req: [RuntimeGlobals.require],
  47. type: "function",
  48. assign: false
  49. },
  50. __webpack_public_path__: {
  51. expr: RuntimeGlobals.publicPath,
  52. req: [RuntimeGlobals.publicPath],
  53. type: "string",
  54. assign: true
  55. },
  56. __webpack_base_uri__: {
  57. expr: RuntimeGlobals.baseURI,
  58. req: [RuntimeGlobals.baseURI],
  59. type: "string",
  60. assign: true
  61. },
  62. __webpack_modules__: {
  63. expr: RuntimeGlobals.moduleFactories,
  64. req: [RuntimeGlobals.moduleFactories],
  65. type: "object",
  66. assign: false
  67. },
  68. __webpack_chunk_load__: {
  69. expr: RuntimeGlobals.ensureChunk,
  70. req: [RuntimeGlobals.ensureChunk],
  71. type: "function",
  72. assign: true
  73. },
  74. __non_webpack_require__: {
  75. expr: "require",
  76. req: null,
  77. type: undefined, // type is not known, depends on environment
  78. assign: true
  79. },
  80. __webpack_nonce__: {
  81. expr: RuntimeGlobals.scriptNonce,
  82. req: [RuntimeGlobals.scriptNonce],
  83. type: "string",
  84. assign: true
  85. },
  86. __webpack_hash__: {
  87. expr: `${RuntimeGlobals.getFullHash}()`,
  88. req: [RuntimeGlobals.getFullHash],
  89. type: "string",
  90. assign: false
  91. },
  92. __webpack_chunkname__: {
  93. expr: RuntimeGlobals.chunkName,
  94. req: [RuntimeGlobals.chunkName],
  95. type: "string",
  96. assign: false
  97. },
  98. __webpack_get_script_filename__: {
  99. expr: RuntimeGlobals.getChunkScriptFilename,
  100. req: [RuntimeGlobals.getChunkScriptFilename],
  101. type: "function",
  102. assign: true
  103. },
  104. __webpack_runtime_id__: {
  105. expr: RuntimeGlobals.runtimeId,
  106. req: [RuntimeGlobals.runtimeId],
  107. assign: false
  108. },
  109. "require.onError": {
  110. expr: RuntimeGlobals.uncaughtErrorHandler,
  111. req: [RuntimeGlobals.uncaughtErrorHandler],
  112. type: undefined, // type is not known, could be function or undefined
  113. assign: true // is never a pattern
  114. },
  115. __system_context__: {
  116. expr: RuntimeGlobals.systemContext,
  117. req: [RuntimeGlobals.systemContext],
  118. type: "object",
  119. assign: false
  120. },
  121. __webpack_share_scopes__: {
  122. expr: RuntimeGlobals.shareScopeMap,
  123. req: [RuntimeGlobals.shareScopeMap],
  124. type: "object",
  125. assign: false
  126. },
  127. __webpack_init_sharing__: {
  128. expr: RuntimeGlobals.initializeSharing,
  129. req: [RuntimeGlobals.initializeSharing],
  130. type: "function",
  131. assign: true
  132. }
  133. };
  134. }
  135. const PLUGIN_NAME = "APIPlugin";
  136. class APIPlugin {
  137. /**
  138. * Applies the plugin by registering its hooks on the compiler.
  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 moduleOutput = compilation.options.output.module;
  147. const nodeTarget = compiler.platform.node;
  148. const nodeEsm = moduleOutput && nodeTarget;
  149. const REPLACEMENTS = getReplacements();
  150. if (nodeEsm) {
  151. REPLACEMENTS.__non_webpack_require__.expr =
  152. "__WEBPACK_EXTERNAL_createRequire_require";
  153. }
  154. compilation.dependencyTemplates.set(
  155. ConstDependency,
  156. new ConstDependency.Template()
  157. );
  158. compilation.dependencyTemplates.set(
  159. ModuleInitFragmentDependency,
  160. new ModuleInitFragmentDependency.Template()
  161. );
  162. compilation.hooks.runtimeRequirementInTree
  163. .for(RuntimeGlobals.chunkName)
  164. .tap(PLUGIN_NAME, (chunk) => {
  165. compilation.addRuntimeModule(
  166. chunk,
  167. new ChunkNameRuntimeModule(/** @type {string} */ (chunk.name))
  168. );
  169. return true;
  170. });
  171. compilation.hooks.runtimeRequirementInTree
  172. .for(RuntimeGlobals.getFullHash)
  173. .tap(PLUGIN_NAME, (chunk, _set) => {
  174. compilation.addRuntimeModule(chunk, new GetFullHashRuntimeModule());
  175. return true;
  176. });
  177. const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
  178. hooks.renderModuleContent.tap(
  179. PLUGIN_NAME,
  180. (source, module, renderContext) => {
  181. if (/** @type {BuildInfo} */ (module.buildInfo).needCreateRequire) {
  182. const chunkInitFragments = [
  183. getExternalModuleNodeCommonjsInitFragment(
  184. renderContext.runtimeTemplate
  185. )
  186. ];
  187. renderContext.chunkInitFragments.push(...chunkInitFragments);
  188. }
  189. return source;
  190. }
  191. );
  192. /**
  193. * Handles the hook callback for this code path.
  194. * @param {JavascriptParser} parser the parser
  195. */
  196. const handler = (parser) => {
  197. parser.hooks.preDeclarator.tap(PLUGIN_NAME, (declarator) => {
  198. if (
  199. parser.scope.topLevelScope === true &&
  200. declarator.id.type === "Identifier" &&
  201. declarator.id.name === "module"
  202. ) {
  203. /** @type {BuildInfo} */
  204. (parser.state.module.buildInfo).moduleArgument =
  205. "__webpack_module__";
  206. }
  207. });
  208. parser.hooks.preStatement.tap(PLUGIN_NAME, (statement) => {
  209. if (parser.scope.topLevelScope === true) {
  210. if (
  211. statement.type === "FunctionDeclaration" &&
  212. statement.id &&
  213. statement.id.name === "module"
  214. ) {
  215. /** @type {BuildInfo} */
  216. (parser.state.module.buildInfo).moduleArgument =
  217. "__webpack_module__";
  218. } else if (
  219. statement.type === "ClassDeclaration" &&
  220. statement.id &&
  221. statement.id.name === "module"
  222. ) {
  223. /** @type {BuildInfo} */
  224. (parser.state.module.buildInfo).moduleArgument =
  225. "__webpack_module__";
  226. }
  227. }
  228. });
  229. for (const key of Object.keys(REPLACEMENTS)) {
  230. const info = REPLACEMENTS[key];
  231. parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expression) => {
  232. const dep = toConstantDependency(parser, info.expr, info.req);
  233. if (key === "__non_webpack_require__" && moduleOutput) {
  234. if (nodeTarget) {
  235. /** @type {BuildInfo} */
  236. (parser.state.module.buildInfo).needCreateRequire = true;
  237. } else {
  238. const warning = new WebpackError(
  239. `${PLUGIN_NAME}\n__non_webpack_require__ is only allowed in target node`
  240. );
  241. warning.loc = /** @type {DependencyLocation} */ (
  242. expression.loc
  243. );
  244. warning.module = parser.state.module;
  245. compilation.warnings.push(warning);
  246. }
  247. }
  248. return dep(expression);
  249. });
  250. if (info.assign === false) {
  251. parser.hooks.assign.for(key).tap(PLUGIN_NAME, (expr) => {
  252. const err = new WebpackError(`${key} must not be assigned`);
  253. err.loc = /** @type {DependencyLocation} */ (expr.loc);
  254. throw err;
  255. });
  256. }
  257. if (info.type) {
  258. parser.hooks.evaluateTypeof
  259. .for(key)
  260. .tap(PLUGIN_NAME, evaluateToString(info.type));
  261. }
  262. }
  263. parser.hooks.expression
  264. .for("__webpack_layer__")
  265. .tap(PLUGIN_NAME, (expr) => {
  266. const dep = new ConstDependency(
  267. JSON.stringify(parser.state.module.layer),
  268. /** @type {Range} */ (expr.range)
  269. );
  270. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  271. parser.state.module.addPresentationalDependency(dep);
  272. return true;
  273. });
  274. parser.hooks.evaluateIdentifier
  275. .for("__webpack_layer__")
  276. .tap(PLUGIN_NAME, (expr) =>
  277. (parser.state.module.layer === null
  278. ? new BasicEvaluatedExpression().setNull()
  279. : new BasicEvaluatedExpression().setString(
  280. parser.state.module.layer
  281. )
  282. ).setRange(/** @type {Range} */ (expr.range))
  283. );
  284. parser.hooks.evaluateTypeof
  285. .for("__webpack_layer__")
  286. .tap(PLUGIN_NAME, (expr) =>
  287. new BasicEvaluatedExpression()
  288. .setString(
  289. parser.state.module.layer === null ? "object" : "string"
  290. )
  291. .setRange(/** @type {Range} */ (expr.range))
  292. );
  293. parser.hooks.expression
  294. .for("__webpack_module__.id")
  295. .tap(PLUGIN_NAME, (expr) => {
  296. /** @type {BuildInfo} */
  297. (parser.state.module.buildInfo).moduleConcatenationBailout =
  298. "__webpack_module__.id";
  299. const moduleArgument = parser.state.module.moduleArgument;
  300. if (moduleArgument === "__webpack_module__") {
  301. const dep = new RuntimeRequirementsDependency([
  302. RuntimeGlobals.moduleId
  303. ]);
  304. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  305. parser.state.module.addPresentationalDependency(dep);
  306. } else {
  307. const initDep = new ModuleInitFragmentDependency(
  308. `var __webpack_internal_module_id__ = ${moduleArgument}.id;\n`,
  309. [RuntimeGlobals.moduleId],
  310. "__webpack_internal_module_id__"
  311. );
  312. parser.state.module.addPresentationalDependency(initDep);
  313. const dep = new ConstDependency(
  314. "__webpack_internal_module_id__",
  315. /** @type {Range} */ (expr.range),
  316. []
  317. );
  318. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  319. parser.state.module.addPresentationalDependency(dep);
  320. }
  321. return true;
  322. });
  323. parser.hooks.expression
  324. .for("__webpack_module__")
  325. .tap(PLUGIN_NAME, (expr) => {
  326. /** @type {BuildInfo} */
  327. (parser.state.module.buildInfo).moduleConcatenationBailout =
  328. "__webpack_module__";
  329. const moduleArgument = parser.state.module.moduleArgument;
  330. if (moduleArgument === "__webpack_module__") {
  331. const dep = new RuntimeRequirementsDependency([
  332. RuntimeGlobals.module
  333. ]);
  334. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  335. parser.state.module.addPresentationalDependency(dep);
  336. } else {
  337. const initDep = new ModuleInitFragmentDependency(
  338. `var __webpack_internal_module__ = ${moduleArgument};\n`,
  339. [RuntimeGlobals.module],
  340. "__webpack_internal_module__"
  341. );
  342. parser.state.module.addPresentationalDependency(initDep);
  343. const dep = new ConstDependency(
  344. "__webpack_internal_module__",
  345. /** @type {Range} */ (expr.range),
  346. []
  347. );
  348. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  349. parser.state.module.addPresentationalDependency(dep);
  350. }
  351. return true;
  352. });
  353. parser.hooks.evaluateTypeof
  354. .for("__webpack_module__")
  355. .tap(PLUGIN_NAME, evaluateToString("object"));
  356. };
  357. normalModuleFactory.hooks.parser
  358. .for(JAVASCRIPT_MODULE_TYPE_AUTO)
  359. .tap(PLUGIN_NAME, handler);
  360. normalModuleFactory.hooks.parser
  361. .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
  362. .tap(PLUGIN_NAME, handler);
  363. normalModuleFactory.hooks.parser
  364. .for(JAVASCRIPT_MODULE_TYPE_ESM)
  365. .tap(PLUGIN_NAME, handler);
  366. }
  367. );
  368. }
  369. }
  370. module.exports = APIPlugin;