Merge pull request #24 from diurnalist/feature/singleton-tag
Adds `singleton` option to allow re-using a single <style> elementmaster
commit
0cc58a7b6c
|
@ -32,6 +32,12 @@ Styles are not added on require, but instead on call to `use`/`ref`. Styles are
|
|||
|
||||
Note: Behavior is undefined when `unuse`/`unref` is called more often than `use`/`ref`. Don't do that.
|
||||
|
||||
### Options
|
||||
|
||||
#### `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 # of style tags allowed on a page.
|
||||
|
||||
## Recommended configuration
|
||||
|
||||
By convention the reference-counted API should be bound to `.useable.css` and the simple API to `.css` (similar to other file types, i. e. `.useable.less` and `.less`).
|
||||
|
|
107
addStyles.js
107
addStyles.js
|
@ -2,14 +2,36 @@
|
|||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var stylesInDom = {};
|
||||
var stylesInDom = {},
|
||||
memoize = function(fn) {
|
||||
var memo;
|
||||
return function () {
|
||||
if (typeof memo === "undefined") memo = fn.apply(this, arguments);
|
||||
return memo;
|
||||
};
|
||||
},
|
||||
isIE9 = memoize(function() {
|
||||
return /msie 9\b/.test(window.navigator.userAgent.toLowerCase());
|
||||
}),
|
||||
getHeadElement = memoize(function () {
|
||||
return document.head || document.getElementsByTagName("head")[0];
|
||||
}),
|
||||
singletonElement = null,
|
||||
singletonCounter = 0;
|
||||
|
||||
module.exports = function(list) {
|
||||
module.exports = function(list, options) {
|
||||
if(typeof DEBUG !== "undefined" && DEBUG) {
|
||||
if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
// Force single-tag solution on IE9, which has a hard limit on the # of <style>
|
||||
// tags it will allow on a page
|
||||
if (typeof options.singleton === "undefined") options.singleton = isIE9();
|
||||
|
||||
var styles = listToStyles(list);
|
||||
addStylesToDom(styles);
|
||||
addStylesToDom(styles, options);
|
||||
|
||||
return function update(newList) {
|
||||
var mayRemove = [];
|
||||
for(var i = 0; i < styles.length; i++) {
|
||||
|
@ -20,7 +42,7 @@ module.exports = function(list) {
|
|||
}
|
||||
if(newList) {
|
||||
var newStyles = listToStyles(newList);
|
||||
addStylesToDom(newStyles);
|
||||
addStylesToDom(newStyles, options);
|
||||
}
|
||||
for(var i = 0; i < mayRemove.length; i++) {
|
||||
var domStyle = mayRemove[i];
|
||||
|
@ -33,7 +55,7 @@ module.exports = function(list) {
|
|||
};
|
||||
}
|
||||
|
||||
function addStylesToDom(styles) {
|
||||
function addStylesToDom(styles, options) {
|
||||
for(var i = 0; i < styles.length; i++) {
|
||||
var item = styles[i];
|
||||
var domStyle = stylesInDom[item.id];
|
||||
|
@ -43,12 +65,12 @@ function addStylesToDom(styles) {
|
|||
domStyle.parts[j](item.parts[j]);
|
||||
}
|
||||
for(; j < item.parts.length; j++) {
|
||||
domStyle.parts.push(addStyle(item.parts[j]));
|
||||
domStyle.parts.push(addStyle(item.parts[j], options));
|
||||
}
|
||||
} else {
|
||||
var parts = [];
|
||||
for(var j = 0; j < item.parts.length; j++) {
|
||||
parts.push(addStyle(item.parts[j]));
|
||||
parts.push(addStyle(item.parts[j], options));
|
||||
}
|
||||
stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
|
||||
}
|
||||
|
@ -73,38 +95,90 @@ function listToStyles(list) {
|
|||
return styles;
|
||||
}
|
||||
|
||||
function addStyle(obj) {
|
||||
function createStyleElement() {
|
||||
var styleElement = document.createElement("style");
|
||||
var head = document.head || document.getElementsByTagName("head")[0];
|
||||
var head = getHeadElement();
|
||||
styleElement.type = "text/css";
|
||||
head.appendChild(styleElement);
|
||||
applyToTag(styleElement, obj);
|
||||
return function(newObj) {
|
||||
return styleElement;
|
||||
}
|
||||
|
||||
function addStyle(obj, options) {
|
||||
var styleElement, update, remove;
|
||||
|
||||
if (options.singleton) {
|
||||
var styleIndex = singletonCounter++;
|
||||
styleElement = singletonElement || (singletonElement = createStyleElement());
|
||||
update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
|
||||
remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
|
||||
} else {
|
||||
styleElement = createStyleElement();
|
||||
update = applyToTag.bind(null, styleElement);
|
||||
remove = function () {
|
||||
styleElement.parentNode.removeChild(styleElement);
|
||||
};
|
||||
}
|
||||
|
||||
update(obj);
|
||||
|
||||
return function updateStyle(newObj) {
|
||||
if(newObj) {
|
||||
if(newObj.css === obj.css && newObj.media === obj.media /*&& newObj.sourceMap === obj.sourceMap*/)
|
||||
return;
|
||||
applyToTag(styleElement, obj = newObj);
|
||||
update(obj = newObj);
|
||||
} else {
|
||||
head.removeChild(styleElement);
|
||||
remove();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function replaceText(source, id, replacement) {
|
||||
var boundaries = ["/** >>" + id + " **/", "/** " + id + "<< **/"];
|
||||
var start = source.lastIndexOf(boundaries[0]);
|
||||
var wrappedReplacement = replacement
|
||||
? (boundaries[0] + replacement + boundaries[1])
|
||||
: "";
|
||||
if (source.lastIndexOf(boundaries[0]) >= 0) {
|
||||
var end = source.lastIndexOf(boundaries[1]) + boundaries[1].length;
|
||||
return source.slice(0, start) + wrappedReplacement + source.slice(end);
|
||||
} else {
|
||||
return source + wrappedReplacement;
|
||||
}
|
||||
}
|
||||
|
||||
function applyToSingletonTag(styleElement, index, remove, obj) {
|
||||
var css = remove ? "" : obj.css;
|
||||
|
||||
if(styleElement.styleSheet) {
|
||||
styleElement.styleSheet.cssText = replaceText(styleElement.styleSheet.cssText, index, css);
|
||||
} else {
|
||||
var cssNode = document.createTextNode(css);
|
||||
var childNodes = styleElement.childNodes;
|
||||
if (childNodes[index]) styleElement.removeChild(childNodes[index]);
|
||||
if (childNodes.length) {
|
||||
styleElement.insertBefore(cssNode, childNodes[index]);
|
||||
} else {
|
||||
styleElement.appendChild(cssNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyToTag(styleElement, obj) {
|
||||
var css = obj.css;
|
||||
var media = obj.media;
|
||||
// var sourceMap = obj.sourceMap;
|
||||
|
||||
// No browser support
|
||||
// if(sourceMap && typeof btoa === "function") {
|
||||
// try {
|
||||
// css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(JSON.stringify(sourceMap)) + " */";
|
||||
// } catch(e) {}
|
||||
// }
|
||||
|
||||
if(media) {
|
||||
styleElement.setAttribute("media", media)
|
||||
}
|
||||
if (styleElement.styleSheet) {
|
||||
|
||||
if(styleElement.styleSheet) {
|
||||
styleElement.styleSheet.cssText = css;
|
||||
} else {
|
||||
while(styleElement.firstChild) {
|
||||
|
@ -112,5 +186,4 @@ function applyToTag(styleElement, obj) {
|
|||
}
|
||||
styleElement.appendChild(document.createTextNode(css));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
6
index.js
6
index.js
|
@ -2,10 +2,12 @@
|
|||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
var path = require("path");
|
||||
var loaderUtils = require("loader-utils"),
|
||||
path = require("path");
|
||||
module.exports = function() {};
|
||||
module.exports.pitch = function(remainingRequest) {
|
||||
this.cacheable && this.cacheable();
|
||||
var query = loaderUtils.parseQuery(this.query);
|
||||
return [
|
||||
"// style-loader: Adds some css to the DOM by adding a <style> tag",
|
||||
"",
|
||||
|
@ -13,7 +15,7 @@ module.exports.pitch = function(remainingRequest) {
|
|||
"var content = require(" + JSON.stringify("!!" + remainingRequest) + ");",
|
||||
"if(typeof content === 'string') content = [[module.id, content, '']];",
|
||||
"// add the styles to the DOM",
|
||||
"var update = require(" + JSON.stringify("!" + path.join(__dirname, "addStyles.js")) + ")(content);",
|
||||
"var update = require(" + JSON.stringify("!" + path.join(__dirname, "addStyles.js")) + ")(content, " + JSON.stringify(query) + ");",
|
||||
"// Hot Module Replacement",
|
||||
"if(module.hot) {",
|
||||
" // When the styles change, update the <style> tags",
|
||||
|
|
41
package.json
41
package.json
|
@ -1,19 +1,22 @@
|
|||
{
|
||||
"name": "style-loader",
|
||||
"version": "0.8.1",
|
||||
"author": "Tobias Koppers @sokra",
|
||||
"description": "style loader module for webpack",
|
||||
"devDependencies": {
|
||||
"css-loader": "~0.8.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:webpack/style-loader.git"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "http://www.opensource.org/licenses/mit-license.php"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"name": "style-loader",
|
||||
"version": "0.8.1",
|
||||
"author": "Tobias Koppers @sokra",
|
||||
"description": "style loader module for webpack",
|
||||
"devDependencies": {
|
||||
"css-loader": "~0.8.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:webpack/style-loader.git"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "http://www.opensource.org/licenses/mit-license.php"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"loader-utils": "^0.2.5"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue