From 289e62f0c1fbcf7a1d1ed3e21cfcb44bf3ca5430 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sun, 7 Sep 2014 22:23:54 +0200 Subject: [PATCH] support an array of items with id This ensures that items with the same id are only added once. i. e. multiple css file and `@import` the same base css file and it's only added once to the DOM. webpack/css-loader#17 --- .gitignore | 1 + addStyle.js | 38 ------------ addStyles.js | 116 +++++++++++++++++++++++++++++++++++++ fixtures/a.css | 4 ++ fixtures/b.css | 4 ++ fixtures/base.css | 3 + fixtures/entry.js | 2 + fixtures/webpack.config.js | 7 +++ index.js | 21 ++++--- package.json | 3 + useable.js | 4 +- 11 files changed, 157 insertions(+), 46 deletions(-) create mode 100644 .gitignore delete mode 100644 addStyle.js create mode 100644 addStyles.js create mode 100644 fixtures/a.css create mode 100644 fixtures/b.css create mode 100644 fixtures/base.css create mode 100644 fixtures/entry.js create mode 100644 fixtures/webpack.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30bc162 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules \ No newline at end of file diff --git a/addStyle.js b/addStyle.js deleted file mode 100644 index 1d7aff9..0000000 --- a/addStyle.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -module.exports = function addStyle(cssCode) { - if(typeof DEBUG !== "undefined" && DEBUG) { - if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); - } - var styleElement = document.createElement("style"), - head = document.head || document.getElementsByTagName("head")[0]; - styleElement.type = "text/css"; - head.appendChild(styleElement); - if (styleElement.styleSheet) { - styleElement.styleSheet.cssText = cssCode; - } else { - styleElement.appendChild(document.createTextNode(cssCode)); - } - if(module.hot) { - return function(cssCode) { - if(typeof cssCode === "string") { - if (styleElement.styleSheet) { - styleElement.styleSheet.cssText = cssCode; - } else { - styleElement.childNodes[0].nodeValue = cssCode; - } - } else { - dispose(); - } - }; - } else { - // For the useable API, provide a function to remove the stylesheet. - return dispose; - } - - function dispose() { - head.removeChild(styleElement); - } -}; diff --git a/addStyles.js b/addStyles.js new file mode 100644 index 0000000..d274d7e --- /dev/null +++ b/addStyles.js @@ -0,0 +1,116 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +var stylesInDom = {}; + +module.exports = function(list) { + if(typeof DEBUG !== "undefined" && DEBUG) { + if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); + } + var styles = listToStyles(list); + addStylesToDom(styles); + return function update(newList) { + var mayRemove = []; + for(var i = 0; i < styles.length; i++) { + var item = styles[i]; + var domStyle = stylesInDom[item.id]; + domStyle.refs--; + mayRemove.push(domStyle); + } + if(newList) { + var newStyles = listToStyles(newList); + addStylesToDom(newStyles); + } + for(var i = 0; i < mayRemove.length; i++) { + var domStyle = mayRemove[i]; + if(domStyle.refs === 0) { + for(var j = 0; j < domStyle.parts.length; j++) + domStyle.parts[j](); + delete stylesInDom[domStyle.id]; + } + } + }; +} + +function addStylesToDom(styles) { + for(var i = 0; i < styles.length; i++) { + var item = styles[i]; + var domStyle = stylesInDom[item.id]; + if(domStyle) { + domStyle.refs++; + for(var j = 0; j < domStyle.parts.length; j++) { + domStyle.parts[j](item.parts[j]); + } + for(; j < item.parts.length; j++) { + domStyle.parts.push(addStyle(item.parts[j])); + } + } else { + var parts = []; + for(var j = 0; j < item.parts.length; j++) { + parts.push(addStyle(item.parts[j])); + } + stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts}; + } + } +} + +function listToStyles(list) { + var styles = []; + var newStyles = {}; + for(var i = 0; i < list.length; i++) { + var item = list[i]; + var id = item[0]; + var css = item[1]; + var media = item[2]; + // var sourceMap = item[3]; + var part = {css: css, media: media/*, sourceMap: sourceMap*/}; + if(!newStyles[id]) + styles.push(newStyles[id] = {id: id, parts: [part]}); + else + newStyles[id].parts.push(part); + } + return styles; +} + +function addStyle(obj) { + var styleElement = document.createElement("style"); + var head = document.head || document.getElementsByTagName("head")[0]; + styleElement.type = "text/css"; + head.appendChild(styleElement); + applyToTag(styleElement, obj); + return function(newObj) { + if(newObj) { + if(newObj.css === obj.css && newObj.media === obj.media /*&& newObj.sourceMap === obj.sourceMap*/) + return; + applyToTag(styleElement, obj = newObj); + } else { + head.removeChild(styleElement); + } + }; +}; + +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) { + styleElement.styleSheet.cssText = css; + } else { + while(styleElement.firstChild) { + styleElement.removeChild(styleElement.firstChild); + } + styleElement.appendChild(document.createTextNode(css)); + } + +} diff --git a/fixtures/a.css b/fixtures/a.css new file mode 100644 index 0000000..34a5e71 --- /dev/null +++ b/fixtures/a.css @@ -0,0 +1,4 @@ +@import "base.css"; +body { + border: 1px solid black; +} \ No newline at end of file diff --git a/fixtures/b.css b/fixtures/b.css new file mode 100644 index 0000000..286eb07 --- /dev/null +++ b/fixtures/b.css @@ -0,0 +1,4 @@ +@import "base.css"; +.a { + background: blue; +} \ No newline at end of file diff --git a/fixtures/base.css b/fixtures/base.css new file mode 100644 index 0000000..302ed05 --- /dev/null +++ b/fixtures/base.css @@ -0,0 +1,3 @@ +body { + background: green; +} \ No newline at end of file diff --git a/fixtures/entry.js b/fixtures/entry.js new file mode 100644 index 0000000..9938b27 --- /dev/null +++ b/fixtures/entry.js @@ -0,0 +1,2 @@ +require("./b.css"); +require("./a.css"); \ No newline at end of file diff --git a/fixtures/webpack.config.js b/fixtures/webpack.config.js new file mode 100644 index 0000000..89cd072 --- /dev/null +++ b/fixtures/webpack.config.js @@ -0,0 +1,7 @@ +module.exports = { + module: { + loaders: [ + { test: /\.css$/, loader: "style!css?sourceMap" } + ] + } +} \ No newline at end of file diff --git a/index.js b/index.js index ff6f9a2..1a6770f 100644 --- a/index.js +++ b/index.js @@ -8,15 +8,22 @@ module.exports.pitch = function(remainingRequest) { this.cacheable && this.cacheable(); return [ "// style-loader: Adds some css to the DOM by adding a