CommonJsSelfReferenceDependency.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RuntimeGlobals = require("../RuntimeGlobals");
  7. const { equals } = require("../util/ArrayHelpers");
  8. const makeSerializable = require("../util/makeSerializable");
  9. const { propertyAccess } = require("../util/property");
  10. const { isWorkerEntryThis } = require("./CommonJsDependencyHelpers");
  11. const NullDependency = require("./NullDependency");
  12. /** @import { ReplaceSource } from "webpack-sources" */
  13. /**
  14. * @import Dependency, {
  15. * ReferencedExports,
  16. * UpdateHashContext,
  17. * ExportInfoName
  18. * } from "../Dependency"
  19. */
  20. /** @import Hash from "../util/Hash" */
  21. /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
  22. /** @import ModuleGraph from "../ModuleGraph" */
  23. /** @import { Range } from "../javascript/JavascriptParser" */
  24. /** @import { RuntimeSpec } from "../util/runtime" */
  25. /**
  26. * @import {
  27. * CommonJSDependencyBaseKeywords
  28. * } from "./CommonJsDependencyHelpers"
  29. */
  30. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[Range, CommonJSDependencyBaseKeywords, ExportInfoName[], boolean]>} ObjectDeserializerContext */
  31. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[Range, CommonJSDependencyBaseKeywords, ExportInfoName[], boolean]>} ObjectSerializerContext */
  32. class CommonJsSelfReferenceDependency extends NullDependency {
  33. /**
  34. * Creates an instance of CommonJsSelfReferenceDependency.
  35. * @param {Range} range range
  36. * @param {CommonJSDependencyBaseKeywords} base base
  37. * @param {ExportInfoName[]} names names
  38. * @param {boolean} call is a call
  39. */
  40. constructor(range, base, names, call) {
  41. super();
  42. this.range = range;
  43. /** @type {CommonJSDependencyBaseKeywords} */
  44. this.base = base;
  45. /** @type {string[]} */
  46. this.names = names;
  47. /** @type {boolean} */
  48. this.call = call;
  49. }
  50. get type() {
  51. return "cjs self exports reference";
  52. }
  53. get category() {
  54. return "self";
  55. }
  56. /**
  57. * Returns true if this dependency can be concatenated
  58. * @param {boolean} concatenateCommonJsModules whether optimization.concatenateModules.commonjs is enabled
  59. * @returns {boolean} true if this dependency can be concatenated
  60. */
  61. canConcatenate(concatenateCommonJsModules) {
  62. return concatenateCommonJsModules;
  63. }
  64. /**
  65. * Returns an identifier to merge equal requests.
  66. * @returns {string | null} an identifier to merge equal requests
  67. */
  68. getResourceIdentifier() {
  69. return "self";
  70. }
  71. /**
  72. * Returns list of exports referenced by this dependency
  73. * @param {ModuleGraph} moduleGraph module graph
  74. * @param {RuntimeSpec} runtime the runtime for which the module is analysed
  75. * @returns {ReferencedExports} referenced exports
  76. */
  77. getReferencedExports(moduleGraph, runtime) {
  78. return [this.call ? this.names.slice(0, -1) : this.names];
  79. }
  80. /**
  81. * Updates the hash with the data contributed by this instance.
  82. * @param {Hash} hash hash to be updated
  83. * @param {UpdateHashContext} context context
  84. * @returns {void}
  85. */
  86. updateHash(hash, context) {
  87. // a classic worker entry generates `this` as the global scope, so it must
  88. // not share one code generation with a runtime that generates it as exports
  89. if (this.base !== "this") return;
  90. const { chunkGraph, runtime, runtimeTemplate } = context;
  91. const module = chunkGraph.moduleGraph.getParentModule(this);
  92. hash.update(
  93. module !== undefined &&
  94. isWorkerEntryThis(module, chunkGraph, runtime, runtimeTemplate)
  95. ? "worker global"
  96. : "exports"
  97. );
  98. }
  99. /**
  100. * Serializes this instance into the provided serializer context.
  101. * @param {ObjectSerializerContext} context context
  102. */
  103. serialize(context) {
  104. context
  105. .write(this.range)
  106. .write(this.base)
  107. .write(this.names)
  108. .write(this.call);
  109. super.serialize(context);
  110. }
  111. /**
  112. * Restores this instance from the provided deserializer context.
  113. * @param {ObjectDeserializerContext} context context
  114. */
  115. deserialize(context) {
  116. this.range = context.read();
  117. const c1 = context.rest;
  118. this.base = c1.read();
  119. const c2 = c1.rest;
  120. this.names = c2.read();
  121. const c3 = c2.rest;
  122. this.call = c3.read();
  123. super.deserialize(c3.rest);
  124. }
  125. }
  126. makeSerializable(
  127. CommonJsSelfReferenceDependency,
  128. "webpack/lib/dependencies/CommonJsSelfReferenceDependency"
  129. );
  130. CommonJsSelfReferenceDependency.Template = class CommonJsSelfReferenceDependencyTemplate extends (
  131. NullDependency.Template
  132. ) {
  133. /**
  134. * Applies the plugin by registering its hooks on the compiler.
  135. * @param {Dependency} dependency the dependency for which the template should be applied
  136. * @param {ReplaceSource} source the current replace source which can be modified
  137. * @param {DependencyTemplateContext} templateContext the context object
  138. * @returns {void}
  139. */
  140. apply(
  141. dependency,
  142. source,
  143. {
  144. module,
  145. moduleGraph,
  146. chunkGraph,
  147. runtime,
  148. runtimeRequirements,
  149. runtimeTemplate
  150. }
  151. ) {
  152. const dep = /** @type {CommonJsSelfReferenceDependency} */ (dependency);
  153. // a classic worker entry reads its global scope, not an export, so the
  154. // names stay as written
  155. const isWorkerEntry =
  156. dep.base === "this" &&
  157. isWorkerEntryThis(module, chunkGraph, runtime, runtimeTemplate);
  158. // CJS exports are never inlined
  159. const used =
  160. isWorkerEntry || dep.names.length === 0
  161. ? dep.names
  162. : /** @type {string | string[] | false} */ (
  163. moduleGraph.getExportsInfo(module).getUsedName(dep.names, runtime)
  164. );
  165. if (!used) {
  166. throw new Error(
  167. "Self-reference dependency has unused export name: This should not happen"
  168. );
  169. }
  170. /** @type {string} */
  171. let base;
  172. switch (dep.base) {
  173. case "exports":
  174. runtimeRequirements.add(RuntimeGlobals.exports);
  175. base = module.exportsArgument;
  176. break;
  177. case "module.exports":
  178. runtimeRequirements.add(RuntimeGlobals.module);
  179. base = `${module.moduleArgument}.exports`;
  180. break;
  181. case "this":
  182. if (isWorkerEntry) {
  183. runtimeRequirements.add(RuntimeGlobals.global);
  184. base = RuntimeGlobals.global;
  185. } else {
  186. runtimeRequirements.add(RuntimeGlobals.thisAsExports);
  187. base = "this";
  188. }
  189. break;
  190. default:
  191. throw new Error(`Unsupported base ${dep.base}`);
  192. }
  193. if (base === dep.base && equals(used, dep.names)) {
  194. // Nothing has to be changed
  195. // We don't use a replacement for compat reasons
  196. // for plugins that update `module._source` which they
  197. // shouldn't do!
  198. return;
  199. }
  200. source.replace(
  201. dep.range[0],
  202. dep.range[1] - 1,
  203. `${base}${propertyAccess(/** @type {string[]} */ (used))}`
  204. );
  205. }
  206. };
  207. module.exports = CommonJsSelfReferenceDependency;