ConsumeSharedModule.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { RawSource } = require("webpack-sources");
  7. const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
  8. const Module = require("../Module");
  9. const {
  10. CONSUME_SHARED_TYPES,
  11. JAVASCRIPT_TYPES
  12. } = require("../ModuleSourceTypeConstants");
  13. const {
  14. WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE
  15. } = require("../ModuleTypeConstants");
  16. const RuntimeGlobals = require("../RuntimeGlobals");
  17. const makeSerializable = require("../util/makeSerializable");
  18. const { rangeToString, stringifyHoley } = require("../util/semver");
  19. const ConsumeSharedFallbackDependency = require("./ConsumeSharedFallbackDependency");
  20. /** @type {WeakMap<ModuleGraph, WeakMap<ConsumeSharedModule, Module | null>>} */
  21. const fallbackModuleCache = new WeakMap();
  22. /**
  23. * @import {
  24. * WebpackOptionsNormalizedWithDefaults as WebpackOptions
  25. * } from "../config/defaults"
  26. */
  27. /** @import Compilation from "../Compilation" */
  28. /** @import { UpdateHashContext } from "../Dependency" */
  29. /**
  30. * @import {
  31. * BuildCallback,
  32. * CodeGenerationContext,
  33. * CodeGenerationResult,
  34. * LibIdentOptions,
  35. * LibIdent,
  36. * NeedBuildCallback,
  37. * NeedBuildContext,
  38. * Sources,
  39. * SourceTypes,
  40. * ExportsType,
  41. * BasicSourceTypes
  42. * } from "../Module"
  43. */
  44. /** @import ModuleGraph from "../ModuleGraph" */
  45. /** @import RequestShortener from "../RequestShortener" */
  46. /** @import { ResolverWithOptions } from "../ResolverFactory" */
  47. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[ConsumeOptions]>} ObjectDeserializerContext */
  48. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[ConsumeOptions]>} ObjectSerializerContext */
  49. /** @import Hash from "../util/Hash" */
  50. /** @import { InputFileSystem } from "../util/fs" */
  51. /** @import { SemVerRange } from "../util/semver" */
  52. /**
  53. * Represents the consume shared module runtime component.
  54. * @typedef {object} ConsumeOptions
  55. * @property {string=} import fallback request
  56. * @property {string=} importResolved resolved fallback request
  57. * @property {string} shareKey global share key
  58. * @property {string} shareScope share scope
  59. * @property {SemVerRange | false | undefined} requiredVersion version requirement
  60. * @property {string=} packageName package name to determine required version automatically
  61. * @property {boolean} strictVersion don't use shared version even if version isn't valid
  62. * @property {boolean} singleton use single global version
  63. * @property {boolean} eager include the fallback module in a sync way
  64. */
  65. class ConsumeSharedModule extends Module {
  66. /**
  67. * Creates an instance of ConsumeSharedModule.
  68. * @param {string} context context
  69. * @param {ConsumeOptions} options consume options
  70. */
  71. constructor(context, options) {
  72. super(WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE, context);
  73. /** @type {ConsumeOptions} */
  74. this.options = options;
  75. }
  76. /**
  77. * Returns the unique identifier used to reference this module.
  78. * @returns {string} a unique identifier of the module
  79. */
  80. identifier() {
  81. const {
  82. shareKey,
  83. shareScope,
  84. importResolved,
  85. requiredVersion,
  86. strictVersion,
  87. singleton,
  88. eager
  89. } = this.options;
  90. return `${WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE}|${shareScope}|${shareKey}|${
  91. requiredVersion && rangeToString(requiredVersion)
  92. }|${strictVersion}|${importResolved}|${singleton}|${eager}`;
  93. }
  94. /**
  95. * Returns a human-readable identifier for this module.
  96. * @param {RequestShortener} requestShortener the request shortener
  97. * @returns {string} a user readable identifier of the module
  98. */
  99. readableIdentifier(requestShortener) {
  100. const {
  101. shareKey,
  102. shareScope,
  103. importResolved,
  104. requiredVersion,
  105. strictVersion,
  106. singleton,
  107. eager
  108. } = this.options;
  109. return `consume shared module (${shareScope}) ${shareKey}@${
  110. requiredVersion ? rangeToString(requiredVersion) : "*"
  111. }${strictVersion ? " (strict)" : ""}${singleton ? " (singleton)" : ""}${
  112. importResolved
  113. ? ` (fallback: ${requestShortener.shorten(importResolved)})`
  114. : ""
  115. }${eager ? " (eager)" : ""}`;
  116. }
  117. /**
  118. * Gets the library identifier.
  119. * @param {LibIdentOptions} options options
  120. * @returns {LibIdent | null} an identifier for library inclusion
  121. */
  122. libIdent(options) {
  123. const { shareKey, shareScope, import: request } = this.options;
  124. return `${
  125. this.layer ? `(${this.layer})/` : ""
  126. }webpack/sharing/consume/${shareScope}/${shareKey}${
  127. request ? `/${request}` : ""
  128. }`;
  129. }
  130. /**
  131. * Checks whether the module needs to be rebuilt for the current build state.
  132. * @param {NeedBuildContext} context context info
  133. * @param {NeedBuildCallback} callback callback function, returns true, if the module needs a rebuild
  134. * @returns {void}
  135. */
  136. needBuild(context, callback) {
  137. callback(null, !this.buildInfo);
  138. }
  139. /**
  140. * Builds the module using the provided compilation context.
  141. * @param {WebpackOptions} options webpack options
  142. * @param {Compilation} compilation the compilation
  143. * @param {ResolverWithOptions} resolver the resolver
  144. * @param {InputFileSystem} fs the file system
  145. * @param {BuildCallback} callback callback function
  146. * @returns {void}
  147. */
  148. build(options, compilation, resolver, fs, callback) {
  149. this.buildMeta = {};
  150. this.buildInfo = {};
  151. if (this.options.import) {
  152. const dep = new ConsumeSharedFallbackDependency(this.options.import);
  153. if (this.options.eager) {
  154. this.addDependency(dep);
  155. } else {
  156. const block = new AsyncDependenciesBlock({});
  157. block.addDependency(dep);
  158. this.addBlock(block);
  159. }
  160. }
  161. callback();
  162. }
  163. /**
  164. * Returns the source types this module can generate.
  165. * @returns {SourceTypes} types available (do not mutate)
  166. */
  167. getSourceTypes() {
  168. return CONSUME_SHARED_TYPES;
  169. }
  170. /**
  171. * Basic source types are high-level categories like javascript, css, webassembly, etc.
  172. * We only have built-in knowledge about the javascript basic type here; other basic types may be
  173. * added or changed over time by generators and do not need to be handled or detected here.
  174. *
  175. * Some modules, e.g. RemoteModule, may return non-basic source types like "remote" and "share-init"
  176. * from getSourceTypes(), but their generated output is still JavaScript, i.e. their basic type is JS.
  177. * @returns {BasicSourceTypes} types available (do not mutate)
  178. */
  179. getSourceBasicTypes() {
  180. return JAVASCRIPT_TYPES;
  181. }
  182. /**
  183. * Get fallback module.
  184. * @param {ModuleGraph} moduleGraph the module graph
  185. * @returns {Module | null} fallback module
  186. */
  187. _getFallbackModule(moduleGraph) {
  188. let moduleCache = fallbackModuleCache.get(moduleGraph);
  189. if (!moduleCache) {
  190. moduleCache = new WeakMap();
  191. fallbackModuleCache.set(moduleGraph, moduleCache);
  192. }
  193. const cached = moduleCache.get(this);
  194. if (cached !== undefined) {
  195. return cached;
  196. }
  197. /** @type {undefined | null | Module} */
  198. let fallbackModule = null;
  199. if (this.options.import) {
  200. if (this.options.eager) {
  201. const dep = this.dependencies[0];
  202. if (dep) {
  203. fallbackModule = moduleGraph.getModule(dep);
  204. }
  205. } else {
  206. const block = this.blocks[0];
  207. if (block && block.dependencies.length > 0) {
  208. fallbackModule = moduleGraph.getModule(block.dependencies[0]);
  209. }
  210. }
  211. }
  212. moduleCache.set(this, fallbackModule);
  213. return fallbackModule;
  214. }
  215. /**
  216. * Returns export type.
  217. * @param {ModuleGraph} moduleGraph the module graph
  218. * @param {boolean | undefined} strict the importing module is strict
  219. * @returns {ExportsType} export type
  220. * "namespace": Exports is already a namespace object. namespace = exports.
  221. * "dynamic": Check at runtime if __esModule is set. When set: namespace = { ...exports, default: exports }. When not set: namespace = { default: exports }.
  222. * "default-only": Provide a namespace object with only default export. namespace = { default: exports }
  223. * "default-with-named": Provide a namespace object with named and default export. namespace = { ...exports, default: exports }
  224. */
  225. getExportsType(moduleGraph, strict) {
  226. const fallbackModule = this._getFallbackModule(moduleGraph);
  227. if (!fallbackModule) return "dynamic";
  228. return fallbackModule.getExportsType(moduleGraph, strict);
  229. }
  230. /**
  231. * Returns the estimated size for the requested source type.
  232. * @param {string=} type the source type for which the size should be estimated
  233. * @returns {number} the estimated size of the module (must be non-zero)
  234. */
  235. size(type) {
  236. return 42;
  237. }
  238. /**
  239. * Updates the hash with the data contributed by this instance.
  240. * @param {Hash} hash the hash used to track dependencies
  241. * @param {UpdateHashContext} context context
  242. * @returns {void}
  243. */
  244. updateHash(hash, context) {
  245. hash.update(JSON.stringify(this.options));
  246. super.updateHash(hash, context);
  247. }
  248. /**
  249. * Generates code and runtime requirements for this module.
  250. * @param {CodeGenerationContext} context context for code generation
  251. * @returns {CodeGenerationResult} result
  252. */
  253. codeGeneration({ chunkGraph, runtimeTemplate }) {
  254. const runtimeRequirements = new Set([RuntimeGlobals.shareScopeMap]);
  255. const {
  256. shareScope,
  257. shareKey,
  258. strictVersion,
  259. requiredVersion,
  260. import: request,
  261. singleton,
  262. eager
  263. } = this.options;
  264. /** @type {undefined | string} */
  265. let fallbackCode;
  266. if (request) {
  267. if (eager) {
  268. const dep = this.dependencies[0];
  269. fallbackCode = runtimeTemplate.syncModuleFactory({
  270. dependency: dep,
  271. chunkGraph,
  272. runtimeRequirements,
  273. request: this.options.import
  274. });
  275. } else {
  276. const block = this.blocks[0];
  277. fallbackCode = runtimeTemplate.asyncModuleFactory({
  278. block,
  279. chunkGraph,
  280. runtimeRequirements,
  281. request: this.options.import,
  282. originModule: this
  283. });
  284. }
  285. }
  286. const args = [
  287. JSON.stringify(shareScope),
  288. JSON.stringify(shareKey),
  289. JSON.stringify(eager)
  290. ];
  291. if (requiredVersion) {
  292. args.push(stringifyHoley(requiredVersion));
  293. }
  294. if (fallbackCode) {
  295. args.push(fallbackCode);
  296. }
  297. /** @type {string} */
  298. let fn;
  299. if (requiredVersion) {
  300. if (strictVersion) {
  301. fn = singleton ? "loadStrictSingletonVersion" : "loadStrictVersion";
  302. } else {
  303. fn = singleton ? "loadSingletonVersion" : "loadVersion";
  304. }
  305. } else {
  306. fn = singleton ? "loadSingleton" : "load";
  307. }
  308. const code = runtimeTemplate.returningFunction(`${fn}(${args.join(", ")})`);
  309. /** @type {Sources} */
  310. const sources = new Map();
  311. sources.set("consume-shared", new RawSource(code));
  312. return {
  313. runtimeRequirements,
  314. sources
  315. };
  316. }
  317. /**
  318. * Serializes this instance into the provided serializer context.
  319. * @param {ObjectSerializerContext} context context
  320. */
  321. serialize(context) {
  322. context.write(this.options);
  323. super.serialize(context);
  324. }
  325. /**
  326. * Restores this instance from the provided deserializer context.
  327. * @param {ObjectDeserializerContext} context context
  328. */
  329. deserialize(context) {
  330. this.options = context.read();
  331. super.deserialize(context.rest);
  332. }
  333. }
  334. makeSerializable(
  335. ConsumeSharedModule,
  336. "webpack/lib/sharing/ConsumeSharedModule"
  337. );
  338. module.exports = ConsumeSharedModule;