You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
2.9 KiB

4 years ago
  1. /**
  2. * When source maps are enabled, `style-loader` uses a link element with a data-uri to
  3. * embed the css on the page. This breaks all relative urls because now they are relative to a
  4. * bundle instead of the current page.
  5. *
  6. * One solution is to only use full urls, but that may be impossible.
  7. *
  8. * Instead, this function "fixes" the relative urls to be absolute according to the current page location.
  9. *
  10. * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.
  11. *
  12. */
  13. module.exports = function (css) {
  14. // get current location
  15. var location = typeof window !== "undefined" && window.location;
  16. if (!location) {
  17. throw new Error("fixUrls requires window.location");
  18. }
  19. // blank or null?
  20. if (!css || typeof css !== "string") {
  21. return css;
  22. }
  23. var baseUrl = location.protocol + "//" + location.host;
  24. var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/");
  25. // convert each url(...)
  26. /*
  27. This regular expression is just a way to recursively match brackets within
  28. a string.
  29. /url\s*\( = Match on the word "url" with any whitespace after it and then a parens
  30. ( = Start a capturing group
  31. (?: = Start a non-capturing group
  32. [^)(] = Match anything that isn't a parentheses
  33. | = OR
  34. \( = Match a start parentheses
  35. (?: = Start another non-capturing groups
  36. [^)(]+ = Match anything that isn't a parentheses
  37. | = OR
  38. \( = Match a start parentheses
  39. [^)(]* = Match anything that isn't a parentheses
  40. \) = Match a end parentheses
  41. ) = End Group
  42. *\) = Match anything and then a close parens
  43. ) = Close non-capturing group
  44. * = Match anything
  45. ) = Close capturing group
  46. \) = Match a close parens
  47. /gi = Get all matches, not the first. Be case insensitive.
  48. */
  49. var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) {
  50. // strip quotes (if they exist)
  51. var unquotedOrigUrl = origUrl
  52. .trim()
  53. .replace(/^"(.*)"$/, function(o, $1){ return $1; })
  54. .replace(/^'(.*)'$/, function(o, $1){ return $1; });
  55. // already a full url? no change
  56. if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(unquotedOrigUrl)) {
  57. return fullMatch;
  58. }
  59. // convert the url to a full url
  60. var newUrl;
  61. if (unquotedOrigUrl.indexOf("//") === 0) {
  62. //TODO: should we add protocol?
  63. newUrl = unquotedOrigUrl;
  64. } else if (unquotedOrigUrl.indexOf("/") === 0) {
  65. // path should be relative to the base url
  66. newUrl = baseUrl + unquotedOrigUrl; // already starts with '/'
  67. } else {
  68. // path should be relative to current directory
  69. newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './'
  70. }
  71. // send back the fixed url(...)
  72. return "url(" + JSON.stringify(newUrl) + ")";
  73. });
  74. // send back the fixed css
  75. return fixedCss;
  76. };