From 5e8d0b5ddc0784dfcbef7a22b1f3c5808096853b Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Sat, 8 Apr 2017 13:25:54 -0700 Subject: [PATCH] feat: add support for locally scoped css (#2) --- lib/index.js | 45 ++++++++++++++++++++++--------- test/fixtures/entry.css | 4 +++ test/fixtures/entry.extracted.css | 4 +++ test/fixtures/entry.js | 4 +-- test/index.test.js | 7 ++++- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/index.js b/lib/index.js index d5d7267..0f6944a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,7 +7,6 @@ const CssHandler = require('./handlers/css-handler') const MANDATORY_BLACKLIST = [ /^webpack\/bootstrap/, - '/css-loader/index.js', '/css-loader/lib/css-base.js', '/style-loader/addStyles.js', ] @@ -34,20 +33,40 @@ class NukeCssPlugin { }) } - static gatherSourceContent(options, assets) { + static determineJsContent(node) { + const source = node.source + const content = node.originalSource + + if (source.includes('/css-loader/index.js') && !content.includes('removed by extract-text')) { + const indexOfLocals = content.indexOf('exports.locals') + return indexOfLocals >= 0 ? content.slice(indexOfLocals) : '' + } else { + return content + } + } + + static gatherJsSourceContent(blacklist, asset) { + return asset.listMap({}).children + .map(node => { + if (!node.source) { + return false + } else if (blacklist.find(pattern => pattern.test(node.source))) { + return false + } + + return { + type: 'js', + content: NukeCssPlugin.determineJsContent(node), + } + }) + .filter(item => item && item.content) + } + + static gatherAllSourceContent(options, assets) { const blacklist = NukeCssPlugin.getBlacklist(options) return _(assets) .filter(item => item.type === 'js') - .map(({asset, type}) => { - return asset.listMap({}).children - .filter(node => { - const doesNotMatch = pattern => !pattern.test(node.source) - return node.source && _.every(blacklist, doesNotMatch) - }) - .map(node => { - return {type, content: node.originalSource} - }) - }) + .map(item => NukeCssPlugin.gatherJsSourceContent(blacklist, item.asset)) .flatten() .concat(options.sources) .value() @@ -59,7 +78,7 @@ class NukeCssPlugin { return {asset, name, type} }) - const sourceContent = NukeCssPlugin.gatherSourceContent(this._options, assets) + const sourceContent = NukeCssPlugin.gatherAllSourceContent(this._options, assets) assets.forEach(({asset, name, type}) => { try { const handler = this._handlers[type] diff --git a/test/fixtures/entry.css b/test/fixtures/entry.css index 98485b5..a017615 100644 --- a/test/fixtures/entry.css +++ b/test/fixtures/entry.css @@ -28,3 +28,7 @@ .fa-blacklisted { content: 'fa'; } + +:local .my-local-class :global .fa-table { + content: 'locally scoped'; +} diff --git a/test/fixtures/entry.extracted.css b/test/fixtures/entry.extracted.css index b3e2e4e..3b6d18c 100644 --- a/test/fixtures/entry.extracted.css +++ b/test/fixtures/entry.extracted.css @@ -44,3 +44,7 @@ a:hover { .html-ignored { color: white; } + +:local .my-other-local-class :global .fa-table { + content: 'locally scoped'; +} diff --git a/test/fixtures/entry.js b/test/fixtures/entry.js index 6601bc7..9d0b704 100644 --- a/test/fixtures/entry.js +++ b/test/fixtures/entry.js @@ -1,5 +1,5 @@ const data = require('./entry.css') -require('./entry.extracted.css') +const data2 = require('./entry.extracted.css') const myIcons = { 'fa-address-book-o': true, 'fa': true, @@ -7,4 +7,4 @@ const myIcons = { 'fa-table': true, } -console.log(data, myIcons) +console.log(data, data2, myIcons) diff --git a/test/index.test.js b/test/index.test.js index 7cc4fc0..2e5df29 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -36,7 +36,7 @@ describe('NukeCssPlugin', () => { function findLineAndColumn(css, string) { const lines = css.split('\n') const line = lines.findIndex(l => l.includes(string)) + 1 - if (line === -1) { + if (line === -1 || !lines[line - 1]) { throw new Error(`could not find string ${string}`) } @@ -59,6 +59,11 @@ describe('NukeCssPlugin', () => { expect(fileStats['out.css'].content).to.not.contain('.my-favorite-class') }) + it('should work with locally scoped', () => { + expect(fileStats['out.js'].content).to.contain('locally scoped') + expect(fileStats['out.css'].content).to.contain('locally scoped') + }) + it('should generate a source map', () => { expect(fileStats).to.have.property('out.css.map')