APIPlugin.js 12 KB

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