diff --git a/src/createUniqueIdentifiers.js b/src/createUniqueIdentifiers.js new file mode 100644 index 00000000..151930c1 --- /dev/null +++ b/src/createUniqueIdentifiers.js @@ -0,0 +1,16 @@ + +module.exports = function createUniqueIdentifiers(identifiers) { + const dupeCount = {}; + + return identifiers.map((identifier) => { + if (typeof dupeCount[identifier] !== 'undefined') { + dupeCount[identifier] += 1; + } else { + dupeCount[identifier] = 0; + } + + return dupeCount[identifier] === 0 + ? `${identifier}` + : `${identifier}_${dupeCount[identifier]}`; + }); +}; diff --git a/src/index.js b/src/index.js index 30bc49b5..f41eafcd 100644 --- a/src/index.js +++ b/src/index.js @@ -17,12 +17,20 @@ module.exports.pitch = function pitch(remainingRequest) { } const insertCssPath = path.join(__dirname, './insertCss.js'); + const createUniqueIdentifiersPath = path.join(__dirname, './createUniqueIdentifiers.js'); let output = ` var content = require(${stringifyRequest(this, `!!${remainingRequest}`)}); var insertCss = require(${stringifyRequest(this, `!${insertCssPath}`)}); + var createUniqueIdentifiers = + require(${stringifyRequest(this, `!${createUniqueIdentifiersPath}`)}); if (typeof content === 'string') { content = [[module.id, content, '']]; + } else { + var identifiers = content.map(x => x[0]); + createUniqueIdentifiers(identifiers).map((identifier, index) => { + content[index][0] = identifier; + }) } module.exports = content.locals || {}; diff --git a/src/insertCss.js b/src/insertCss.js index 7d6c1414..37fd0c91 100644 --- a/src/insertCss.js +++ b/src/insertCss.js @@ -8,7 +8,7 @@ */ const prefix = 's'; -const inserted = {}; +let inserted = {}; // Base64 encoding and decoding - The "Unicode Problem" // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem @@ -49,8 +49,7 @@ function insertCss(styles, options) { const ids = []; for (let i = 0; i < styles.length; i++) { - const [moduleId, css, media, sourceMap] = styles[i]; - const id = `${moduleId}-${i}`; + const [id, css, media, sourceMap] = styles[i]; ids.push(id); @@ -103,4 +102,8 @@ function insertCss(styles, options) { return removeCss.bind(null, ids); } +insertCss._clearCache = () => { + inserted = {}; +}; + module.exports = insertCss; diff --git a/test/createUniqueIdentifiersSpec.js b/test/createUniqueIdentifiersSpec.js new file mode 100644 index 00000000..d298f415 --- /dev/null +++ b/test/createUniqueIdentifiersSpec.js @@ -0,0 +1,60 @@ +/** + * Isomorphic CSS style loader for Webpack + * + * Copyright © 2015-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import { expect } from 'chai'; +import createUniqueIdentifiers from '../src/createUniqueIdentifiers'; + +const { describe, it } = global; + +describe('createUniqueIdentifiers', () => { + describe('when all identifiers are unique', () => { + const identifiers = ['1', '23', '13']; + + it('retains all identifiers', () => { + expect(createUniqueIdentifiers(identifiers)).to.deep.equal(identifiers); + }); + }); + + describe('when some of the identifiers are non-unique', () => { + const identifiers = ['0', '12', '12', '23']; + + it('makes non-unique identifiers unique', () => { + const uniqueIdentifiers = createUniqueIdentifiers(identifiers); + expect(uniqueIdentifiers[1]).to.not.equal(uniqueIdentifiers[2]); + }); + + it('retains unique identifiers', () => { + const uniqueIdentifiers = createUniqueIdentifiers(identifiers); + expect(uniqueIdentifiers[0]).to.equal(identifiers[0]); + expect(uniqueIdentifiers[3]).to.equal(identifiers[3]); + }); + }); + + + describe('when there are multiple groups of non-unique identifiers', () => { + const identifiers = ['12', '14', '12', '4', '800', '800', '801', '12']; + + it('makes non-unique identifiers unique', () => { + const uniqueIdentifiers = createUniqueIdentifiers(identifiers); + + expect(uniqueIdentifiers[0]).to.not.equal(uniqueIdentifiers[2]); + expect(uniqueIdentifiers[0]).to.not.equal(uniqueIdentifiers[7]); + expect(uniqueIdentifiers[2]).to.not.equal(uniqueIdentifiers[7]); + + expect(uniqueIdentifiers[4]).to.not.equal(uniqueIdentifiers[5]); + }); + + it('retains unique identifiers', () => { + const uniqueIdentifiers = createUniqueIdentifiers(identifiers); + expect(uniqueIdentifiers[1]).to.equal(identifiers[1]); + expect(uniqueIdentifiers[3]).to.equal(identifiers[3]); + expect(uniqueIdentifiers[6]).to.equal(identifiers[6]); + }); + }); +}); diff --git a/test/insertCssSpec.js b/test/insertCssSpec.js index 58cc61a1..a1d9c820 100644 --- a/test/insertCssSpec.js +++ b/test/insertCssSpec.js @@ -8,37 +8,69 @@ */ import jsdom from 'jsdom'; -import { describe, it } from 'mocha'; import { expect } from 'chai'; import insertCss from '../src/insertCss'; +const { describe, it, beforeEach } = global; + global.document = jsdom.jsdom(''); global.window = document.parentWindow; +const css1 = 'body { color: red; }'; +const css2 = 'body { color: blue; }'; + +function getStyleTags() { + return global.document.getElementsByTagName('style'); +} + describe('insertCss(styles, options)', () => { - it('Should insert and remove