CommonJsExportsDependency.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const InitFragment = require("../InitFragment");
  7. const makeSerializable = require("../util/makeSerializable");
  8. const { propertyAccess } = require("../util/property");
  9. const {
  10. handleDependencyBase,
  11. isThisBase,
  12. isWorkerEntryThis
  13. } = require("./CommonJsDependencyHelpers");
  14. const NullDependency = require("./NullDependency");
  15. /** @import { ReplaceSource } from "webpack-sources" */
  16. /**
  17. * @import Dependency, {
  18. * ExportsSpec,
  19. * UpdateHashContext,
  20. * ExportInfoName
  21. * } from "../Dependency"
  22. */
  23. /** @import Hash from "../util/Hash" */
  24. /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
  25. /** @import ModuleGraph from "../ModuleGraph" */
  26. /** @import { Range } from "../javascript/JavascriptParser" */
  27. /**
  28. * @import {
  29. * CommonJSDependencyBaseKeywords
  30. * } from "./CommonJsDependencyHelpers"
  31. */
  32. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[Range, Range | null, CommonJSDependencyBaseKeywords, ExportInfoName[]]>} ObjectDeserializerContext */
  33. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[Range, Range | null, CommonJSDependencyBaseKeywords, ExportInfoName[]]>} ObjectSerializerContext */
  34. const EMPTY_OBJECT = {};
  35. class CommonJsExportsDependency extends NullDependency {
  36. /**
  37. * Creates an instance of CommonJsExportsDependency.
  38. * @param {Range} range range
  39. * @param {Range | null} valueRange value range
  40. * @param {CommonJSDependencyBaseKeywords} base base
  41. * @param {ExportInfoName[]} names names
  42. */
  43. constructor(range, valueRange, base, names) {
  44. super();
  45. this.range = range;
  46. this.valueRange = valueRange;
  47. /** @type {CommonJSDependencyBaseKeywords} */
  48. this.base = base;
  49. /** @type {string[]} */
  50. this.names = names;
  51. }
  52. get type() {
  53. return "cjs exports";
  54. }
  55. /**
  56. * Returns the exported names
  57. * @param {ModuleGraph} moduleGraph module graph
  58. * @returns {ExportsSpec | undefined} export names
  59. */
  60. getExports(moduleGraph) {
  61. const name = this.names[0];
  62. return {
  63. exports: [
  64. {
  65. name,
  66. // we can't mangle names that are in an empty object
  67. // because one could access the prototype property
  68. // when export isn't set yet
  69. canMangle: !(name in EMPTY_OBJECT)
  70. }
  71. ],
  72. dependencies: undefined
  73. };
  74. }
  75. /**
  76. * Updates the hash with the data contributed by this instance.
  77. * @param {Hash} hash hash to be updated
  78. * @param {UpdateHashContext} context context
  79. * @returns {void}
  80. */
  81. updateHash(hash, context) {
  82. // a classic worker entry generates `this` as the global scope, so it must
  83. // not share one code generation with a runtime that generates it as exports
  84. if (!isThisBase(this.base)) return;
  85. const { chunkGraph, runtime, runtimeTemplate } = context;
  86. const module = chunkGraph.moduleGraph.getParentModule(this);
  87. hash.update(
  88. module !== undefined &&
  89. isWorkerEntryThis(module, chunkGraph, runtime, runtimeTemplate)
  90. ? "worker global"
  91. : "exports"
  92. );
  93. }
  94. /**
  95. * Serializes this instance into the provided serializer context.
  96. * @param {ObjectSerializerContext} context context
  97. */
  98. serialize(context) {
  99. context
  100. .write(this.range)
  101. .write(this.valueRange)
  102. .write(this.base)
  103. .write(this.names);
  104. super.serialize(context);
  105. }
  106. /**
  107. * Restores this instance from the provided deserializer context.
  108. * @param {ObjectDeserializerContext} context context
  109. */
  110. deserialize(context) {
  111. this.range = context.read();
  112. const c1 = context.rest;
  113. this.valueRange = c1.read();
  114. const c2 = c1.rest;
  115. this.base = c2.read();
  116. const c3 = c2.rest;
  117. this.names = c3.read();
  118. super.deserialize(c3.rest);
  119. }
  120. }
  121. makeSerializable(
  122. CommonJsExportsDependency,
  123. "webpack/lib/dependencies/CommonJsExportsDependency"
  124. );
  125. CommonJsExportsDependency.Template = class CommonJsExportsDependencyTemplate extends (
  126. NullDependency.Template
  127. ) {
  128. /**
  129. * Applies the plugin by registering its hooks on the compiler.
  130. * @param {Dependency} dependency the dependency for which the template should be applied
  131. * @param {ReplaceSource} source the current replace source which can be modified
  132. * @param {DependencyTemplateContext} templateContext the context object
  133. * @returns {void}
  134. */
  135. apply(
  136. dependency,
  137. source,
  138. {
  139. module,
  140. moduleGraph,
  141. chunkGraph,
  142. initFragments,
  143. runtimeRequirements,
  144. runtime,
  145. runtimeTemplate
  146. }
  147. ) {
  148. const dep = /** @type {CommonJsExportsDependency} */ (dependency);
  149. // a classic worker entry writes to its global scope, not to an export, so
  150. // the names stay as written and are never dropped as unused
  151. const isWorkerEntry =
  152. isThisBase(dep.base) &&
  153. isWorkerEntryThis(module, chunkGraph, runtime, runtimeTemplate);
  154. // CJS exports are never inlined
  155. const used = isWorkerEntry
  156. ? dep.names
  157. : /** @type {string | string[] | false} */ (
  158. moduleGraph.getExportsInfo(module).getUsedName(dep.names, runtime)
  159. );
  160. const [type, base] = handleDependencyBase(
  161. dep.base,
  162. module,
  163. runtimeRequirements,
  164. isWorkerEntry
  165. );
  166. switch (type) {
  167. case "expression":
  168. if (!used) {
  169. initFragments.push(
  170. new InitFragment(
  171. "var __webpack_unused_export__;\n",
  172. InitFragment.STAGE_CONSTANTS,
  173. 0,
  174. "__webpack_unused_export__"
  175. )
  176. );
  177. source.replace(
  178. dep.range[0],
  179. dep.range[1] - 1,
  180. "__webpack_unused_export__"
  181. );
  182. return;
  183. }
  184. source.replace(
  185. dep.range[0],
  186. dep.range[1] - 1,
  187. `${base}${propertyAccess(/** @type {string[]} */ (used))}`
  188. );
  189. return;
  190. case "Object.defineProperty":
  191. if (!used) {
  192. initFragments.push(
  193. new InitFragment(
  194. "var __webpack_unused_export__;\n",
  195. InitFragment.STAGE_CONSTANTS,
  196. 0,
  197. "__webpack_unused_export__"
  198. )
  199. );
  200. source.replace(
  201. dep.range[0],
  202. /** @type {Range} */ (dep.valueRange)[0] - 1,
  203. "__webpack_unused_export__ = ("
  204. );
  205. source.replace(
  206. /** @type {Range} */ (dep.valueRange)[1],
  207. dep.range[1] - 1,
  208. ")"
  209. );
  210. return;
  211. }
  212. source.replace(
  213. dep.range[0],
  214. /** @type {Range} */ (dep.valueRange)[0] - 1,
  215. `Object.defineProperty(${base}${propertyAccess(
  216. /** @type {string[]} */ (used).slice(0, -1)
  217. )}, ${JSON.stringify(used[used.length - 1])}, (`
  218. );
  219. source.replace(
  220. /** @type {Range} */ (dep.valueRange)[1],
  221. dep.range[1] - 1,
  222. "))"
  223. );
  224. }
  225. }
  226. };
  227. module.exports = CommonJsExportsDependency;