WorkerDependency.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Ivan Kopeykin @vankop
  4. */
  5. "use strict";
  6. const Dependency = require("../Dependency");
  7. const RuntimeGlobals = require("../RuntimeGlobals");
  8. const makeSerializable = require("../util/makeSerializable");
  9. const ModuleDependency = require("./ModuleDependency");
  10. /** @import { ReplaceSource } from "webpack-sources" */
  11. /** @import AsyncDependenciesBlock from "../AsyncDependenciesBlock" */
  12. /** @import { ReferencedExports, UpdateHashContext } from "../Dependency" */
  13. /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
  14. /** @import Entrypoint from "../Entrypoint" */
  15. /** @import ModuleGraph from "../ModuleGraph" */
  16. /** @import { Range } from "../javascript/JavascriptParser" */
  17. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[WorkerDependencyOptions]>} ObjectDeserializerContext */
  18. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[WorkerDependencyOptions]>} ObjectSerializerContext */
  19. /** @import Hash from "../util/Hash" */
  20. /** @import { RuntimeSpec } from "../util/runtime" */
  21. /**
  22. * @typedef {object} ResourceHint
  23. * @property {true=} prefetch emit `<link rel="prefetch">`
  24. * @property {true=} preload emit `<link rel="preload">` (wins over prefetch when both are set)
  25. * @property {("high" | "low" | "auto")=} fetchPriority value for the `fetchpriority` attribute
  26. * @property {string=} as override for the `as` attribute (defaults to `"script"`)
  27. * @property {string=} type value for the `type` attribute
  28. * @property {string=} media value for the `media` attribute
  29. */
  30. /**
  31. * Represents the worker dependency runtime component.
  32. * @typedef {object} WorkerDependencyOptions
  33. * @property {string=} publicPath public path for the worker
  34. * @property {boolean=} needNewUrl true when need generate `new URL(...)`, otherwise false
  35. * @property {Range=} workerConstructorRange range of the global `Worker` constructor, set only for the `new Worker()` global syntax that needs a node-target rewrite
  36. * @property {ResourceHint=} resourceHint resource hint to inject for the worker chunk
  37. */
  38. class WorkerDependency extends ModuleDependency {
  39. /**
  40. * Creates an instance of WorkerDependency.
  41. * @param {string} request request
  42. * @param {Range} range range
  43. * @param {WorkerDependencyOptions} workerDependencyOptions options
  44. */
  45. constructor(request, range, workerDependencyOptions) {
  46. super(request);
  47. this.range = range;
  48. // If options are updated, don't forget to update the hash and serialization functions
  49. /** @type {WorkerDependencyOptions} */
  50. this.options = workerDependencyOptions;
  51. /** Cache the hash */
  52. /** @type {undefined | string} */
  53. this._hashUpdate = undefined;
  54. }
  55. /**
  56. * Returns list of exports referenced by this dependency
  57. * @param {ModuleGraph} moduleGraph module graph
  58. * @param {RuntimeSpec} runtime the runtime for which the module is analysed
  59. * @returns {ReferencedExports} referenced exports
  60. */
  61. getReferencedExports(moduleGraph, runtime) {
  62. return Dependency.NO_EXPORTS_REFERENCED;
  63. }
  64. get type() {
  65. return "new Worker()";
  66. }
  67. get category() {
  68. return "worker";
  69. }
  70. /**
  71. * Updates the hash with the data contributed by this instance.
  72. * @param {Hash} hash hash to be updated
  73. * @param {UpdateHashContext} context context
  74. * @returns {void}
  75. */
  76. updateHash(hash, context) {
  77. if (this._hashUpdate === undefined) {
  78. this._hashUpdate = JSON.stringify(this.options);
  79. }
  80. hash.update(this._hashUpdate);
  81. }
  82. /**
  83. * Serializes this instance into the provided serializer context.
  84. * @param {ObjectSerializerContext} context context
  85. */
  86. serialize(context) {
  87. context.write(this.options);
  88. super.serialize(context);
  89. }
  90. /**
  91. * Restores this instance from the provided deserializer context.
  92. * @param {ObjectDeserializerContext} context context
  93. */
  94. deserialize(context) {
  95. this.options = context.read();
  96. super.deserialize(context.rest);
  97. }
  98. }
  99. WorkerDependency.Template = class WorkerDependencyTemplate extends (
  100. ModuleDependency.Template
  101. ) {
  102. /**
  103. * Applies the plugin by registering its hooks on the compiler.
  104. * @param {Dependency} dependency the dependency for which the template should be applied
  105. * @param {ReplaceSource} source the current replace source which can be modified
  106. * @param {DependencyTemplateContext} templateContext the context object
  107. * @returns {void}
  108. */
  109. apply(dependency, source, templateContext) {
  110. const { chunkGraph, moduleGraph, runtimeRequirements, runtimeTemplate } =
  111. templateContext;
  112. const dep = /** @type {WorkerDependency} */ (dependency);
  113. const block = /** @type {AsyncDependenciesBlock} */ (
  114. moduleGraph.getParentBlock(dependency)
  115. );
  116. const entrypoint = /** @type {Entrypoint} */ (
  117. chunkGraph.getBlockChunkGroup(block)
  118. );
  119. const chunk = entrypoint.getEntrypointChunk();
  120. // For ESM module output emit the analyzable `new URL("./worker.<name>.js",
  121. // import.meta.url)` form (literal specifier, no runtime helpers) so other bundlers
  122. // and webpack itself can statically follow the worker. A resource hint doesn't
  123. // block it: the `<link>` is emitted at chunk startup instead of wrapping the href,
  124. // which would turn the specifier into a non-analyzable expression.
  125. const analyzableSpecifier = runtimeTemplate.supportsAnalyzable(
  126. "url",
  127. chunkGraph,
  128. templateContext.module
  129. )
  130. ? runtimeTemplate._getAnalyzableChunkSpecifier(
  131. dep.options.publicPath,
  132. chunk,
  133. templateContext.module,
  134. chunkGraph,
  135. runtimeRequirements
  136. )
  137. : null;
  138. if (analyzableSpecifier !== null) {
  139. // No `<link>` helper is asked for here: this form emits no call, and
  140. // `ResourceHintPlugin` — which spells the one that fires at startup —
  141. // declares what that one reads while requirements are still open.
  142. const urlArgs = `/* worker import */ ${analyzableSpecifier}, ${runtimeTemplate.outputOptions.importMetaName}.url`;
  143. source.replace(
  144. dep.range[0],
  145. dep.range[1] - 1,
  146. dep.options.needNewUrl ? `new URL(${urlArgs})` : urlArgs
  147. );
  148. } else {
  149. // We use the workerPublicPath option if provided, else we fallback to the RuntimeGlobal publicPath
  150. const workerImportBaseUrl = dep.options.publicPath
  151. ? `"${dep.options.publicPath}"`
  152. : RuntimeGlobals.publicPath;
  153. runtimeRequirements.add(RuntimeGlobals.publicPath);
  154. runtimeRequirements.add(RuntimeGlobals.baseURI);
  155. runtimeRequirements.add(RuntimeGlobals.getChunkScriptFilename);
  156. const hrefExpr = `${workerImportBaseUrl} + ${
  157. RuntimeGlobals.getChunkScriptFilename
  158. }(${JSON.stringify(chunk.id)})`;
  159. const hint = dep.options.resourceHint;
  160. const rel =
  161. hint && hint.preload
  162. ? "preload"
  163. : hint && hint.prefetch
  164. ? "prefetch"
  165. : null;
  166. let wrappedHref;
  167. if (rel && hint) {
  168. const fn =
  169. rel === "preload"
  170. ? RuntimeGlobals.preloadAsset
  171. : RuntimeGlobals.prefetchAsset;
  172. runtimeRequirements.add(fn);
  173. const as = hint.as || "script";
  174. const args = [
  175. hrefExpr,
  176. JSON.stringify(as),
  177. hint.type ? JSON.stringify(hint.type) : "undefined",
  178. hint.media ? JSON.stringify(hint.media) : "undefined",
  179. hint.fetchPriority ? JSON.stringify(hint.fetchPriority) : "undefined"
  180. ];
  181. wrappedHref = `${fn}(${args.join(", ")})`;
  182. } else {
  183. wrappedHref = hrefExpr;
  184. }
  185. const workerImportStr = `/* worker import */ ${wrappedHref}, ${RuntimeGlobals.baseURI}`;
  186. source.replace(
  187. dep.range[0],
  188. dep.range[1] - 1,
  189. dep.options.needNewUrl ? `new URL(${workerImportStr})` : workerImportStr
  190. );
  191. }
  192. // `Worker` is global only on the web, so route node (universal or node-only) through `worker_threads`
  193. const { platform } = runtimeTemplate.compilation.compiler;
  194. const ctorRange = dep.options.workerConstructorRange;
  195. if (
  196. ctorRange &&
  197. (runtimeTemplate.isUniversalTarget() || (platform.node && !platform.web))
  198. ) {
  199. // `__webpack_require__.wc` attaches to `__webpack_require__`; the analyzable
  200. // branch adds no other requirement that would emit it, so require it here.
  201. runtimeRequirements.add(RuntimeGlobals.require);
  202. runtimeRequirements.add(RuntimeGlobals.worker);
  203. source.replace(ctorRange[0], ctorRange[1] - 1, RuntimeGlobals.worker);
  204. }
  205. }
  206. };
  207. makeSerializable(WorkerDependency, "webpack/lib/dependencies/WorkerDependency");
  208. module.exports = WorkerDependency;