Skip to content
This repository was archived by the owner on Aug 19, 2019. It is now read-only.

🆒 Add extended syntax #4

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 87 additions & 10 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,98 @@ import postcss from 'postcss';
const modDelim = process.env.REBEM_MOD_DELIM || '_';
const elemDelim = process.env.REBEM_ELEM_DELIM || '__';

function buildSelector (ctx, mod) {
let selector = '.';

if (ctx.blockName) {
selector += ctx.blockName;
}
if (ctx.elemName) {
selector += elemDelim + ctx.elemName;
}
if (mod) {
selector += modDelim + mod;
}

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\s->'",]+\))/gi);

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

const result = [];

for (const group of groups) {
if (!group.match(re)) {
result.push(group);
continue;
}

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;
ctx.elemName = false;
continue;
}

if (tag === 'elem') {
requiredBuild = true;
ctx.elemName = value;
continue;
}

if (tag === 'mod') {
requiredBuild = false;
const mod = value.replace(/(\s?->\s?|,\s?|\s+?)/g, modDelim);

selector += buildSelector(ctx, mod);
continue;
}

// For pseudo-classes
selector += requiredBuild ? buildSelector(ctx) : '';
selector += spliter + tag;
if (rawValue) {
selector += rawValue;
}

requiredBuild = false;
}
if (requiredBuild) {
selector += buildSelector(ctx);
}

result.push(selector);
}

return modDelim + mod;
return result.join(' ');
});
});
});
26 changes: 15 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,35 @@
"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": [
"Kir Belevich <[email protected]> (https://github.com/deepsweet)",
"Denis Koltsov <[email protected]> (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",
Expand Down
10 changes: 9 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()`
Expand All @@ -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
Expand All @@ -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) {
Expand Down
78 changes: 77 additions & 1 deletion test/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,38 @@ describe('plugin', () => {

it('multiple blocks', () => {
test(
':block(block1) :block(block2)',
':block(block1):elem(elem) :block(block2)',
'.block1__elem .block2'
);
});

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',
'.block1 div .bl2 img'
);
});
});

describe('elem', () => {
Expand All @@ -42,6 +70,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', () => {
Expand All @@ -53,26 +88,61 @@ 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)',
'.block1_mod1 .block2_mod2'
);
});

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)',
'.block_mod_val'
);
});

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', () => {
Expand All @@ -82,6 +152,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(
Expand Down