CommonJsObjectExportDependency.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Natsu @xiaoxiaojx
  4. */
  5. "use strict";
  6. const makeSerializable = require("../util/makeSerializable");
  7. const { propertyName } = require("../util/property");
  8. const NullDependency = require("./NullDependency");
  9. /** @import { ReplaceSource } from "webpack-sources" */
  10. /** @import Dependency, { ExportsSpec, ExportInfoName } from "../Dependency" */
  11. /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
  12. /** @import ModuleGraph from "../ModuleGraph" */
  13. /** @import { Range } from "../javascript/JavascriptParser" */
  14. /** @typedef {"keyValue" | "shorthand" | "get" | "set" | "method" | "asyncMethod" | "generatorMethod" | "asyncGeneratorMethod"} CommonJsObjectExportKind */
  15. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[Range, Range, Range, ExportInfoName, CommonJsObjectExportKind, boolean]>} ObjectDeserializerContext */
  16. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[Range, Range, Range, ExportInfoName, CommonJsObjectExportKind, boolean]>} ObjectSerializerContext */
  17. const EMPTY_OBJECT = {};
  18. /** @type {ReadonlyMap<CommonJsObjectExportKind, string>} */
  19. const FUNCTION_PROPERTY_DROP_HEAD = new Map([
  20. ["get", "...void (function "],
  21. ["set", "...void (function "],
  22. ["method", "...void (function "],
  23. ["asyncMethod", "...void (async function "],
  24. ["generatorMethod", "...void (function* "],
  25. ["asyncGeneratorMethod", "...void (async function* "]
  26. ]);
  27. class CommonJsObjectExportDependency extends NullDependency {
  28. /**
  29. * @param {Range} range property range
  30. * @param {Range} keyRange key range
  31. * @param {Range} valueRange value / function range
  32. * @param {ExportInfoName} name export name
  33. * @param {CommonJsObjectExportKind} kind property kind
  34. * @param {boolean} pure value is known to be side-effect free
  35. */
  36. constructor(range, keyRange, valueRange, name, kind, pure) {
  37. super();
  38. this.range = range;
  39. this.keyRange = keyRange;
  40. this.valueRange = valueRange;
  41. /** @type {ExportInfoName} */
  42. this.name = name;
  43. /** @type {CommonJsObjectExportKind} */
  44. this.kind = kind;
  45. /** @type {boolean} */
  46. this.pure = pure;
  47. }
  48. get type() {
  49. return "cjs object exports";
  50. }
  51. /**
  52. * Returns the exported names
  53. * @param {ModuleGraph} moduleGraph module graph
  54. * @returns {ExportsSpec | undefined} export names
  55. */
  56. getExports(moduleGraph) {
  57. const name = this.name;
  58. return {
  59. exports: [
  60. {
  61. name,
  62. // Keep __esModule stable: the template never renames it.
  63. canMangle: name !== "__esModule" && !(name in EMPTY_OBJECT)
  64. }
  65. ],
  66. dependencies: undefined
  67. };
  68. }
  69. /**
  70. * Serializes this instance into the provided serializer context.
  71. * @param {ObjectSerializerContext} context context
  72. */
  73. serialize(context) {
  74. context
  75. .write(this.range)
  76. .write(this.keyRange)
  77. .write(this.valueRange)
  78. .write(this.name)
  79. .write(this.kind)
  80. .write(this.pure);
  81. super.serialize(context);
  82. }
  83. /**
  84. * Restores this instance from the provided deserializer context.
  85. * @param {ObjectDeserializerContext} context context
  86. */
  87. deserialize(context) {
  88. this.range = context.read();
  89. const c1 = context.rest;
  90. this.keyRange = c1.read();
  91. const c2 = c1.rest;
  92. this.valueRange = c2.read();
  93. const c3 = c2.rest;
  94. this.name = c3.read();
  95. const c4 = c3.rest;
  96. this.kind = c4.read();
  97. const c5 = c4.rest;
  98. this.pure = c5.read();
  99. super.deserialize(c5.rest);
  100. }
  101. }
  102. makeSerializable(
  103. CommonJsObjectExportDependency,
  104. "webpack/lib/dependencies/CommonJsObjectExportDependency"
  105. );
  106. CommonJsObjectExportDependency.Template = class CommonJsObjectExportDependencyTemplate extends (
  107. NullDependency.Template
  108. ) {
  109. /**
  110. * Applies the plugin by registering its hooks on the compiler.
  111. * @param {Dependency} dependency the dependency for which the template should be applied
  112. * @param {ReplaceSource} source the current replace source which can be modified
  113. * @param {DependencyTemplateContext} templateContext the context object
  114. * @returns {void}
  115. */
  116. apply(dependency, source, { module, moduleGraph, runtime, runtimeTemplate }) {
  117. const dep = /** @type {CommonJsObjectExportDependency} */ (dependency);
  118. // Interop marker must stay even when unused.
  119. if (dep.name === "__esModule") return;
  120. const used = /** @type {string | false} */ (
  121. moduleGraph.getExportsInfo(module).getUsedName(dep.name, runtime)
  122. );
  123. if (!used) {
  124. // Object spread rewrite needs environment.spread.
  125. if (!runtimeTemplate.supportsSpread()) return;
  126. const range = /** @type {Range} */ (dep.range);
  127. if (FUNCTION_PROPERTY_DROP_HEAD.has(dep.kind)) {
  128. // Keep the function body so nested dependency replaces stay valid.
  129. const valueRange = /** @type {Range} */ (dep.valueRange);
  130. source.replace(
  131. range[0],
  132. valueRange[0] - 1,
  133. /** @type {string} */ (FUNCTION_PROPERTY_DROP_HEAD.get(dep.kind))
  134. );
  135. source.insert(valueRange[1], ")");
  136. return;
  137. }
  138. if (dep.kind === "shorthand") {
  139. source.replace(range[0], range[1] - 1, `...void (${dep.name})`);
  140. return;
  141. }
  142. // keyValue: keep the value's side effects in place.
  143. const valueRange = /** @type {Range} */ (dep.valueRange);
  144. if (dep.pure) {
  145. // spreading null adds nothing; the text stays so nested replaces hold
  146. source.replace(
  147. range[0],
  148. valueRange[0] - 1,
  149. "...(/* unused pure expression */ null && ("
  150. );
  151. source.replace(valueRange[1], range[1] - 1, "))");
  152. return;
  153. }
  154. source.replace(range[0], valueRange[0] - 1, "...void (");
  155. source.replace(valueRange[1], range[1] - 1, ")");
  156. return;
  157. }
  158. if (used === dep.name) return;
  159. // Rename the key to the mangled export name.
  160. const keyRange = /** @type {Range} */ (dep.keyRange);
  161. if (dep.kind === "shorthand") {
  162. source.replace(
  163. keyRange[0],
  164. keyRange[1] - 1,
  165. `${propertyName(used)}: ${dep.name}`
  166. );
  167. return;
  168. }
  169. source.replace(keyRange[0], keyRange[1] - 1, propertyName(used));
  170. }
  171. };
  172. CommonJsObjectExportDependency.FUNCTION_PROPERTY_DROP_HEAD =
  173. FUNCTION_PROPERTY_DROP_HEAD;
  174. module.exports = CommonJsObjectExportDependency;