提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
9.2 KiB

  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 BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
  18. const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
  19. const {
  20. evaluateToString,
  21. toConstantDependency
  22. } = require("./javascript/JavascriptParserHelpers");
  23. const ChunkNameRuntimeModule = require("./runtime/ChunkNameRuntimeModule");
  24. const GetFullHashRuntimeModule = require("./runtime/GetFullHashRuntimeModule");
  25. /** @typedef {import("./Compiler")} Compiler */
  26. /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
  27. /** @typedef {import("./Module").BuildInfo} BuildInfo */
  28. /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
  29. /** @typedef {import("./javascript/JavascriptParser").Range} Range */
  30. /**
  31. * @returns {Record<string, {expr: string, req: string[] | null, type?: string, assign: boolean}>} replacements
  32. */
  33. function getReplacements() {
  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: "require",
  67. req: null,
  68. type: undefined, // type is not known, depends on environment
  69. assign: true
  70. },
  71. __webpack_nonce__: {
  72. expr: RuntimeGlobals.scriptNonce,
  73. req: [RuntimeGlobals.scriptNonce],
  74. type: "string",
  75. assign: true
  76. },
  77. __webpack_hash__: {
  78. expr: `${RuntimeGlobals.getFullHash}()`,
  79. req: [RuntimeGlobals.getFullHash],
  80. type: "string",
  81. assign: false
  82. },
  83. __webpack_chunkname__: {
  84. expr: RuntimeGlobals.chunkName,
  85. req: [RuntimeGlobals.chunkName],
  86. type: "string",
  87. assign: false
  88. },
  89. __webpack_get_script_filename__: {
  90. expr: RuntimeGlobals.getChunkScriptFilename,
  91. req: [RuntimeGlobals.getChunkScriptFilename],
  92. type: "function",
  93. assign: true
  94. },
  95. __webpack_runtime_id__: {
  96. expr: RuntimeGlobals.runtimeId,
  97. req: [RuntimeGlobals.runtimeId],
  98. assign: false
  99. },
  100. "require.onError": {
  101. expr: RuntimeGlobals.uncaughtErrorHandler,
  102. req: [RuntimeGlobals.uncaughtErrorHandler],
  103. type: undefined, // type is not known, could be function or undefined
  104. assign: true // is never a pattern
  105. },
  106. __system_context__: {
  107. expr: RuntimeGlobals.systemContext,
  108. req: [RuntimeGlobals.systemContext],
  109. type: "object",
  110. assign: false
  111. },
  112. __webpack_share_scopes__: {
  113. expr: RuntimeGlobals.shareScopeMap,
  114. req: [RuntimeGlobals.shareScopeMap],
  115. type: "object",
  116. assign: false
  117. },
  118. __webpack_init_sharing__: {
  119. expr: RuntimeGlobals.initializeSharing,
  120. req: [RuntimeGlobals.initializeSharing],
  121. type: "function",
  122. assign: true
  123. }
  124. };
  125. }
  126. const PLUGIN_NAME = "APIPlugin";
  127. class APIPlugin {
  128. /**
  129. * Apply the plugin
  130. * @param {Compiler} compiler the compiler instance
  131. * @returns {void}
  132. */
  133. apply(compiler) {
  134. compiler.hooks.compilation.tap(
  135. PLUGIN_NAME,
  136. (compilation, { normalModuleFactory }) => {
  137. const moduleOutput = compilation.options.output.module;
  138. const nodeTarget = compiler.platform.node;
  139. const nodeEsm = moduleOutput && nodeTarget;
  140. const REPLACEMENTS = getReplacements();
  141. if (nodeEsm) {
  142. REPLACEMENTS.__non_webpack_require__.expr =
  143. "__WEBPACK_EXTERNAL_createRequire_require";
  144. }
  145. compilation.dependencyTemplates.set(
  146. ConstDependency,
  147. new ConstDependency.Template()
  148. );
  149. compilation.hooks.runtimeRequirementInTree
  150. .for(RuntimeGlobals.chunkName)
  151. .tap(PLUGIN_NAME, (chunk) => {
  152. compilation.addRuntimeModule(
  153. chunk,
  154. new ChunkNameRuntimeModule(/** @type {string} */ (chunk.name))
  155. );
  156. return true;
  157. });
  158. compilation.hooks.runtimeRequirementInTree
  159. .for(RuntimeGlobals.getFullHash)
  160. .tap(PLUGIN_NAME, (chunk, _set) => {
  161. compilation.addRuntimeModule(chunk, new GetFullHashRuntimeModule());
  162. return true;
  163. });
  164. const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
  165. hooks.renderModuleContent.tap(
  166. PLUGIN_NAME,
  167. (source, module, renderContext) => {
  168. if (/** @type {BuildInfo} */ (module.buildInfo).needCreateRequire) {
  169. const chunkInitFragments = [
  170. getExternalModuleNodeCommonjsInitFragment(
  171. renderContext.runtimeTemplate
  172. )
  173. ];
  174. renderContext.chunkInitFragments.push(...chunkInitFragments);
  175. }
  176. return source;
  177. }
  178. );
  179. /**
  180. * @param {JavascriptParser} parser the parser
  181. */
  182. const handler = (parser) => {
  183. for (const key of Object.keys(REPLACEMENTS)) {
  184. const info = REPLACEMENTS[key];
  185. parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expression) => {
  186. const dep = toConstantDependency(parser, info.expr, info.req);
  187. if (key === "__non_webpack_require__" && moduleOutput) {
  188. if (nodeTarget) {
  189. /** @type {BuildInfo} */
  190. (parser.state.module.buildInfo).needCreateRequire = true;
  191. } else {
  192. const warning = new WebpackError(
  193. `${PLUGIN_NAME}\n__non_webpack_require__ is only allowed in target node`
  194. );
  195. warning.loc = /** @type {DependencyLocation} */ (
  196. expression.loc
  197. );
  198. warning.module = parser.state.module;
  199. compilation.warnings.push(warning);
  200. }
  201. }
  202. return dep(expression);
  203. });
  204. if (info.assign === false) {
  205. parser.hooks.assign.for(key).tap(PLUGIN_NAME, (expr) => {
  206. const err = new WebpackError(`${key} must not be assigned`);
  207. err.loc = /** @type {DependencyLocation} */ (expr.loc);
  208. throw err;
  209. });
  210. }
  211. if (info.type) {
  212. parser.hooks.evaluateTypeof
  213. .for(key)
  214. .tap(PLUGIN_NAME, evaluateToString(info.type));
  215. }
  216. }
  217. parser.hooks.expression
  218. .for("__webpack_layer__")
  219. .tap(PLUGIN_NAME, (expr) => {
  220. const dep = new ConstDependency(
  221. JSON.stringify(parser.state.module.layer),
  222. /** @type {Range} */ (expr.range)
  223. );
  224. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  225. parser.state.module.addPresentationalDependency(dep);
  226. return true;
  227. });
  228. parser.hooks.evaluateIdentifier
  229. .for("__webpack_layer__")
  230. .tap(PLUGIN_NAME, (expr) =>
  231. (parser.state.module.layer === null
  232. ? new BasicEvaluatedExpression().setNull()
  233. : new BasicEvaluatedExpression().setString(
  234. parser.state.module.layer
  235. )
  236. ).setRange(/** @type {Range} */ (expr.range))
  237. );
  238. parser.hooks.evaluateTypeof
  239. .for("__webpack_layer__")
  240. .tap(PLUGIN_NAME, (expr) =>
  241. new BasicEvaluatedExpression()
  242. .setString(
  243. parser.state.module.layer === null ? "object" : "string"
  244. )
  245. .setRange(/** @type {Range} */ (expr.range))
  246. );
  247. parser.hooks.expression
  248. .for("__webpack_module__.id")
  249. .tap(PLUGIN_NAME, (expr) => {
  250. /** @type {BuildInfo} */
  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. /** @type {BuildInfo} */
  266. (parser.state.module.buildInfo).moduleConcatenationBailout =
  267. "__webpack_module__";
  268. const dep = new ConstDependency(
  269. parser.state.module.moduleArgument,
  270. /** @type {Range} */ (expr.range),
  271. [RuntimeGlobals.module]
  272. );
  273. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  274. parser.state.module.addPresentationalDependency(dep);
  275. return true;
  276. });
  277. parser.hooks.evaluateTypeof
  278. .for("__webpack_module__")
  279. .tap(PLUGIN_NAME, evaluateToString("object"));
  280. };
  281. normalModuleFactory.hooks.parser
  282. .for(JAVASCRIPT_MODULE_TYPE_AUTO)
  283. .tap(PLUGIN_NAME, handler);
  284. normalModuleFactory.hooks.parser
  285. .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
  286. .tap(PLUGIN_NAME, handler);
  287. normalModuleFactory.hooks.parser
  288. .for(JAVASCRIPT_MODULE_TYPE_ESM)
  289. .tap(PLUGIN_NAME, handler);
  290. }
  291. );
  292. }
  293. }
  294. module.exports = APIPlugin;