CssUrlDependency.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Ivan Kopeykin @vankop
  4. */
  5. "use strict";
  6. const RawDataUrlModule = require("../asset/RawDataUrlModule");
  7. const makeSerializable = require("../util/makeSerializable");
  8. const memoize = require("../util/memoize");
  9. const ModuleDependency = require("./ModuleDependency");
  10. /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
  11. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  12. /** @typedef {import("../Dependency")} Dependency */
  13. /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
  14. /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
  15. /** @typedef {import("../Module")} Module */
  16. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  17. /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
  18. /** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
  19. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  20. /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
  21. /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
  22. /** @typedef {import("../util/Hash")} Hash */
  23. /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
  24. const getIgnoredRawDataUrlModule = memoize(() => {
  25. return new RawDataUrlModule("data:,", `ignored-asset`, `(ignored asset)`);
  26. });
  27. class CssUrlDependency extends ModuleDependency {
  28. /**
  29. * @param {string} request request
  30. * @param {Range} range range of the argument
  31. * @param {"string" | "url"} urlType dependency type e.g. url() or string
  32. */
  33. constructor(request, range, urlType) {
  34. super(request);
  35. this.range = range;
  36. this.urlType = urlType;
  37. }
  38. get type() {
  39. return "css url()";
  40. }
  41. get category() {
  42. return "url";
  43. }
  44. /**
  45. * @param {string} context context directory
  46. * @returns {Module | null} a module
  47. */
  48. createIgnoredModule(context) {
  49. return getIgnoredRawDataUrlModule();
  50. }
  51. /**
  52. * @param {ObjectSerializerContext} context context
  53. */
  54. serialize(context) {
  55. const { write } = context;
  56. write(this.urlType);
  57. super.serialize(context);
  58. }
  59. /**
  60. * @param {ObjectDeserializerContext} context context
  61. */
  62. deserialize(context) {
  63. const { read } = context;
  64. this.urlType = read();
  65. super.deserialize(context);
  66. }
  67. }
  68. /**
  69. * @param {string} str string
  70. * @returns {string} string in quotes if needed
  71. */
  72. const cssEscapeString = str => {
  73. let countWhiteOrBracket = 0;
  74. let countQuotation = 0;
  75. let countApostrophe = 0;
  76. for (let i = 0; i < str.length; i++) {
  77. const cc = str.charCodeAt(i);
  78. switch (cc) {
  79. case 9: // tab
  80. case 10: // nl
  81. case 32: // space
  82. case 40: // (
  83. case 41: // )
  84. countWhiteOrBracket++;
  85. break;
  86. case 34:
  87. countQuotation++;
  88. break;
  89. case 39:
  90. countApostrophe++;
  91. break;
  92. }
  93. }
  94. if (countWhiteOrBracket < 2) {
  95. return str.replace(/[\n\t ()'"\\]/g, m => `\\${m}`);
  96. } else if (countQuotation <= countApostrophe) {
  97. return `"${str.replace(/[\n"\\]/g, m => `\\${m}`)}"`;
  98. } else {
  99. return `'${str.replace(/[\n'\\]/g, m => `\\${m}`)}'`;
  100. }
  101. };
  102. CssUrlDependency.Template = class CssUrlDependencyTemplate extends (
  103. ModuleDependency.Template
  104. ) {
  105. /**
  106. * @param {Dependency} dependency the dependency for which the template should be applied
  107. * @param {ReplaceSource} source the current replace source which can be modified
  108. * @param {DependencyTemplateContext} templateContext the context object
  109. * @returns {void}
  110. */
  111. apply(
  112. dependency,
  113. source,
  114. { moduleGraph, runtimeTemplate, codeGenerationResults }
  115. ) {
  116. const dep = /** @type {CssUrlDependency} */ (dependency);
  117. const module = /** @type {Module} */ (moduleGraph.getModule(dep));
  118. /** @type {string | undefined} */
  119. let newValue;
  120. switch (dep.urlType) {
  121. case "string":
  122. newValue = cssEscapeString(
  123. runtimeTemplate.assetUrl({
  124. module,
  125. codeGenerationResults
  126. })
  127. );
  128. break;
  129. case "url":
  130. newValue = `url(${cssEscapeString(
  131. runtimeTemplate.assetUrl({
  132. module,
  133. codeGenerationResults
  134. })
  135. )})`;
  136. break;
  137. }
  138. source.replace(
  139. dep.range[0],
  140. dep.range[1] - 1,
  141. /** @type {string} */ (newValue)
  142. );
  143. }
  144. };
  145. makeSerializable(CssUrlDependency, "webpack/lib/dependencies/CssUrlDependency");
  146. CssUrlDependency.PUBLIC_PATH_AUTO = "__WEBPACK_CSS_PUBLIC_PATH_AUTO__";
  147. module.exports = CssUrlDependency;