-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fillup method in import-notation #312
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,7 @@ | ||
const hashSet = require('hash-set'); | ||
const helpers = require('./lib/helpers'); | ||
|
||
const tmpl = { | ||
b : b => `b:${b}`, | ||
e : e => e ? ` e:${e}` : '', | ||
m : m => Object.keys(m).map(name => `${tmpl.mn(name)}${tmpl.mv(m[name])}`).join(''), | ||
mn : m => ` m:${m}`, | ||
mv : v => v.length ? `=${v.join('|')}` : '', | ||
t : t => t ? ` t:${t}` : '' | ||
}; | ||
|
||
const btmpl = Object.assign({}, tmpl, { | ||
m : m => m ? `${tmpl.mn(m['name'])}${tmpl.mv([m['val']])}` : '' | ||
}); | ||
|
||
const BemCellSet = hashSet(cell => | ||
['block', 'elem', 'mod', 'tech'] | ||
.map(k => btmpl[k[0]](cell[k])) | ||
.join('') | ||
); | ||
const BemCellSet = hashSet(helpers.stringifyCell); | ||
|
||
/** | ||
* Parse import statement and extract bem entities | ||
|
@@ -38,43 +22,41 @@ const BemCellSet = hashSet(cell => | |
*/ | ||
function parse(importString, scope) { | ||
const main = {}; | ||
scope || (scope = {}); | ||
|
||
scope && (importString = helpers.fillup(importString, scope)); | ||
|
||
return Array.from(importString.split(' ').reduce((acc, importToken) => { | ||
const split = importToken.split(':'), | ||
type = split[0], | ||
tail = split[1]; | ||
val = split[1]; | ||
|
||
if(type === 'b') { | ||
main.block = tail; | ||
switch(type) { | ||
case 'b': | ||
main.block = val; | ||
acc.add(main); | ||
} else if(type === 'e') { | ||
main.elem = tail; | ||
if(!main.block && scope.elem !== tail) { | ||
main.block = scope.block; | ||
acc.add(main); | ||
} | ||
} else if(type === 'm' || type === 't') { | ||
if(!main.block) { | ||
main.block = scope.block; | ||
main.elem || scope.elem && (main.elem = scope.elem); | ||
acc.add(main); | ||
} | ||
|
||
if(type === 'm') { | ||
const splitMod = tail.split('='), | ||
modName = splitMod[0], | ||
modVals = splitMod[1]; | ||
break; | ||
case 'e': | ||
// mutates already added item | ||
main.elem = val; | ||
break; | ||
case 'm': { | ||
const splitMod = val.split('='), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Аналогично — можно заменить на деструкторизацию There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Кажется, что стоит отказаться от There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. создай плиз issue про это со ссылкой на место, где написано, что она перестанет поддерживаться |
||
modName = splitMod[0], | ||
modVals = splitMod[1]; | ||
|
||
acc.add(Object.assign({}, main, { mod : { name : modName } })); | ||
acc.add(Object.assign({}, main, { mod : { name : modName } })); | ||
|
||
modVals && modVals.split('|').forEach(modVal => { | ||
acc.add(Object.assign({}, main, { mod : { name : modName, val : modVal } })); | ||
}); | ||
} else { | ||
acc.size || acc.add(main); | ||
acc.forEach(e => (e.tech = tail)); | ||
} | ||
modVals && modVals.split('|').forEach(modVal => { | ||
acc.add(Object.assign({}, main, { mod : { name : modName, val : modVal } })); | ||
}); | ||
break; | ||
} | ||
case 't': | ||
// mutates already added items | ||
acc.forEach(e => { | ||
e.tech = val; | ||
}); | ||
break; | ||
} | ||
return acc; | ||
}, new BemCellSet())); | ||
|
@@ -94,20 +76,21 @@ function parse(importString, scope) { | |
*/ | ||
function stringify(cells) { | ||
const merged = [].concat(cells).reduce((acc, cell) => { | ||
cell.block && (acc.b = cell.block); | ||
cell.elem && (acc.e = cell.elem); | ||
cell.mod && (acc.m[cell.mod.name] || (acc.m[cell.mod.name] = [])) | ||
cell.block && (acc.block = cell.block); | ||
cell.elem && (acc.elem = cell.elem); | ||
cell.mod && (acc.mod[cell.mod.name] || (acc.mod[cell.mod.name] = [])) | ||
&& cell.mod.val && typeof cell.mod.val !== 'boolean' | ||
&& !~acc.m[cell.mod.name].indexOf(cell.mod.val) | ||
&& acc.m[cell.mod.name].push(cell.mod.val); | ||
cell.tech && (acc.t = cell.tech); | ||
&& !~acc.mod[cell.mod.name].indexOf(cell.mod.val) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ну камон ребята: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. тут везде такое кунфу) но мне тож не нравится There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. через includes? |
||
&& acc.mod[cell.mod.name].push(cell.mod.val); | ||
cell.tech && (acc.tech = cell.tech); | ||
return acc; | ||
}, { m : {} }); | ||
}, { mod : {} }); | ||
|
||
return ['b', 'e', 'm', 't'].map(k => tmpl[k](merged[k])).join(''); | ||
return helpers.stringifyMergedCells(merged); | ||
} | ||
|
||
module.exports = { | ||
parse, | ||
stringify | ||
stringify, | ||
fillup : helpers.fillup | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
/** | ||
* Helpers to test import-notation first part | ||
*/ | ||
const tests = { | ||
/** | ||
* Returns true if notation starts with block-part | ||
* | ||
* Example: | ||
* ```js | ||
* b('b:button e:icon') // true | ||
* b('e:icon') // false | ||
* ``` | ||
* | ||
* @param {String} n - notation | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
b : n => /^b:/.test(n), | ||
|
||
/** | ||
* Returns true if notation starts with element-part | ||
* | ||
* Example: | ||
* ```js | ||
* b('b:button e:icon') // false | ||
* b('e:icon') // true | ||
* ``` | ||
* | ||
* @param {String} n - notation | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
e : n => /^e:/.test(n) | ||
}; | ||
|
||
/** | ||
* Helpers to build parts of import-notation. | ||
* All parts concatenated by '' gives full import-notation string | ||
*/ | ||
const templates = { | ||
/** | ||
* Returns block-part of notation | ||
* | ||
* Example: | ||
* ```js | ||
* b('button') // 'b:button' | ||
* ``` | ||
* | ||
* @param {String} b - name of block | ||
* | ||
* @returns {String} | ||
*/ | ||
b : b => `b:${b}`, | ||
|
||
/** | ||
* Returns element-part of notation | ||
* | ||
* Example: | ||
* ```js | ||
* e('icon') // ' e:icon' | ||
* ``` | ||
* | ||
* @param {String} e - name of element | ||
* | ||
* @returns {String} | ||
*/ | ||
e : e => e ? ` e:${e}` : '', | ||
|
||
/** | ||
* Returns modifiers-part of notation | ||
* | ||
* Example: | ||
* ```js | ||
* m({ color: ['red', 'yellow'], theme: ['default'] }) | ||
* // ' m:color=red|yellow m:theme=default' | ||
* ``` | ||
* | ||
* @param {Object<String[]>} m - modifiers with values in arrays | ||
* | ||
* @returns {String} | ||
*/ | ||
m : m => Object.keys(m).map(name => `${templates._mn(name)}${templates._mv(m[name])}`).join(''), | ||
|
||
/** | ||
* Returns modifier-name-part of notation | ||
* | ||
* Example: | ||
* ```js | ||
* _mn('color') // ' m:color' | ||
* ``` | ||
* | ||
* @param {String} mn - name of modifier | ||
* | ||
* @returns {String} | ||
*/ | ||
_mn : mn => ` m:${mn}`, | ||
|
||
/** | ||
* Returns modifier-values-part of notation | ||
* | ||
* Example: | ||
* ```js | ||
* _mv(['red', 'yellow']) // '=red|yellow' | ||
* ``` | ||
* | ||
* @param {String[]} mv - modifier values in array | ||
* | ||
* @returns {String} | ||
*/ | ||
_mv : mv => mv.length ? `=${mv.join('|')}` : '', | ||
|
||
/** | ||
* Returns technology-part of notation | ||
* | ||
* Example: | ||
* ```js | ||
* t('js') // ' t:js' | ||
* ``` | ||
* | ||
* @param {String[]} t - name of technology | ||
* | ||
* @returns {String} | ||
*/ | ||
t : t => t ? ` t:${t}` : '' | ||
}; | ||
|
||
/** | ||
* BemCell variant of templates | ||
*/ | ||
const cellTemplates = Object.assign({}, templates, { | ||
/** | ||
* Returns modifiers-part of notation | ||
* | ||
* Example: | ||
* ```js | ||
* m({ name: 'theme', val: 'default' }) // ' m:theme=default' | ||
* ``` | ||
* | ||
* @param {{ name: String, val: String }} m - modifier with single value | ||
* | ||
* @returns {String} | ||
*/ | ||
m : m => m ? ` m:${m.name}=${m.val}` : '' | ||
}); | ||
|
||
/** | ||
* Returns import-notation stringify method for set of templates | ||
* | ||
* @param {Object} ts - helpers to build parts of import-notation | ||
* | ||
* @returns {String} | ||
*/ | ||
const stringifyMethod = ts => x => ['block', 'elem', 'mod', 'tech'].map(k => ts[k[0]](x[k])).join(''); | ||
|
||
/** | ||
* Returns import-notation filled up by context entity | ||
* | ||
* @param {String} n - notation | ||
* @param {BemEntity} scope - context entity | ||
* | ||
* @returns {String} | ||
*/ | ||
const fillup = (n, scope) => { | ||
if(tests.b(n)) { | ||
return n; | ||
} | ||
|
||
return templates.b(scope.block) + | ||
(tests.e(n) ? '' : templates.e(scope.elem)) + ' ' + n; | ||
}; | ||
|
||
module.exports = { | ||
fillup, | ||
stringifyMergedCells : stringifyMethod(templates), | ||
stringifyCell : stringifyMethod(cellTemplates) | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,9 @@ | |
"import" | ||
], | ||
"author": "Vasiliy Loginevskiy <[email protected]>", | ||
"files": [ | ||
"lib" | ||
], | ||
"license": "MPL-2.0", | ||
"bugs": { | ||
"url": "https://github.com/bem/bem-sdk/issues?q=label%3Apkg%3Aimport-notation" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
var expect = require('chai').expect, | ||
f = require('..').fillup; | ||
|
||
describe('scope', () => { | ||
describe('block', () => { | ||
it('should copy block if notation has no block #1', () => { | ||
expect(f('e:icon', { block : 'button' })).to.eql('b:button e:icon'); | ||
}); | ||
|
||
it('should copy block if notation has no block #2', () => { | ||
expect(f('m:theme=default', { block : 'button' })).to.eql('b:button m:theme=default'); | ||
}); | ||
|
||
it('should not copy block if notation has block', () => { | ||
expect(f('b:button e:icon', { block : 'input' })).to.eql('b:button e:icon'); | ||
}); | ||
}); | ||
|
||
describe('elem', () => { | ||
it('should copy elem if notation has no elem', () => { | ||
expect(f('m:theme=default', { block : 'button', elem : 'icon' })).to.eql('b:button e:icon m:theme=default'); | ||
}); | ||
|
||
it('should not copy elem if notation has elem', () => { | ||
expect(f('b:button e:icon', { block : 'input', elem : 'clear' })).to.eql('b:button e:icon'); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется, что
split
больше нигде не используется, можно на деструкторизацию заменить: