fix(fixUrls): add param to fix relative urls (#186)
parent
988e58acf3
commit
19959eeb77
|
@ -1 +1,2 @@
|
||||||
/node_modules
|
/node_modules
|
||||||
|
.idea/
|
||||||
|
|
|
@ -72,6 +72,10 @@ By default, the style-loader appends `<style>` elements to the end of the `<head
|
||||||
|
|
||||||
If defined, the style-loader will re-use a single `<style>` element, instead of adding/removing individual elements for each required module. **Note:** this option is on by default in IE9, which has strict limitations on the number of style tags allowed on a page. You can enable or disable it with the singleton query parameter (`?singleton` or `?-singleton`).
|
If defined, the style-loader will re-use a single `<style>` element, instead of adding/removing individual elements for each required module. **Note:** this option is on by default in IE9, which has strict limitations on the number of style tags allowed on a page. You can enable or disable it with the singleton query parameter (`?singleton` or `?-singleton`).
|
||||||
|
|
||||||
|
#### `convertToAbsoluteUrls`
|
||||||
|
|
||||||
|
If convertToAbsoluteUrls and sourceMaps are both enabled, relative urls will be converted to absolute urls right before the css is injected into the page. This resolves [an issue](https://github.com/webpack/style-loader/pull/96) where relative resources fail to load when source maps are enabled. You can enable it with the convertToAbsoluteUrls query parameter (`?convertToAbsoluteUrls`).
|
||||||
|
|
||||||
#### `attrs`
|
#### `attrs`
|
||||||
|
|
||||||
If defined, style-loader will attach given attributes with their values on `<style>` / `<link>` element.
|
If defined, style-loader will attach given attributes with their values on `<style>` / `<link>` element.
|
||||||
|
@ -113,7 +117,7 @@ So the recommended configuration for webpack is:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note** about source maps support and assets referenced with `url`: when style loader is used with ?sourceMap option, the CSS modules will be generated as `Blob`s, so relative paths don't work (they would be relative to `chrome:blob` or `chrome:devtools`). In order for assets to maintain correct paths setting `output.publicPath` property of webpack configuration must be set, so that absolute paths are generated.
|
**Note** about source maps support and assets referenced with `url`: when style loader is used with ?sourceMap option, the CSS modules will be generated as `Blob`s, so relative paths don't work (they would be relative to `chrome:blob` or `chrome:devtools`). In order for assets to maintain correct paths setting `output.publicPath` property of webpack configuration must be set, so that absolute paths are generated. Alternatively you can enable the `convertToAbsoluteUrls` option mentioned above.
|
||||||
|
|
||||||
<h2 align="center">Contributing</h2>
|
<h2 align="center">Contributing</h2>
|
||||||
|
|
||||||
|
|
20
addStyles.js
20
addStyles.js
|
@ -18,7 +18,8 @@ var stylesInDom = {},
|
||||||
}),
|
}),
|
||||||
singletonElement = null,
|
singletonElement = null,
|
||||||
singletonCounter = 0,
|
singletonCounter = 0,
|
||||||
styleElementsInsertedAtTop = [];
|
styleElementsInsertedAtTop = [],
|
||||||
|
fixUrls = require("./fixUrls");
|
||||||
|
|
||||||
module.exports = function(list, options) {
|
module.exports = function(list, options) {
|
||||||
if(typeof DEBUG !== "undefined" && DEBUG) {
|
if(typeof DEBUG !== "undefined" && DEBUG) {
|
||||||
|
@ -59,7 +60,7 @@ module.exports = function(list, options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
function addStylesToDom(styles, options) {
|
function addStylesToDom(styles, options) {
|
||||||
for(var i = 0; i < styles.length; i++) {
|
for(var i = 0; i < styles.length; i++) {
|
||||||
|
@ -168,7 +169,7 @@ function addStyle(obj, options) {
|
||||||
typeof Blob === "function" &&
|
typeof Blob === "function" &&
|
||||||
typeof btoa === "function") {
|
typeof btoa === "function") {
|
||||||
styleElement = createLinkElement(options);
|
styleElement = createLinkElement(options);
|
||||||
update = updateLink.bind(null, styleElement);
|
update = updateLink.bind(null, styleElement, options);
|
||||||
remove = function() {
|
remove = function() {
|
||||||
removeStyleElement(styleElement);
|
removeStyleElement(styleElement);
|
||||||
if(styleElement.href)
|
if(styleElement.href)
|
||||||
|
@ -239,10 +240,21 @@ function applyToTag(styleElement, obj) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLink(linkElement, obj) {
|
function updateLink(linkElement, options, obj) {
|
||||||
var css = obj.css;
|
var css = obj.css;
|
||||||
var sourceMap = obj.sourceMap;
|
var sourceMap = obj.sourceMap;
|
||||||
|
|
||||||
|
/* If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
|
||||||
|
and there is no publicPath defined then lets turn convertToAbsoluteUrls
|
||||||
|
on by default. Otherwise default to the convertToAbsoluteUrls option
|
||||||
|
directly
|
||||||
|
*/
|
||||||
|
const autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;
|
||||||
|
|
||||||
|
if (options.convertToAbsoluteUrls || autoFixUrls){
|
||||||
|
css = fixUrls(css);
|
||||||
|
}
|
||||||
|
|
||||||
if(sourceMap) {
|
if(sourceMap) {
|
||||||
// http://stackoverflow.com/a/26603875
|
// http://stackoverflow.com/a/26603875
|
||||||
css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
|
css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(...)
|
||||||
|
var fixedCss = css.replace(/url *\( *(.+?) *\)/g, function(fullMatch, origUrl) {
|
||||||
|
// strip quotes (if they exist)
|
||||||
|
var unquotedOrigUrl = origUrl
|
||||||
|
.replace(/^"(.*)"$/, function(o, $1){ return $1; })
|
||||||
|
.replace(/^'(.*)'$/, function(o, $1){ return $1; });
|
||||||
|
|
||||||
|
// already a full url? no change
|
||||||
|
if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/)/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;
|
||||||
|
};
|
|
@ -0,0 +1,183 @@
|
||||||
|
// Node v4 requires "use strict" to allow block scoped let & const
|
||||||
|
"use strict";
|
||||||
|
var assert = require("assert");
|
||||||
|
var url = require('url');
|
||||||
|
|
||||||
|
describe("fix urls tests", function() {
|
||||||
|
var fixUrls = require("../fixUrls");
|
||||||
|
var defaultUrl = "https://x.y.z/a/b.html";
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
global.window = {
|
||||||
|
location: url.parse(defaultUrl)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
var assertUrl = function (origCss, expectedCss, specialUrl) {
|
||||||
|
if (specialUrl) {
|
||||||
|
global.window = {
|
||||||
|
location: url.parse(specialUrl)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var resultCss = fixUrls(origCss, specialUrl || defaultUrl);
|
||||||
|
expectedCss = expectedCss || origCss;
|
||||||
|
|
||||||
|
assert.equal(resultCss, expectedCss);
|
||||||
|
};
|
||||||
|
|
||||||
|
// no change
|
||||||
|
it("Null css is not modified", function() {
|
||||||
|
assertUrl(null)
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Blank css is not modified", function() { assertUrl("") });
|
||||||
|
|
||||||
|
it("No url is not modified", function () { assertUrl("body { }") });
|
||||||
|
|
||||||
|
it("Full url isn't changed (no quotes)", function() {
|
||||||
|
assertUrl("body { background-image:url(http://example.com/bg.jpg); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Full url isn't changed (no quotes, spaces)", function() {
|
||||||
|
assertUrl("body { background-image:url ( http://example.com/bg.jpg ); }");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Full url isn't changed (double quotes)", function() {
|
||||||
|
assertUrl("body { background-image:url(\"http://example.com/bg.jpg\"); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Full url isn't changed (double quotes, spaces)", function() {
|
||||||
|
assertUrl("body { background-image:url ( \"http://example.com/bg.jpg\" ); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Full url isn't changed (single quotes)", function() {
|
||||||
|
assertUrl("body { background-image:url('http://example.com/bg.jpg'); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Full url isn't changed (single quotes, spaces)", function() {
|
||||||
|
assertUrl("body { background-image:url ( 'http://example.com/bg.jpg' ); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Multiple full urls are not changed", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(http://example.com/bg.jpg); }\ndiv.main { background-image:url ( 'https://www.anothersite.com/another.png' ); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Http url isn't changed", function() {
|
||||||
|
assertUrl("body { background-image:url(http://example.com/bg.jpg); }");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Https url isn't changed", function() {
|
||||||
|
assertUrl("body { background-image:url(https://example.com/bg.jpg); }");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("HTTPS url isn't changed", function() {
|
||||||
|
assertUrl("body { background-image:url(HTTPS://example.com/bg.jpg); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("File url isn't changed", function() {
|
||||||
|
assertUrl("body { background-image:url(file:///example.com/bg.jpg); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Double slash url isn't changed", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(//example.com/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"//example.com/bg.jpg\"); }"
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Image data uri url isn't changed", function() {
|
||||||
|
assertUrl("body { background-image:url(data:image/png;base64,qsrwABYuwNkimqm3gAAAABJRU5ErkJggg==); }")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Font data uri url isn't changed", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(data:application/x-font-woff;charset=utf-8;base64,qsrwABYuwNkimqm3gAAAABJRU5ErkJggg); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// relative urls
|
||||||
|
it("Relative url", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(bg.jpg); }",
|
||||||
|
"body { background-image:url(\"https://x.y.z/a/bg.jpg\"); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Relative url with path", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(c/d/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"https://x.y.z/a/c/d/bg.jpg\"); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("Relative url with dot slash", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(./c/d/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"https://x.y.z/a/c/d/bg.jpg\"); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Multiple relative urls", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(bg.jpg); }\ndiv.main { background-image:url(./c/d/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"https://x.y.z/a/bg.jpg\"); }\ndiv.main { background-image:url(\"https://x.y.z/a/c/d/bg.jpg\"); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("Relative url that looks like data-uri", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(data/image/png.base64); }",
|
||||||
|
"body { background-image:url(\"https://x.y.z/a/data/image/png.base64\"); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// urls with hashes
|
||||||
|
it("Relative url with hash are not changed", function() {
|
||||||
|
assertUrl("body { background-image:url(#bg.jpg); }");
|
||||||
|
});
|
||||||
|
|
||||||
|
// rooted urls
|
||||||
|
it("Rooted url", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"https://x.y.z/bg.jpg\"); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("Rooted url with path", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(/a/b/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"https://x.y.z/a/b/bg.jpg\"); }"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
//special locations
|
||||||
|
it("Location with no path, filename only", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(bg.jpg); }",
|
||||||
|
"body { background-image:url(\"http://x.y.z/bg.jpg\"); }",
|
||||||
|
"http://x.y.z"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Location with no path, path with filename", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(a/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"http://x.y.z/a/bg.jpg\"); }",
|
||||||
|
"http://x.y.z"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("Location with no path, rel path with filename", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(./a/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"http://x.y.z/a/bg.jpg\"); }",
|
||||||
|
"http://x.y.z"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("Location with no path, root filename", function() {
|
||||||
|
assertUrl(
|
||||||
|
"body { background-image:url(/a/bg.jpg); }",
|
||||||
|
"body { background-image:url(\"http://x.y.z/a/bg.jpg\"); }",
|
||||||
|
"http://x.y.z"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue