HtmlInlineScriptDependency.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. */
  4. "use strict";
  5. const makeSerializable = require("../util/makeSerializable");
  6. const memoize = require("../util/memoize");
  7. const ModuleDependency = require("./ModuleDependency");
  8. // only the template needs it, and that runs only for a real html module
  9. const getHtmlGenerator = memoize(() => require("../html/HtmlGenerator"));
  10. /** @import { ReplaceSource } from "webpack-sources" */
  11. /** @import Chunk from "../Chunk" */
  12. /** @import Dependency from "../Dependency" */
  13. /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
  14. /** @import Entrypoint from "../Entrypoint" */
  15. /** @import { Range } from "../javascript/JavascriptParser" */
  16. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[number, Range, string, string]>} ObjectDeserializerContext */
  17. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[number, Range, string, string]>} ObjectSerializerContext */
  18. /**
  19. * Represents an inline `<script>...</script>` block in an HTML module. The
  20. * tag's body is bundled as its own entry chunk — the same pipeline that
  21. * processes `<script src>` — and the inline body is replaced with a
  22. * `src` attribute pointing at the emitted chunk URL.
  23. */
  24. class HtmlInlineScriptDependency extends ModuleDependency {
  25. /**
  26. * Creates an instance of HtmlInlineScriptDependency.
  27. * @param {string} request virtual request resolving to the inline JS (data URI)
  28. * @param {number} insertPos position right after `<script` where ` src="…"` is inserted
  29. * @param {Range} contentRange range of the inline JS body (between `<script>` and `</script>`)
  30. * @param {string} entryName name of the entry the inline JS is bundled into
  31. * @param {string=} category dependency category used for resolving and grouping
  32. */
  33. constructor(request, insertPos, contentRange, entryName, category) {
  34. super(request);
  35. /** @type {number} */
  36. this.insertPos = insertPos;
  37. this.contentRange = contentRange;
  38. this.range = contentRange;
  39. /** @type {string} */
  40. this.entryName = entryName;
  41. /** @type {string} */
  42. this._category = category || "commonjs";
  43. }
  44. get type() {
  45. return "html inline script";
  46. }
  47. get category() {
  48. return this._category;
  49. }
  50. /**
  51. * Serializes this instance into the provided serializer context.
  52. * @param {ObjectSerializerContext} context context
  53. */
  54. serialize(context) {
  55. context
  56. .write(this.insertPos)
  57. .write(this.contentRange)
  58. .write(this.entryName)
  59. .write(this._category);
  60. super.serialize(context);
  61. }
  62. /**
  63. * Restores this instance from the provided deserializer context.
  64. * @param {ObjectDeserializerContext} context context
  65. */
  66. deserialize(context) {
  67. this.insertPos = context.read();
  68. const c1 = context.rest;
  69. this.contentRange = c1.read();
  70. this.range = this.contentRange;
  71. const c2 = c1.rest;
  72. this.entryName = c2.read();
  73. const c3 = c2.rest;
  74. this._category = c3.read();
  75. super.deserialize(c3.rest);
  76. }
  77. }
  78. HtmlInlineScriptDependency.Template = class HtmlInlineScriptDependencyTemplate extends (
  79. ModuleDependency.Template
  80. ) {
  81. /**
  82. * Applies the plugin by registering its hooks on the compiler.
  83. * @param {Dependency} dependency the dependency for which the template should be applied
  84. * @param {ReplaceSource} source the current replace source which can be modified
  85. * @param {DependencyTemplateContext} templateContext the context object
  86. * @returns {void}
  87. */
  88. apply(dependency, source, templateContext) {
  89. const { runtimeTemplate } = templateContext;
  90. const dep = /** @type {HtmlInlineScriptDependency} */ (dependency);
  91. const compilation = runtimeTemplate.compilation;
  92. const entrypoint = /** @type {Entrypoint | undefined} */ (
  93. compilation.entrypoints.get(dep.entryName)
  94. );
  95. /** @type {string} */
  96. let url = "data:,";
  97. if (entrypoint) {
  98. const chunk = /** @type {Chunk} */ (entrypoint.getEntrypointChunk());
  99. // Defer chunk-URL substitution to renderManifest — chunk hashes aren't ready yet.
  100. url = getHtmlGenerator().makeChunkUrlSentinel(chunk, "javascript");
  101. }
  102. // Insert ` src="…"` right after `<script` so the inline body is
  103. // served from the emitted chunk instead. The browser ignores the
  104. // remaining inline body once `src` is present, but we still clear
  105. // it below so the unprocessed JS doesn't ride along.
  106. source.insert(dep.insertPos, ` src="${url}"`);
  107. source.replace(dep.contentRange[0], dep.contentRange[1] - 1, "");
  108. }
  109. };
  110. makeSerializable(
  111. HtmlInlineScriptDependency,
  112. "webpack/lib/dependencies/HtmlInlineScriptDependency"
  113. );
  114. module.exports = HtmlInlineScriptDependency;