| |
| /** |
| * When source maps are enabled, `style-loader` uses a link element with a data-uri to |
| * embed the css on the page. This breaks all relative urls because now they are relative to a |
| * bundle instead of the current page. |
| * |
| * One solution is to only use full urls, but that may be impossible. |
| * |
| * Instead, this function "fixes" the relative urls to be absolute according to the current page location. |
| * |
| * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command. |
| * |
| */ |
| |
| module.exports = function (css) { |
| // get current location |
| var location = typeof window !== "undefined" && window.location; |
| |
| if (!location) { |
| throw new Error("fixUrls requires window.location"); |
| } |
| |
| // blank or null? |
| if (!css || typeof css !== "string") { |
| return css; |
| } |
| |
| var baseUrl = location.protocol + "//" + location.host; |
| var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/"); |
| |
| // convert each url(...) |
| /* |
| This regular expression is just a way to recursively match brackets within |
| a string. |
| |
| /url\s*\( = Match on the word "url" with any whitespace after it and then a parens |
| ( = Start a capturing group |
| (?: = Start a non-capturing group |
| [^)(] = Match anything that isn't a parentheses |
| | = OR |
| \( = Match a start parentheses |
| (?: = Start another non-capturing groups |
| [^)(]+ = Match anything that isn't a parentheses |
| | = OR |
| \( = Match a start parentheses |
| [^)(]* = Match anything that isn't a parentheses |
| \) = Match a end parentheses |
| ) = End Group |
| *\) = Match anything and then a close parens |
| ) = Close non-capturing group |
| * = Match anything |
| ) = Close capturing group |
| \) = Match a close parens |
| |
| /gi = Get all matches, not the first. Be case insensitive. |
| */ |
| var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) { |
| // strip quotes (if they exist) |
| var unquotedOrigUrl = origUrl |
| .trim() |
| .replace(/^"(.*)"$/, function(o, $1){ return $1; }) |
| .replace(/^'(.*)'$/, function(o, $1){ return $1; }); |
| |
| // already a full url? no change |
| if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(unquotedOrigUrl)) { |
| return fullMatch; |
| } |
| |
| // convert the url to a full url |
| var newUrl; |
| |
| if (unquotedOrigUrl.indexOf("//") === 0) { |
| //TODO: should we add protocol? |
| newUrl = unquotedOrigUrl; |
| } else if (unquotedOrigUrl.indexOf("/") === 0) { |
| // path should be relative to the base url |
| newUrl = baseUrl + unquotedOrigUrl; // already starts with '/' |
| } else { |
| // path should be relative to current directory |
| newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './' |
| } |
| |
| // send back the fixed url(...) |
| return "url(" + JSON.stringify(newUrl) + ")"; |
| }); |
| |
| // send back the fixed css |
| return fixedCss; |
| }; |