diff --git a/src/index.js b/src/index.js index 2328922c..2283dfe9 100644 --- a/src/index.js +++ b/src/index.js @@ -159,7 +159,9 @@ if (content.locals) { exported.use = function() { if (!(refs++)) { - dispose = api(content, options); + var id = ${loaderUtils.stringifyRequest(this, `!!${request}`)}; + + dispose = api(id, content, options); } return exported; @@ -248,7 +250,8 @@ var options = ${JSON.stringify(options)}; options.insert = ${insert}; options.singleton = ${isSingleton}; -var update = api(content, options); +var id = ${loaderUtils.stringifyRequest(this, `!!${request}`)}; +var update = api(id, content, options); var exported = content.locals ? content.locals : {}; diff --git a/src/runtime/injectStylesIntoStyleTag.js b/src/runtime/injectStylesIntoStyleTag.js index cbfe2db5..0c8c0e59 100644 --- a/src/runtime/injectStylesIntoStyleTag.js +++ b/src/runtime/injectStylesIntoStyleTag.js @@ -46,70 +46,51 @@ const getTarget = (function getTarget() { }; })(); -function listToStyles(list, options) { - const styles = []; - const newStyles = {}; +function addModulesToDom(id, list, options) { + id = options.base ? id + options.base : id; + + if (!stylesInDom[id]) { + stylesInDom[id] = []; + } for (let i = 0; i < list.length; i++) { const item = list[i]; - const id = options.base ? item[0] + options.base : item[0]; - const css = item[1]; - const media = item[2]; - const sourceMap = item[3]; - const part = { css, media, sourceMap }; - - if (!newStyles[id]) { - styles.push((newStyles[id] = { id, parts: [part] })); + const part = { css: item[1], media: item[2], sourceMap: item[3] }; + const styleInDomById = stylesInDom[id]; + + if (styleInDomById[i]) { + styleInDomById[i].updater(part); } else { - newStyles[id].parts.push(part); + styleInDomById.push({ updater: addStyle(part, options) }); } } - return styles; -} - -function addStylesToDom(styles, options) { - for (let i = 0; i < styles.length; i++) { - const item = styles[i]; - const domStyle = stylesInDom[item.id]; - let j = 0; - - if (domStyle) { - domStyle.refs++; - - for (; j < domStyle.parts.length; j++) { - domStyle.parts[j](item.parts[j]); - } + for (let j = list.length; j < stylesInDom[id].length; j++) { + stylesInDom[id][j].updater(); + } - for (; j < item.parts.length; j++) { - domStyle.parts.push(addStyle(item.parts[j], options)); - } - } else { - const parts = []; + stylesInDom[id].length = list.length; - for (; j < item.parts.length; j++) { - parts.push(addStyle(item.parts[j], options)); - } - - stylesInDom[item.id] = { id: item.id, refs: 1, parts }; - } + if (stylesInDom[id].length === 0) { + delete stylesInDom[id]; } } function insertStyleElement(options) { const style = document.createElement('style'); + const attributes = options.attributes || {}; - if (typeof options.attributes.nonce === 'undefined') { + if (typeof attributes.nonce === 'undefined') { const nonce = typeof __webpack_nonce__ !== 'undefined' ? __webpack_nonce__ : null; if (nonce) { - options.attributes.nonce = nonce; + attributes.nonce = nonce; } } - Object.keys(options.attributes).forEach((key) => { - style.setAttribute(key, options.attributes[key]); + Object.keys(attributes).forEach((key) => { + style.setAttribute(key, attributes[key]); }); if (typeof options.insert === 'function') { @@ -179,6 +160,8 @@ function applyToTag(style, options, obj) { if (media) { style.setAttribute('media', media); + } else { + style.removeAttribute('media'); } if (sourceMap && btoa) { @@ -243,51 +226,18 @@ function addStyle(obj, options) { }; } -module.exports = (list, options) => { +module.exports = (id, list, options) => { options = options || {}; - options.attributes = - typeof options.attributes === 'object' ? options.attributes : {}; - // Force single-tag solution on IE6-9, which has a hard limit on the # of

Hello world

"`; +exports[`addStyle should work with updates #12 1`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #12 2`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #12 3`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #12 4`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #13 1`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #13 2`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #13 3`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #13 4`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #13 5`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #13 6`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #13 7`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #14 1`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #14 2`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #14 3`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #14 4`] = `"Title

Hello world

"`; + +exports[`addStyle should work with updates #14 5`] = `"Title

Hello world

"`; + exports[`addStyle should work with updates 1`] = `"Title

Hello world

"`; exports[`addStyle should work with updates 2`] = `"Title

Hello world

"`; diff --git a/test/runtime/injectStylesIntoStyleTag.test.js b/test/runtime/injectStylesIntoStyleTag.test.js index 50637b9b..d5ea4808 100644 --- a/test/runtime/injectStylesIntoStyleTag.test.js +++ b/test/runtime/injectStylesIntoStyleTag.test.js @@ -38,6 +38,14 @@ function insertBeforeAt(element) { window._lastElementInsertedByStyleLoader = element; } +let counter = -1; + +function getId() { + counter += 1; + + return `!!../../node_modules/css-loader/dist/cjs.js!./style.css${counter}`; +} + describe('addStyle', () => { beforeEach(() => { document.head.innerHTML = 'Title'; @@ -47,13 +55,15 @@ describe('addStyle', () => { // Each query should have be unique because style-loader caching styles in dom it('should work', () => { - injectStylesIntoStyleTag([['./style-1.css', '.foo { color: red }', '']]); + injectStylesIntoStyleTag(getId(), [ + ['./style-1.css', '.foo { color: red }', ''], + ]); expect(document.documentElement.innerHTML).toMatchSnapshot(); }); it('should work with multiple styles', () => { - injectStylesIntoStyleTag([ + injectStylesIntoStyleTag(getId(), [ ['./style-2-1.css', '.foo { color: red }', ''], ['./style-2-2.css', '.bar { color: blue }', ''], ]); @@ -62,7 +72,7 @@ describe('addStyle', () => { }); it('should work with same module id in list', () => { - injectStylesIntoStyleTag([ + injectStylesIntoStyleTag(getId(), [ ['./style-3.css', '.foo { color: red }', ''], ['./style-3.css', '.foo { color: green }', ''], ]); @@ -71,7 +81,7 @@ describe('addStyle', () => { }); it('should work with media', () => { - injectStylesIntoStyleTag([ + injectStylesIntoStyleTag(getId(), [ ['./style-4.css', '.foo { color: red }', 'screen and (min-width:320px)'], ]); @@ -79,7 +89,7 @@ describe('addStyle', () => { }); it('should work with source maps', () => { - injectStylesIntoStyleTag([ + injectStylesIntoStyleTag(getId(), [ [ './style-5.css', '.foo { color: red }', @@ -102,7 +112,9 @@ describe('addStyle', () => { // eslint-disable-next-line no-underscore-dangle window.__webpack_nonce__ = '12345678'; - injectStylesIntoStyleTag([['./style-6.css', '.foo { color: red }', '']]); + injectStylesIntoStyleTag(getId(), [ + ['./style-6.css', '.foo { color: red }', ''], + ]); expect(document.documentElement.innerHTML).toMatchSnapshot(); @@ -114,9 +126,13 @@ describe('addStyle', () => { // eslint-disable-next-line no-underscore-dangle window.__webpack_nonce__ = '12345678'; - injectStylesIntoStyleTag([['./style-7.css', '.foo { color: red }', '']], { - attributes: { nonce: '87654321' }, - }); + injectStylesIntoStyleTag( + getId(), + [['./style-7.css', '.foo { color: red }', '']], + { + attributes: { nonce: '87654321' }, + } + ); expect(document.documentElement.innerHTML).toMatchSnapshot(); @@ -125,23 +141,32 @@ describe('addStyle', () => { }); it('should work with "base" option', () => { - injectStylesIntoStyleTag([['./style-8.css', '.foo { color: red }', '']], { - base: 1000, - }); + injectStylesIntoStyleTag( + getId(), + [['./style-8.css', '.foo { color: red }', '']], + { + base: 1000, + } + ); expect(document.documentElement.innerHTML).toMatchSnapshot(); }); it('should work with "attributes" option', () => { - injectStylesIntoStyleTag([['./style-9.css', '.foo { color: red }', '']], { - attributes: { foo: 'bar' }, - }); + injectStylesIntoStyleTag( + getId(), + [['./style-9.css', '.foo { color: red }', '']], + { + attributes: { foo: 'bar' }, + } + ); expect(document.documentElement.innerHTML).toMatchSnapshot(); }); it('should work with "attributes" option #2', () => { injectStylesIntoStyleTag( + getId(), [ ['./style-10-1.css', '.foo { color: red }', ''], ['./style-10-2.css', '.bar { color: blue }', ''], @@ -156,6 +181,7 @@ describe('addStyle', () => { it('should work with "insert" option', () => { injectStylesIntoStyleTag( + getId(), [ ['./style-11-1.css', '.foo { color: red }', ''], ['./style-11-2.css', '.bar { color: blue }', ''], @@ -170,6 +196,7 @@ describe('addStyle', () => { it('should work with "insert" option #2', () => { injectStylesIntoStyleTag( + getId(), [ ['./style-12-1.css', '.foo { color: red }', ''], ['./style-12-2.css', '.bar { color: blue }', ''], @@ -187,6 +214,7 @@ describe('addStyle', () => { "

Hello world