diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f586ee..389664d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.2.1 - 2018-08-11 +### Changed +- Simpler nested rules management system. + ## 1.2.0 - 2018-08-11 ### Added - Now packing together same media queries and same selectors, respecting css diff --git a/index.js b/index.js index 32624a7..b353180 100644 --- a/index.js +++ b/index.js @@ -105,29 +105,6 @@ const parse = (nodes, opts) => { }; }; -/** - * Return the css rule parent of depth 1 - * @param {rule} rule - */ -const deepParent = rule => { - if (rule.parent.type === 'rule') { - return deepParent(rule.parent); - } - return rule; -}; - -/** - * Return nested selector - * @param {rule} rule - * @param {string} [suffix=''] - */ -const deepParentSelector = (rule, suffix = '') => { - if (rule.parent.type === 'rule') { - return deepParentSelector(rule.parent, `${rule.parent.selector} ${suffix}`); - } - return suffix; -}; - /** * Query class * @param {string} selector @@ -166,56 +143,34 @@ class Query { /** * RulePack class * @param {string} parent + * @param {string} selector */ class RulePack { - constructor(parent) { + constructor(parent, selector) { this.parent = parent; + this.selector = selector; this.queries = []; } - addQuery({ selector, prop, content }) { + addQuery({ prop, content }) { content.queries.forEach(({ media, value }) => { let foundMedia = false; this.queries.forEach(item => { if (item.media === media && !foundMedia) { foundMedia = true; - - let foundSelector = false; - item.selectors.forEach(selectorPack => { - if (selectorPack.selector === selector && !foundSelector) { - foundSelector = true; - - selectorPack.queries.push({ - prop, - value - }); - } + item.queries.push({ + prop, + value }); - if (!foundSelector) { - item.selectors.push({ - selector, - queries: [ - { - prop, - value - } - ] - }); - } } }); if (!foundMedia) { this.queries.push({ media, - selectors: [ + queries: [ { - selector, - queries: [ - { - prop, - value - } - ] + prop, + value } ] }); @@ -238,16 +193,16 @@ module.exports = postcss.plugin('postcss-inline-media', (opts = {}) => { shorthandUnit: typeof opts.shorthandUnit === 'string' ? opts.shorthandUnit : 'px' }); - const parent = deepParent(rule); - const query = new Query(deepParentSelector(rule), rule.prop, content); + const query = new Query(rule.parent.selector, rule.prop, content); if ( mediaQueries.length - && mediaQueries[mediaQueries.length - 1].parent === parent + && mediaQueries[mediaQueries.length - 1].parent === rule.parent + && mediaQueries[mediaQueries.length - 1].selector === query.selector ) { mediaQueries[mediaQueries.length - 1].addQuery(query); } else { - const pack = new RulePack(parent); + const pack = new RulePack(rule.parent, query.selector); pack.addQuery(query); mediaQueries.push(pack); } @@ -264,26 +219,24 @@ module.exports = postcss.plugin('postcss-inline-media', (opts = {}) => { }); mediaQueries.forEach(mq => { - const root = mq.parent.root(); + const root = mq.parent.parent; - mq.queries.forEach(({ media, selectors }) => { + mq.queries.forEach(({ media, queries }) => { const atRule = postcss.atRule({ name: 'media', params: media }); - selectors.forEach(({ selector, queries }) => { - const mediaRule = postcss.rule({ - selector - }); - queries.forEach(({ prop, value }) => { - mediaRule.append({ - prop, - value - }); + const mediaRule = postcss.rule({ + selector: mq.selector + }); + queries.forEach(({ prop, value }) => { + mediaRule.append({ + prop, + value }); - atRule.append(mediaRule); }); + atRule.append(mediaRule); root.append(atRule); }); diff --git a/test.js b/test.js index bcbb2a8..1a963a1 100644 --- a/test.js +++ b/test.js @@ -179,21 +179,46 @@ test('postcss-media-minmax many', async t => { }); }); -test('test', async t => { +test('nested rules', async t => { const input = 'div { margin: 20px @900 10px @600 5px; padding: 20px @900 10px; header { span { color: black @900 red; } } } span { color: black @900 red; }'; const output = [ - 'div { margin: 20px; padding: 20px; header { span { color: black; } } }', - 'span { color: black; }', - '@media (max-width: 900px) {', + 'div {', + 'margin: 20px; padding: 20px; header {', + 'span { color: black; } @media (max-width: 900px) { span { color: red; } }', + '} } span { color: black; } @media (max-width: 900px) {', 'div { margin: 10px; padding: 10px; }', - 'div header span { color: red; }', - '}', - '@media (max-width: 600px) {', - 'div { margin: 5px; }', - '}', + '} @media (max-width: 600px) { div { margin: 5px; } }', + '@media (max-width: 900px) { span { color: red; } }' + ].join(' '); + testPostcss(input, output, t); +}); + +test('nested pseudo element', async t => { + const input = 'div { margin: 20px @900 10px; padding: @600 10px; &::before { color: black @900 red; } }'; + const output = [ + 'div {', + 'margin: 20px; &::before { color: black; }', + '@media (max-width: 900px) {', + '&::before { color: red; }', + '} }', + '@media (max-width: 900px) {', + 'div { margin: 10px; }', + '} @media (max-width: 600px) { div { padding: 10px; } }' + ].join(' '); + testPostcss(input, output, t); +}); + +test('nested unknown rule type', async t => { + const input = 'div { margin: 20px @900 10px; padding: @600 10px; -something { color: black @900 red; } }'; + const output = [ + 'div {', + 'margin: 20px; -something { color: black; }', + '@media (max-width: 900px) {', + '-something { color: red; }', + '} }', '@media (max-width: 900px) {', - 'span { color: red; }', - '}' + 'div { margin: 10px; }', + '} @media (max-width: 600px) { div { padding: 10px; } }' ].join(' '); testPostcss(input, output, t); });