From 9c3673c3961700b778341e047e55cce492a4f3ec Mon Sep 17 00:00:00 2001 From: Sergey Belozyorcev Date: Sun, 10 Jul 2016 20:02:26 +0300 Subject: [PATCH 1/4] :cool: Add extended syntax --- lib/index.js | 70 ++++++++++++++++++++++++++++++++++++++++------- readme.md | 10 ++++++- test/lib/index.js | 27 ++++++++++++++++++ 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/lib/index.js b/lib/index.js index ddeaba5..6f17e3a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,21 +3,71 @@ import postcss from 'postcss'; const modDelim = process.env.REBEM_MOD_DELIM || '_'; const elemDelim = process.env.REBEM_ELEM_DELIM || '__'; +function buildSelector (block) { + let selector = '.'; + + if (block.blockName) { + selector += block.blockName; + } + if (block.elemName) { + selector += elemDelim + block.elemName; + } + if (block.modName) { + selector += modDelim + block.modName; + } + + return selector; +} + export default postcss.plugin('rebem-css', () => (css) => { css.walkRules((rule) => { rule.selector = rule.selector - // :block(block) → .block - .replace(/:block\(([\w-]+)\)/g, '.$1') - // :elem(elem) → __elem - .replace(/:elem\(([\w-]+)\)/g, elemDelim + '$1') - // :mod(mod) → _mod - // :mod(mod val) → _mod_val - .replace(/:mod\(([\w-]+)\s?([\w-]+)?\)/g, (match, mod, val) => { - if (val) { - return modDelim + mod + modDelim + val; + .replace(/(:block\(.+)/g, (match, rawSelector) => { + + // ":block(b):mod(m v) div :block(b2)" -> [":block(b):mod(m v)", "div", "block(b2)"] + const groups = rawSelector.split(/\s+(?!\w+\))/gi); + + const re = /:(block|elem|mod)\(([\w-\s]+)\)(\s+)?/g; + const blockDecl = { blockName: false, elemName: false, modName: false }; + + // Convert all groups to CSS selectors + // :block(b):mod(m v) -> .b_m_v + + const result = []; + + for (const group of groups) { + if (!group.match(re)) { + result.push(group); + continue; + } + + let selector = ''; + let mathes = null; + + while ((mathes = re.exec(group)) !== null) { + + if (mathes[1] === 'block') { + blockDecl.blockName = mathes[2]; + } + + if (mathes[1] === 'elem') { + blockDecl.elemName = mathes[2]; + } + + if (mathes[1] === 'mod') { + blockDecl.modName = mathes[2].replace(' ', modDelim); + selector += buildSelector(blockDecl); + } + + } + if (!blockDecl.modName) { + selector += buildSelector(blockDecl); + } + + result.push(selector); } - return modDelim + mod; + return result.join(' '); }); }); }); diff --git a/readme.md b/readme.md index f5f3a89..83c55f9 100644 --- a/readme.md +++ b/readme.md @@ -24,6 +24,9 @@ It just replaces substrings in selectors: ```css :block(block):elem(elem) {} .block__elem {} + +:block(block):elem(elem) :elem(elem2) :block(block2):elem(elem) {} +.block__elem .block__elem2 .block2__elem {} ``` #### `:mod()` @@ -34,6 +37,9 @@ It just replaces substrings in selectors: :block(block):mod(mod val) {} .block_mod_val {} + +:block(block):mod(mod val):mod(mod2) {} +.block_mod_val.block_mod2 {} ``` ```css @@ -51,7 +57,9 @@ It's just a custom pseudo-classes, so you can use it with Less or any other CSS ```less :block(block) { &:mod(mod) { - + :elem(elem) { + + } } &:elem(elem) { diff --git a/test/lib/index.js b/test/lib/index.js index 46c9532..986e38f 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -26,6 +26,13 @@ describe('plugin', () => { '.block1 .block2' ); }); + + it('with other tags', () => { + test( + ':block(block1) div :block(bl2) img', + '.block1 div .bl2 img' + ); + }); }); describe('elem', () => { @@ -42,6 +49,13 @@ describe('plugin', () => { '.block1__elem1 .block2__elem2' ); }); + + it('block multiple short elems', () => { + test( + ':block(block1):elem(elem1) :elem(elem2) :elem(elem3)', + '.block1__elem1 .block1__elem2 .block1__elem3' + ); + }); }); describe('mod', () => { @@ -53,6 +67,13 @@ describe('plugin', () => { ); }); + it('block multiple short mods', () => { + test( + ':block(block):mod(mod):mod(mod2)', + '.block_mod.block_mod2' + ); + }); + it('multiple blocks shorts mods', () => { test( ':block(block1):mod(mod1) :block(block2):mod(mod2)', @@ -82,6 +103,12 @@ describe('plugin', () => { '.block__elem_mod' ); }); + it('elem multiple short mods', () => { + test( + ':block(block):elem(elem):mod(mod):mod(mod2)', + '.block__elem_mod.block__elem_mod2' + ); + }); it('multiple elems short mods', () => { test( From d43943548799d56e87e60291d08bf2d6d828c676 Mon Sep 17 00:00:00 2001 From: Sergey Belozyorcev Date: Sun, 10 Jul 2016 21:39:02 +0300 Subject: [PATCH 2/4] Update dependencies --- package.json | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index a682207..dc07db1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,11 @@ "name": "rebem-css", "version": "0.2.0", "description": "BEM syntax for CSS", - "keywords": [ "rebem", "bem", "css" ], + "keywords": [ + "rebem", + "bem", + "css" + ], "homepage": "https://github.com/rebem/css", "repository": "rebem/css", "maintainers": [ @@ -10,23 +14,23 @@ "Denis Koltsov (https://github.com/mistadikay)" ], "main": "build/index.js", - "files": [ "build/" ], + "files": [ + "build/" + ], "dependencies": { "postcss": "5.0.x" }, "devDependencies": { - "start-babel-cli": "1.x.x", - "start-rebem-preset": "0.x.x", - - "babel-preset-es2015": "6.6.x", - "babel-plugin-add-module-exports": "0.1.x", - "babel-eslint": "5.0.x", + "babel-plugin-add-module-exports": "0.1.x", + "babel-preset-es2015": "6.6.x", + "eslint-config-rebem": "1.1.x", "eslint-plugin-babel": "3.1.x", - "eslint-config-rebem": "0.3.x", - + "estraverse-fb": "^1.3.1", + "husky": "0.11.x", "require-uncached": "1.0.x", - "husky": "0.11.x" + "start-babel-cli": "1.x.x", + "start-rebem-preset": "0.x.x" }, "scripts": { "start": "start-runner start-rebem-preset", From 3bcd20ce492767d43ef39fa3e58857e7f88cea13 Mon Sep 17 00:00:00 2001 From: Sergey Belozyorcev Date: Wed, 13 Jul 2016 02:11:22 +0300 Subject: [PATCH 3/4] Merge from 'pobems v0.3.1' --- lib/index.js | 64 +++++++++++++++++++++++++++++++++-------------- test/lib/index.js | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 19 deletions(-) diff --git a/lib/index.js b/lib/index.js index 6f17e3a..db0ea1e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,17 +3,17 @@ import postcss from 'postcss'; const modDelim = process.env.REBEM_MOD_DELIM || '_'; const elemDelim = process.env.REBEM_ELEM_DELIM || '__'; -function buildSelector (block) { +function buildSelector (ctx, mod) { let selector = '.'; - if (block.blockName) { - selector += block.blockName; + if (ctx.blockName) { + selector += ctx.blockName; } - if (block.elemName) { - selector += elemDelim + block.elemName; + if (ctx.elemName) { + selector += elemDelim + ctx.elemName; } - if (block.modName) { - selector += modDelim + block.modName; + if (mod) { + selector += modDelim + mod; } return selector; @@ -25,10 +25,10 @@ export default postcss.plugin('rebem-css', () => (css) => { .replace(/(:block\(.+)/g, (match, rawSelector) => { // ":block(b):mod(m v) div :block(b2)" -> [":block(b):mod(m v)", "div", "block(b2)"] - const groups = rawSelector.split(/\s+(?!\w+\))/gi); + const groups = rawSelector.split(/\s+(?![\w\s->'",]+\))/gi); - const re = /:(block|elem|mod)\(([\w-\s]+)\)(\s+)?/g; - const blockDecl = { blockName: false, elemName: false, modName: false }; + const re = /(:+)([\w-]+)(\((['",\w->\s]+)\))?/g; + const ctx = { blockName: false, elemName: false }; // Convert all groups to CSS selectors // :block(b):mod(m v) -> .b_m_v @@ -43,25 +43,51 @@ export default postcss.plugin('rebem-css', () => (css) => { let selector = ''; let mathes = null; + let requiredBuild = false; while ((mathes = re.exec(group)) !== null) { + const _spliter = 1; + const _tag = 2; + const _rawValue = 3; + const _value = 4; + const spliter = mathes[_spliter]; + const tag = mathes[_tag]; + const rawValue = mathes[_rawValue]; + const value = mathes[_value] ? + mathes[_value].replace(/([\(\)'"])/g, '').trim() : false; + + + if (tag === 'block') { + requiredBuild = true; + ctx.blockName = value; + continue; + } - if (mathes[1] === 'block') { - blockDecl.blockName = mathes[2]; + if (tag === 'elem') { + requiredBuild = true; + ctx.elemName = value; + continue; } - if (mathes[1] === 'elem') { - blockDecl.elemName = mathes[2]; + if (tag === 'mod') { + requiredBuild = false; + const mod = value.replace(/(\s?->\s?|,\s?|\s+?)/g, modDelim); + + selector += buildSelector(ctx, mod); + continue; } - if (mathes[1] === 'mod') { - blockDecl.modName = mathes[2].replace(' ', modDelim); - selector += buildSelector(blockDecl); + // For pseudo-classes + selector += requiredBuild ? buildSelector(ctx) : ''; + selector += spliter + tag; + if (rawValue) { + selector += rawValue; } + requiredBuild = false; } - if (!blockDecl.modName) { - selector += buildSelector(blockDecl); + if (requiredBuild) { + selector += buildSelector(ctx); } result.push(selector); diff --git a/test/lib/index.js b/test/lib/index.js index 986e38f..cae7917 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -27,6 +27,27 @@ describe('plugin', () => { ); }); + it('block name with double quotes', () => { + test( + ':block("block1") :block("block2")', + '.block1 .block2' + ); + }); + + it('block name with single quotes', () => { + test( + ':block(\'block1\') :block(\'block2\')', + '.block1 .block2' + ); + }); + + it('with pseudo classes', () => { + test( + ':root :block(block1):mod(m v):hover::before :block(block1):nth-of-type(2)', + ':root .block1_m_v:hover::before .block1:nth-of-type(2)' + ); + }); + it('with other tags', () => { test( ':block(block1) div :block(bl2) img', @@ -81,6 +102,13 @@ describe('plugin', () => { ); }); + it('multiple blocks mods with delimeter "-" in value', () => { + test( + ':block(block):mod(mod val-1) :block(block):elem(icon)', + '.block_mod_val-1 .block__icon' + ); + }); + it('block mod', () => { test( ':block(block):mod(mod val)', @@ -88,12 +116,33 @@ describe('plugin', () => { ); }); + it('block mod with double quotes', () => { + test( + ':block("block1"):mod("mod", "val")', + '.block1_mod_val' + ); + }); + + it('block mod with single quotes', () => { + test( + ':block(\'block1\'):mod(\'mod\',\'val\')', + '.block1_mod_val' + ); + }); + it('multiple blocks mods', () => { test( ':block(block1):mod(mod1 val1) :block(block2):mod(mod2 val2)', '.block1_mod1_val1 .block2_mod2_val2' ); }); + + it('mod val with delimeter "->"', () => { + test( + ':block(block1):mod(mod1 -> val1) :block(block2):mod("mod2" -> "val2")', + '.block1_mod1_val1 .block2_mod2_val2' + ); + }); }); describe('elem', () => { From aa17b2c87ac44a2a56bd15656e79770be68629a6 Mon Sep 17 00:00:00 2001 From: Sergey Belozyorcev Date: Thu, 14 Jul 2016 09:27:13 +0300 Subject: [PATCH 4/4] Fix block after elem --- lib/index.js | 1 + test/lib/index.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index db0ea1e..42686af 100644 --- a/lib/index.js +++ b/lib/index.js @@ -60,6 +60,7 @@ export default postcss.plugin('rebem-css', () => (css) => { if (tag === 'block') { requiredBuild = true; ctx.blockName = value; + ctx.elemName = false; continue; } diff --git a/test/lib/index.js b/test/lib/index.js index cae7917..ecc3599 100644 --- a/test/lib/index.js +++ b/test/lib/index.js @@ -22,8 +22,8 @@ describe('plugin', () => { it('multiple blocks', () => { test( - ':block(block1) :block(block2)', - '.block1 .block2' + ':block(block1):elem(elem) :block(block2)', + '.block1__elem .block2' ); });