Remove global variable, add ID to style elements so server- and client-rendered match each other

master
Vitaliy Filippov 2021-08-11 21:11:06 +03:00
parent e5aaab0300
commit 7d48da227e
2 changed files with 86 additions and 41 deletions

View File

@ -1,7 +1,8 @@
/* /**
MIT License http://www.opensource.org/licenses/mit-license.php * MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra * Tobias Koppers @sokra
*/ * Vitaliy Filippov vitalif@yourcmc.ru
*/
var stylesInDom = {}, var stylesInDom = {},
memoize = function(fn) { memoize = function(fn) {
var memo; var memo;
@ -42,15 +43,17 @@ module.exports = function(list, options) {
options = options || {}; options = options || {};
options.attrs = typeof options.attrs === "object" ? options.attrs : {}; options.attrs = typeof options.attrs === "object" ? options.attrs : {};
// Force single-tag solution on IE6-9, which has a hard limit on the # of <style> // By default, use "__universalLoaderStyles" as the ID of style element
// tags it will allow on a page if (!options.elementId) options.elementId = "__universalLoaderStyles";
if (typeof options.singleton === "undefined") options.singleton = isOldIE();
// Use single-tag solution by default
if (options.singleton == undefined) options.singleton = true;
// By default, add <style> tags to the <head> element // By default, add <style> tags to the <head> element
if (typeof options.insertInto === "undefined") options.insertInto = "head"; if (options.insertInto == undefined) options.insertInto = "head";
// By default, add <style> tags to the bottom of the target // By default, add <style> tags to the bottom of the target
if (typeof options.insertAt === "undefined") options.insertAt = "bottom"; if (options.insertAt == undefined) options.insertAt = "bottom";
var styles = listToStyles(list); var styles = listToStyles(list);
addStylesToDom(styles, options); addStylesToDom(styles, options);
@ -103,6 +106,7 @@ function addStylesToDom(styles, options) {
function listToStyles(list) { function listToStyles(list) {
var styles = []; var styles = [];
var newStyles = {}; var newStyles = {};
list = list.default || list;
for(var i = 0; i < list.length; i++) { for(var i = 0; i < list.length; i++) {
var item = list[i]; var item = list[i];
var id = item[0]; var id = item[0];
@ -148,12 +152,15 @@ function removeStyleElement(styleElement) {
} }
} }
function createStyleElement(options) { function createStyleElement(options, id) {
var styleElement = document.createElement("style"); var styleElement = document.getElementById(id);
options.attrs.type = "text/css"; options.attrs.type = "text/css";
if (!styleElement) {
attachTagAttrs(styleElement, options.attrs); styleElement = document.createElement("style");
insertStyleElement(options, styleElement); attachTagAttrs(styleElement, options.attrs);
styleElement.setAttribute('id', id);
insertStyleElement(options, styleElement);
}
return styleElement; return styleElement;
} }
@ -176,9 +183,9 @@ function attachTagAttrs(element, attrs) {
function addStyle(obj, options) { function addStyle(obj, options) {
var styleElement, update, remove; var styleElement, update, remove;
var styleIndex = singletonCounter++;
if (options.singleton) { if (options.singleton) {
var styleIndex = singletonCounter++; styleElement = singletonElement || (singletonElement = createStyleElement(options, options.elementId));
styleElement = singletonElement || (singletonElement = createStyleElement(options));
update = applyToSingletonTag.bind(null, styleElement, styleIndex, false); update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true); remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
} else if(obj.sourceMap && } else if(obj.sourceMap &&
@ -195,7 +202,7 @@ function addStyle(obj, options) {
URL.revokeObjectURL(styleElement.href); URL.revokeObjectURL(styleElement.href);
}; };
} else { } else {
styleElement = createStyleElement(options); styleElement = createStyleElement(options, options.elementId+styleIndex);
update = applyToTag.bind(null, styleElement); update = applyToTag.bind(null, styleElement);
remove = function() { remove = function() {
removeStyleElement(styleElement); removeStyleElement(styleElement);
@ -232,7 +239,8 @@ function applyToSingletonTag(styleElement, index, remove, obj) {
} else { } else {
var cssNode = document.createTextNode(css); var cssNode = document.createTextNode(css);
var childNodes = styleElement.childNodes; var childNodes = styleElement.childNodes;
if (childNodes[index]) styleElement.removeChild(childNodes[index]); if (childNodes[index])
styleElement.removeChild(childNodes[index]);
if (childNodes.length) { if (childNodes.length) {
styleElement.insertBefore(cssNode, childNodes[index]); styleElement.insertBefore(cssNode, childNodes[index]);
} else { } else {

View File

@ -1,39 +1,76 @@
/* /**
MIT License http://www.opensource.org/licenses/mit-license.php * MIT License http://www.opensource.org/licenses/mit-license.php
Istvan Jano janoist1@gmail.com * Istvan Jano janoist1@gmail.com
* Vitaliy Filippov vitalif@yourcmc.ru
*/ */
var collectedStyleElementId = "__universalLoaderStyles";
var collectedStyleSingleton = true;
var collectedStyles = [];
/** /**
* Add styles - used by the loader * Add styles - used by the loader
* *
* @param list * @param list
* @param options * @param options
*/ */
module.exports.addStyles = function (list, options) { function addStyles(list, options)
global.__styles__ = global.__styles__ || [] {
var newStyles = {} // By default, use "__universalLoaderStyles" as the ID of style element
collectedStyleElementId = options && options.elementId || "__universalLoaderStyles";
for (var i = 0; i < list.length; i++) { collectedStyleSingleton = !options || options.singleton == undefined ? true : options.singleton;
var item = list[i] var newStyles = {};
var id = item[0] // default is for ES6 CSS modules
var css = item[1] list = list.default || list;
var media = item[2] for (var i = 0; i < list.length; i++)
var sourceMap = item[3] {
var part = {css: css, media: media, sourceMap: sourceMap} var item = list[i];
var id = item[0];
if (!newStyles[id]){ var css = item[1];
global.__styles__.push(newStyles[id] = {id: id, parts: [part]}) var media = item[2];
} else { var sourceMap = item[3];
var part = { css: css, media: media, sourceMap: sourceMap };
if (!newStyles[id])
collectedStyles.push(newStyles[id] = { id: id, parts: [ part ]})
else
newStyles[id].parts.push(part) newStyles[id].parts.push(part)
}
} }
} }
/** /**
* Return the styles that have been collected so far * Return the styles that have been collected so far as array
* *
* @returns {*} * @returns object[]
*/ */
module.exports.getStyles = function () { function getStyleList()
return global.__styles__ {
return collectedStyles;
} }
/**
* Return the styles that have been collected so far as a <style>
* element code ready for insertion into <head>
*
* @returns string
*/
function getStyles()
{
if (collectedStyleSingleton)
{
return "<style type=\"text/css\" id=\""+collectedStyleElementId+"\">"+
collectedStyles.map(style => style.parts.map(part => part.css + "\n").join('')).join('')+
"</style>";
}
else
{
return collectedStyles.map((style, idx) =>
"<style type=\"text/css\" id=\""+collectedStyleElementId+idx+"\">"+
style.parts.map(part => part.css + "\n").join('')+
"</style>"
).join('');
}
}
module.exports.addStyles = addStyles;
module.exports.getStyleList = getStyleList;
module.exports.getStyles = getStyles;