From c03d31b565a5eb1422f40bbda0eb227a90183aac Mon Sep 17 00:00:00 2001 From: Lisbon Date: Sat, 24 Mar 2018 13:25:15 +0300 Subject: [PATCH 01/13] format_tests --- lib/create-entity.js | 7 ++----- lib/create.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/create-entity.js b/lib/create-entity.js index b813721..904bd85 100644 --- a/lib/create-entity.js +++ b/lib/create-entity.js @@ -3,21 +3,18 @@ var fs = require('fs'), path = require('path'), stream = require('stream'), - bemNaming = require('@bem/naming'), Promise = require('pinkie-promise'), mkdirp = require('mkdirp'), createTree = require('./create-tree'), relativePath = function(to) { return path.relative(process.cwd(), to); }; -module.exports = function(entity, fileName, template, options) { +module.exports = function(fileName, content, options) { return new Promise(function(resolve, reject) { function onEnd(error) { error ? reject(error) : resolve(fileName); } - var isPiped = template instanceof stream.Readable, - content = (isPiped || typeof template === 'string') ? template : - template(entity, bemNaming(options.naming)), + var isPiped = content instanceof stream.Readable, isFile = isPiped || (typeof content !== 'object'); fs.exists(fileName, function(exists) { diff --git a/lib/create.js b/lib/create.js index ad4905a..916882b 100644 --- a/lib/create.js +++ b/lib/create.js @@ -10,7 +10,8 @@ var path = require('path'), createEntity = require('./create-entity'), getTemplate = require('./template'), uniq = require('uniq'), - Promise = require('pinkie-promise'); + Promise = require('pinkie-promise'), + stream = require('stream'); module.exports = function create(entities, levels, techs, options) { options || (options = {}); @@ -91,9 +92,15 @@ module.exports = function create(entities, levels, techs, options) { absPathToFile = path.join(path.resolve(level), pathToFile), template = options.fileContent || getTemplate(tech, levelOptions); - levelOptions.forceRewrite = options.forceRewrite; + if (options.forceRewrite) levelOptions.forceRewrite = options.forceRewrite; - return createEntity(entity, absPathToFile, template, levelOptions); + var isPipedR = template instanceof stream.Readable, + contentR = (isPipedR || typeof template === 'string') ? template : + template(entity, bemNaming(levelOptions.naming)); + + // console.log('absPathToFile', absPathToFile); + // console.log('contentR', contentR); + return createEntity(absPathToFile, contentR, levelOptions); })); }); })); From 754f1562ed3c85b34f86500b32486b3c45722fcf Mon Sep 17 00:00:00 2001 From: Alexandr Shleyko Date: Sat, 24 Mar 2018 15:53:39 +0300 Subject: [PATCH 02/13] wip --- package.json | 4 +++- test/test1.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 test/test1.js diff --git a/package.json b/package.json index edad692..3c7e99d 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,8 @@ "eslint": "^3.16.0", "eslint-config-pedant": "^0.8.0", "mocha": "^3.1.0", - "rimraf": "^2.5.4" + "proxyquire": "^2.0.1", + "rimraf": "^2.5.4", + "sinon": "^4.4.8" } } diff --git a/test/test1.js b/test/test1.js new file mode 100644 index 0000000..f124d47 --- /dev/null +++ b/test/test1.js @@ -0,0 +1,66 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const mkdirp = require('mkdirp'); +const rimraf = require('rimraf'); +const proxyquire = require('proxyquire'); +const naming = require('@bem/naming'); +const EOL = require('os').EOL; +const assert = require('assert'); +const stream = require('stream'); +const sinon = require('sinon'); +const spy = sinon.spy(); +const tmpDir = path.join(__dirname, '..'); + +const createEntity = function() { + console.log(arguments); + spy.apply(null, arguments); +} +const create = proxyquire('../lib/create', + { + './create-entity': createEntity + } +); + + + + +const templates = { + css: require('../lib/templates/css') +}; + +function testEntityHelper(entities, levels, techs, options, expected, done) { + const expectedOptions = {}; // TODO fix this + + create(entities, levels, techs, options).then(() => { + assert.equal(spy.callCount, entities.length); // вызвана столько-то раз + + expected.forEach( + file => assert(spy.calledWith(file.name, file.content, expectedOptions)) // вызывалась с expected параметрам + ); + + done(); + }); +} + +describe('bem-tools-create', () => { + describe('default scheme and default naming', () => { + it('should create a block using `nested` scheme and default naming', done => { + create({ block: 'b' }, ['/tmp'], ['css']).then(() => { + console.log(spy.calledOnce); + assert(spy.calledOnce); + done(); + }); + }); + }); + + describe('default scheme and default naming', () => { + it.only('should create a block using `nested` scheme and default naming', done => { + return testEntityHelper([{ block: 'b' }], ['./'], ['css'], {}, [{ + name: path.join(tmpDir, 'b', 'b.css'), + content: '.b {\n \n}\n' + }], done); + }); + }); +}); From 1dacafdb12116db304e0a442bceeeee33e93b8fd Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Sat, 24 Mar 2018 16:34:30 +0300 Subject: [PATCH 03/13] =?UTF-8?q?wip:=204=20=D1=82=D0=B5=D1=81=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/test1.js | 791 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 761 insertions(+), 30 deletions(-) diff --git a/test/test1.js b/test/test1.js index f124d47..acdb65e 100644 --- a/test/test1.js +++ b/test/test1.js @@ -10,34 +10,29 @@ const EOL = require('os').EOL; const assert = require('assert'); const stream = require('stream'); const sinon = require('sinon'); -const spy = sinon.spy(); const tmpDir = path.join(__dirname, '..'); -const createEntity = function() { - console.log(arguments); - spy.apply(null, arguments); -} -const create = proxyquire('../lib/create', - { - './create-entity': createEntity - } -); - - - - const templates = { - css: require('../lib/templates/css') + css: function(entity, namingScheme) { + const className = typeof entity === 'string' ? entity : naming(namingScheme).stringify(entity); + + return [ + '.' + className + ' {', + ' ', + '}', + '' + ].join(EOL); + } }; function testEntityHelper(entities, levels, techs, options, expected, done) { - const expectedOptions = {}; // TODO fix this - + const spy = sinon.spy(); + const create = proxyquire('../lib/create', { './create-entity': spy }); create(entities, levels, techs, options).then(() => { - assert.equal(spy.callCount, entities.length); // вызвана столько-то раз + assert.equal(spy.callCount, entities.length); - expected.forEach( - file => assert(spy.calledWith(file.name, file.content, expectedOptions)) // вызывалась с expected параметрам + expected.forEach(file => + assert(spy.calledWith(file.name, file.content, file.options)) ); done(); @@ -45,22 +40,758 @@ function testEntityHelper(entities, levels, techs, options, expected, done) { } describe('bem-tools-create', () => { - describe('default scheme and default naming', () => { + describe.only('default scheme and default naming', () => { it('should create a block using `nested` scheme and default naming', done => { - create({ block: 'b' }, ['/tmp'], ['css']).then(() => { - console.log(spy.calledOnce); - assert(spy.calledOnce); - done(); + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], {}, [{ + name: path.join(tmpDir, 'b', 'b.css'), + content: templates.css('b'), + options: {} + }], done); + }); + + it('should create an element using `nested` scheme and default naming', done => { + return testEntityHelper([{ block: 'b', elem: 'e' }], [tmpDir], ['css'], {}, [{ + name: path.join(tmpDir, 'b', '__e', 'b__e.css'), + content: templates.css('b__e'), + options: {} + }], done); + }); + + it('should create an block modifier using `nested` scheme and default naming', done => { + return testEntityHelper([{ block: 'b', modName: 'm', modVal: 'v' }], [tmpDir], ['css'], {}, [{ + name: path.join(tmpDir, 'b', '_m', 'b_m_v.css'), + content: templates.css('b_m_v'), + options: {} + }], done); + }); + + it('should create an element modifier using `nested` scheme and default naming', done => { + return testEntityHelper([{ block: 'b', elem: 'e', modName: 'em', modVal: 'ev' }], [tmpDir], ['css'], {}, [{ + name: path.join(tmpDir, 'b', '__e', '_em', 'b__e_em_ev.css'), + content: templates.css('b__e_em_ev'), + options: {} + }], done); + }); + + it('should create a block with different techs', done => { + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css', 'deps.js'], {}, [ + { + name: path.join(tmpDir, 'b', 'b.css'), + content: templates.css('b'), + options: {} + }, + { + name: path.join(tmpDir, 'b', 'b.deps.js'), + content: ['({', ' shouldDeps: [', ' ', ' ]', '})', ''].join(EOL), + options: {} + } + ], done); + }); + }); + + describe('custom options', () => { + it('should create entities with naming from config', () => { + const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; + const namingScheme = { + delims: { + elem: '-', + mod: { name: '--', val: '_' } + } + }; + + return testEntityHelper([entity], [tmpDir], ['css'], { defaults: { naming: namingScheme } }, [{ + name: path.join(tmpDir, 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), + content: templates.css(entity, namingScheme) + }]); + }); + + it('should create blocks with scheme from config', () => { + const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; + + return testEntityHelper([entity], [tmpDir], ['css'], { defaults: { scheme: 'flat' } }, [{ + name: path.join(tmpDir, 'b__e1_m1_v1.css'), + content: templates.css(entity) + }]); + }); + + describe('levels', () => { + it('should create a block on default levels from config', () => { + const opts = { + defaults: { levels: {} }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + ['level1', 'level2'].forEach(function(lvl) { + const level = path.join(tmpDir, lvl); + opts.defaults.levels[level] = { 'default': true }; + }); + + return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ + { + name: path.join(tmpDir, 'level1', 'b', 'b.css'), + content: templates.css('b') + }, + { + name: path.join(tmpDir, 'level2', 'b', 'b.css'), + content: templates.css('b') + } + ]); + }); + + it('should create entities on levels with provided config', () => { + const levels = [path.join(tmpDir, 'l1'), path.join(tmpDir, 'l2')]; + const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; + const namingScheme = { + delims: { + elem: '-', + mod: { name: '--', val: '_' } + } + }; + const opts = { + defaults: { + levels: {} + } + }; + + opts.defaults.levels[levels[0]] = { + naming: namingScheme + }; + + opts.defaults.levels[levels[1]] = { + scheme: 'flat' + }; + + return testEntityHelper([entity], levels, ['css'], opts, [ + { + name: path.join(tmpDir, 'l1', 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), + content: templates.css(entity, namingScheme) + }, + { + name: path.join(tmpDir, 'l2', 'b__e1_m1_v1.css'), + content: templates.css(entity) + } + ]); + }); + + it('should bubble to parent level when cwd is inside an entity', () => { + const opts = { + defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + ['level1', 'level2'].forEach(function(lvl) { + const level = path.join(tmpDir, lvl); + opts.defaults.levels[level] = { 'default': lvl === 'level2' }; + }); + + const fakeCwd = path.join(tmpDir, 'level1', 'b1', '__e1'); + mkdirp.sync(fakeCwd); + process.chdir(fakeCwd); + + return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ + { + name: path.join(tmpDir, 'level1', 'b', 'b.css'), + content: templates.css('b') + } + ]); + }); + + it('should create an entity on default level when cwd is not inside a level folder', () => { + const opts = { + defaults: { levels: {} }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + ['level1', 'level2'].forEach(function(lvl) { + const level = path.join(tmpDir, lvl); + opts.defaults.levels[level] = { 'default': true }; + }); + + const fakeCwd = path.join(tmpDir, 'some-folder', 'cwd'); + mkdirp.sync(fakeCwd); + process.chdir(fakeCwd); + + return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ + { + name: path.join(tmpDir, 'level1', 'b', 'b.css'), + content: templates.css('b') + }, + { + name: path.join(tmpDir, 'level2', 'b', 'b.css'), + content: templates.css('b') + } + ]); + }); + + it('should create an entity on provided not default level when cwd is not inside a level folder', () => { + const opts = { + defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + ['level1', 'level2'].forEach(function(lvl) { + const level = path.join(tmpDir, lvl); + opts.defaults.levels[level] = { 'default': lvl === 'level1' }; + }); + + const fakeCwd = path.join(tmpDir, 'some-folder', 'cwd'); + mkdirp.sync(fakeCwd); + process.chdir(fakeCwd); + + return testEntityHelper([{ block: 'b' }], 'level2', ['css'], opts, [ + { + name: path.join(tmpDir, 'level2', 'b', 'b.css'), + content: templates.css('b') + } + ]); + }); + + it('should create a block on cwd as a fallback', () => { + const fakeCwd = path.join(tmpDir, 'cwd'); + mkdirp.sync(fakeCwd); + process.chdir(fakeCwd); + + return testEntityHelper([{ block: 'b' }], null, ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ + name: path.join(fakeCwd, 'b', 'b.css'), + content: templates.css('b') + }]); + }); + + it('should create block on provided levels', () => { + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ + name: path.join(tmpDir, 'b', 'b.css'), + content: templates.css('b') + }]); + }); + + describe('level config in plugin config', () => { + it('should respect level techs', () => { + const createLevels = {}; + const opts = { + defaults: { + levels: {}, + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['common-create-tech1', 'common-create-tech2'], + levels: createLevels + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + const level = path.join(tmpDir, 'level1'); + opts.defaults.levels[level] = { 'default': true }; + + createLevels[level] = { + techs: ['create-level-tech1'] + }; + + return testEntityHelper([{ block: 'b' }], null, ['tech1', 'tech2'], opts, [ + { name: path.join(level, 'b', 'b.tech1') }, + { name: path.join(level, 'b', 'b.tech2') }, + { name: path.join(level, 'b', 'b.create-level-tech1') } + ]); + }); + + it('should get default level from plugin config', () => { + const createLevels = {}; + const opts = { + defaults: { + levels: {}, + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['common-create-tech1', 'common-create-tech2'], + levels: createLevels + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + const level = path.join(tmpDir, 'level1'); + + createLevels[level] = { + techs: ['create-level-tech1'], + 'default': true + }; + + return testEntityHelper([{ block: 'b' }], null, ['tech1', 'tech2'], opts, [ + { name: path.join(level, 'b', 'b.tech1') }, + { name: path.join(level, 'b', 'b.tech2') }, + { name: path.join(level, 'b', 'b.create-level-tech1') } + ]); + }); + + it('should respect level templates', () => { + const createLevels = {}; + const opts = { + defaults: { + levels: {}, + modules: { + 'bem-tools': { + plugins: { + create: { + templates: { css: path.join(__dirname, 'tech-templates', 'css') }, + levels: createLevels + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + const level = path.join(tmpDir, 'level1'); + + createLevels[level] = { + templates: { css: path.join(__dirname, 'tech-templates', 'css2') }, + 'default': true + }; + + return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ + { + name: path.join(level, 'b', 'b.css'), + content: '.b {\n}\n' + } + ]); + }); + + it('should support glob with absolute level path', () => { + const createPluginLevels = {}; + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['tech1', 'tech2'], + levels: createPluginLevels + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + const level = path.join(tmpDir, '*.blocks'); + createPluginLevels[level] = { + techs: ['tech4', 'tech3'], + 'default': true + }; + + mkdirp.sync(path.join(tmpDir, 'common.blocks')); + mkdirp.sync(path.join(tmpDir, 'desktop.blocks')); + + return testEntityHelper([{ block: 'b' }], null, null, opts, [ + { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3') }, + { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4') }, + { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3') }, + { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4') } + ]); + }); + + it('should support glob resolution for levels', () => { + const levels = {}; + const createPluginLevels = {}; + const opts = { + defaults: { + levels, + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['tech1', 'tech2'], + levels: createPluginLevels + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + const level = '*.blocks'; + levels[level] = { 'default': true }; + createPluginLevels[level] = { techs: ['tech4', 'tech3'] }; + + mkdirp.sync(path.join(tmpDir, 'common.blocks')); + mkdirp.sync(path.join(tmpDir, 'desktop.blocks')); + process.chdir(tmpDir); + + return testEntityHelper([{ block: 'b' }], null, null, opts, [ + { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3') }, + { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4') }, + { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3') }, + { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4') } + ]); + }); + }); + }); + + describe('techs', () => { + it('should create block in techs from config', () => { + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['tech1', 'tech2'] + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper([{ block: 'b' }], [tmpDir], null, opts, [ + { name: path.join(tmpDir, 'b', 'b.tech1') }, + { name: path.join(tmpDir, 'b', 'b.tech2') } + ]); + }); + + it('should create block in techs from config and provided techs', () => { + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['tech1', 'tech2'] + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech3', 'tech4'], opts, [ + { name: path.join(tmpDir, 'b', 'b.tech1') }, + { name: path.join(tmpDir, 'b', 'b.tech2') }, + { name: path.join(tmpDir, 'b', 'b.tech3') }, + { name: path.join(tmpDir, 'b', 'b.tech4') } + ]); + }); + + // TODO: check that it fires only twice instead of four times + it('should create block in techs from config and the same provided techs', () => { + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['tech1', 'tech2'] + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech1', 'tech2'], opts, [ + { name: path.join(tmpDir, 'b', 'b.tech1') }, + { name: path.join(tmpDir, 'b', 'b.tech2') } + ]); + }); + + it('should create block only in provided techs', () => { + const opts = { + onlyTech: ['only1', 'only2'], + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['defTech1', 'defTech2'] + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech1', 'tech2'], opts, [ + { name: path.join(tmpDir, 'b', 'b.only1') }, + { name: path.join(tmpDir, 'b', 'b.only2') } + ]); + }); + }); + + describe('template', () => { + it('should create a block using templates from config', () => { + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + templates: { + css: path.join(__dirname, 'tech-templates', 'css') + } + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], opts, [{ + name: path.join(tmpDir, 'b', 'b.css'), + content: '.b { }' + }]); + }); + + it('should create a block using template ID', () => { + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techsTemplates: { + 'bemtree.js': 'bemhtml.js' + } + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper([{ block: 'b' }], [tmpDir], ['bemtree.js'], opts, [{ + name: path.join(tmpDir, 'b', 'b.bemtree.js'), + content: [ + "block('b').content()(function() {", + " return;", + "});", + "" + ].join(EOL) + }]); }); }); }); - describe('default scheme and default naming', () => { - it.only('should create a block using `nested` scheme and default naming', done => { - return testEntityHelper([{ block: 'b' }], ['./'], ['css'], {}, [{ + describe('string parsing', () => { + describe('entity parsing', () => { + it('should parse block from string with techs from args', () => { + return testEntityHelper('b1', tmpDir, ['t1', 't2'], {}, [ + { name: path.join(tmpDir, 'b1', 'b1.t1') }, + { name: path.join(tmpDir, 'b1', 'b1.t2') } + ]); + }); + + it('should parse block from string with techs from config', () => { + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['tech1', 'tech2'] + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper('b1', tmpDir, null, opts, [ + { name: path.join(tmpDir, 'b1', 'b1.tech1') }, + { name: path.join(tmpDir, 'b1', 'b1.tech2') } + ]); + }); + + it('should parse block with a tech from a string and ignore techs from config', () => { + const opts = { + defaults: { + modules: { + 'bem-tools': { + plugins: { + create: { + techs: ['tech1', 'tech2'] + } + } + } + } + }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + return testEntityHelper('b1.css', tmpDir, ['argTech'], opts, [ + { + name: path.join(tmpDir, 'b1', 'b1.css'), + content: templates.css('b1') + }, + { name: path.join(tmpDir, 'b1', 'b1.argTech') } + ]); + }); + + it('should parse elem from string', () => { + return testEntityHelper('b1__e1', tmpDir, ['t1'], {}, [ + { name: path.join(tmpDir, 'b1', '__e1', 'b1__e1.t1') } + ]); + }); + + it('should parse block mod from string', () => { + return testEntityHelper('b1_m1', tmpDir, ['t1'], {}, [ + { name: path.join(tmpDir, 'b1', '_m1', 'b1_m1.t1') } + ]); + }); + + it('should parse block modVal from string', () => { + return testEntityHelper('b1_m1_v1', tmpDir, ['t1'], {}, [ + { name: path.join(tmpDir, 'b1', '_m1', 'b1_m1_v1.t1') } + ]); + }); + + it('should parse elem mod from string', () => { + return testEntityHelper('b1__e1_m1_v1', tmpDir, ['t1'], {}, [ + { name: path.join(tmpDir, 'b1', '__e1', '_m1', 'b1__e1_m1_v1.t1') } + ]); + }); + }); + + describe('levels from string', () => { + it('should get level from string', () => { + return testEntityHelper(tmpDir + '/level1/b1.t1', null, null, {}, [ + { name: path.join(tmpDir, 'level1', 'b1', 'b1.t1') } + ]); + }); + + it('should resolve level from string by config', () => { + const opts = { + defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, + fsRoot: tmpDir, + fsHome: tmpDir + }; + + ['level1', 'level2'].forEach(function(lvl) { + const level = path.join(tmpDir, lvl); + opts.defaults.levels[level] = { 'default': lvl === 'level1' }; + }); + + const fakeCwd = path.join(tmpDir, 'some-folder', 'cwd'); + mkdirp.sync(fakeCwd); + process.chdir(fakeCwd); + + return testEntityHelper(tmpDir + '/level1/b1.t1', null, null, opts, [ + { + name: path.join(tmpDir, 'level1', 'b1', 'b1.t1') + } + ]); + }); + }); + + it('should expand braces', () => { + return testEntityHelper('{b1,b2}.{t1,t2}', tmpDir, null, {}, [ + { name: path.join(tmpDir, 'b1', 'b1.t1') }, + { name: path.join(tmpDir, 'b1', 'b1.t2') }, + { name: path.join(tmpDir, 'b2', 'b2.t1') }, + { name: path.join(tmpDir, 'b2', 'b2.t2') } + ]); + }); + }); + + describe('respect context', () => { + it.skip('should get block from context', () => { + + }); + + it.skip('should get block and elem from context', () => { + + }); + + it.skip('should get modName from context', () => { + + }); + + // modVal if cwd is inside mod + }); + + describe('command line arguments support', () => { + it('should exclude tech', () => { + const excludedTechs = ['css', 'js']; + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css', 'js', 't1'], + { excludeTech: excludedTechs }, [{ + name: path.join(tmpDir, 'b', 'b.t1') + }] + ); + }); + + it('should support custom content', () => { + const content = 'Some testing content'; + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], + { fileContent: content }, [{ + name: path.join(tmpDir, 'b', 'b.css'), + content: content + }] + ); + }); + + it('should force rewrite', () => { + const content = 'Some testing content'; + // run first time + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ name: path.join(tmpDir, 'b', 'b.css'), - content: '.b {\n \n}\n' - }], done); + content: templates.css('b') + }]) + // run second time with force and another content + .then(() => testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], + { fileContent: content, forceRewrite: true }, [{ + name: path.join(tmpDir, 'b', 'b.css'), + content: content + }]) + ); }); + + it('should support custom content with pipe', () => { + const content = 'Some piped testing content'; + const srcStream = new stream.Readable(); + srcStream.push(content); + srcStream.push(null); + + return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], + { fileContent: srcStream }, [{ + name: path.join(tmpDir, 'b', 'b.css'), + content: content + }] + ); + }); + }); }); From 6a1ac7fde58117b261d296c67ed2e8805b82afae Mon Sep 17 00:00:00 2001 From: Alexandr Shleyko Date: Sat, 24 Mar 2018 17:33:37 +0300 Subject: [PATCH 04/13] create sync version --- lib/create.js | 151 ++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 78 deletions(-) diff --git a/lib/create.js b/lib/create.js index 916882b..5947a62 100644 --- a/lib/create.js +++ b/lib/create.js @@ -17,101 +17,96 @@ module.exports = function create(entities, levels, techs, options) { options || (options = {}); techs || (techs = []); var baseConfig = bemConfig(options), - cwd = path.resolve(options.cwd || ''); + cwd = path.resolve(options.cwd || ''), - return baseConfig.module('bem-tools') - .then(function(bemToolsConf) { - var pluginConf = bemToolsConf && bemToolsConf.plugins && bemToolsConf.plugins.create || {}; + bemToolsConf = baseConfig.moduleSync('bem-tools'), - return bemConfig(Object.assign({}, options, { extendBy: pluginConf })); - }).then(function(config) { - if (!levels || !levels.length) { - var levelsMap = config.levelMapSync(), - levelList = Object.keys(levelsMap); + pluginConf = bemToolsConf && bemToolsConf.plugins && bemToolsConf.plugins.create || {}, - var defaultLevels = levelList.filter(function(level) { - return levelsMap[level].default; - }); + config = bemConfig(Object.assign({}, options, { extendBy: pluginConf })); - var levelByCwd = levelList.filter(function(level) { - return cwd.indexOf(level) === 0; - }).sort().reverse()[0]; + if (!levels || !levels.length) { + var levelsMap = config.levelMapSync(), + levelList = Object.keys(levelsMap); - levels = levelByCwd || (defaultLevels.length ? defaultLevels : cwd); - } + var defaultLevels = levelList.filter(function (level) { + return levelsMap[level].default; + }); + + var levelByCwd = levelList.filter(function (level) { + return cwd.indexOf(level) === 0; + }).sort().reverse()[0]; + + levels = levelByCwd || (defaultLevels.length ? defaultLevels : cwd); + } + + Array.isArray(entities) || (entities = [entities]); + Array.isArray(levels) || (levels = [levels]); - Array.isArray(entities) || (entities = [entities]); - Array.isArray(levels) || (levels = [levels]); + return flatten(entities.map(function (input) { + var isFileGlob = typeof input === 'string'; - return Promise.all(entities.map(function(input) { - var isFileGlob = typeof input === 'string'; + return (isFileGlob ? braceExpansion(input) : [input]).map(function (filepathOrInput) { + var currentLevels = levels; - return Promise.all((isFileGlob ? braceExpansion(input) : [input]).map(function(filepathOrInput) { - var currentLevels = levels; + if (typeof filepathOrInput === 'string') { + var currentLevel = path.dirname(filepathOrInput); + currentLevel !== '.' && (currentLevels = [currentLevel]); + } - if (typeof filepathOrInput === 'string') { - var currentLevel = path.dirname(filepathOrInput); - currentLevel !== '.' && (currentLevels = [currentLevel]); + return currentLevels.map(function (relLevel) { + var rootDir = config.rootSync() || cwd, + level = path.resolve(rootDir, relLevel), + levelOptions = config.levelSync(level) || {}, + levelScheme = levelOptions.scheme, + buildPath = scheme(levelScheme).path, + currentTechs = uniq([].concat(levelOptions.techs || [], techs)), + entity; + + if (isFileGlob) { + var file = path.basename(filepathOrInput), + // split for entity key and tech (by first dot) + match = file.match(/^([^.]+)(?:\.(.+))?$/); + + entity = bemNaming(levelOptions.naming).parse(match[1]); + if (match[2]) { + currentTechs = uniq(techs.concat(match[2])); } + } else { + entity = BemEntityName.create(filepathOrInput); + } - return Promise.all(currentLevels.map(function(relLevel) { - var rootDir = config.rootSync() || cwd, - level = path.resolve(rootDir, relLevel); - - return config.level(level).then(function(levelOptions) { - levelOptions || (levelOptions = {}); - - var levelScheme = levelOptions.scheme, - buildPath = scheme(levelScheme).path, - currentTechs = uniq([].concat(levelOptions.techs || [], techs)), - entity; - - if (isFileGlob) { - var file = path.basename(filepathOrInput), - // split for entity key and tech (by first dot) - match = file.match(/^([^.]+)(?:\.(.+))?$/); - - entity = bemNaming(levelOptions.naming).parse(match[1]); - if (match[2]) { - currentTechs = uniq(techs.concat(match[2])); - } - } else { - entity = BemEntityName.create(filepathOrInput); - } - - options.onlyTech && (currentTechs = options.onlyTech); - - options.excludeTech && (currentTechs = currentTechs.filter(function(tech) { - return options.excludeTech.indexOf(tech) === -1; - })); - - return Promise.all(currentTechs.map(function(tech) { - var pathToFile = buildPath( - new BemCell({ entity: entity, tech: tech }), - levelOptions.schemeOptions || levelOptions), - absPathToFile = path.join(path.resolve(level), pathToFile), - template = options.fileContent || getTemplate(tech, levelOptions); - - if (options.forceRewrite) levelOptions.forceRewrite = options.forceRewrite; - - var isPipedR = template instanceof stream.Readable, - contentR = (isPipedR || typeof template === 'string') ? template : - template(entity, bemNaming(levelOptions.naming)); - - // console.log('absPathToFile', absPathToFile); - // console.log('contentR', contentR); - return createEntity(absPathToFile, contentR, levelOptions); - })); - }); - })); + options.onlyTech && (currentTechs = options.onlyTech); + options.excludeTech && (currentTechs = currentTechs.filter(function (tech) { + return options.excludeTech.indexOf(tech) === -1; })); - })).then(flatten); + + return currentTechs.map(function (tech) { + var pathToFile = buildPath( + new BemCell({ entity: entity, tech: tech }), + levelOptions.schemeOptions || levelOptions), + absPathToFile = path.join(path.resolve(level), pathToFile), + template = options.fileContent || getTemplate(tech, levelOptions); + + if (options.forceRewrite) levelOptions.forceRewrite = options.forceRewrite; + + var contentR = typeof template === 'string' ? + template : template(entity, bemNaming(levelOptions.naming)); + + return { + path: absPathToFile, + content: contentR, + options: levelOptions + }; + }); + }); }); + })); }; function flatten(arr) { - return arr.reduce(function(acc, item) { + return arr.reduce(function (acc, item) { return acc.concat(Array.isArray(item) ? flatten(item) : item); }, []); } From e39b7474487fe87eff7f646a028b273007865113 Mon Sep 17 00:00:00 2001 From: Lisbon Date: Sat, 24 Mar 2018 18:00:07 +0300 Subject: [PATCH 05/13] create new async create --- lib/create.js | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/create.js b/lib/create.js index 5947a62..7e46e8e 100644 --- a/lib/create.js +++ b/lib/create.js @@ -13,7 +13,8 @@ var path = require('path'), Promise = require('pinkie-promise'), stream = require('stream'); -module.exports = function create(entities, levels, techs, options) { +//TODO: переименовать:) +function prepareEntityData(entities, levels, techs, options) { options || (options = {}); techs || (techs = []); var baseConfig = bemConfig(options), @@ -86,18 +87,18 @@ module.exports = function create(entities, levels, techs, options) { var pathToFile = buildPath( new BemCell({ entity: entity, tech: tech }), levelOptions.schemeOptions || levelOptions), - absPathToFile = path.join(path.resolve(level), pathToFile), - template = options.fileContent || getTemplate(tech, levelOptions); - - if (options.forceRewrite) levelOptions.forceRewrite = options.forceRewrite; + absPathToFile = path.join(path.resolve(level), pathToFile); - var contentR = typeof template === 'string' ? - template : template(entity, bemNaming(levelOptions.naming)); + if (options.forceRewrite) { + levelOptions.forceRewrite = options.forceRewrite; + } return { path: absPathToFile, - content: contentR, - options: levelOptions + levelOptions: levelOptions, + tech: tech, + options: options, + entity: entity }; }); }); @@ -110,3 +111,15 @@ function flatten(arr) { return acc.concat(Array.isArray(item) ? flatten(item) : item); }, []); } + +module.exports = function create(entities, levels, techs, options) { + var createRes = prepareEntityData(entities, levels, techs, options); + + return Promise.all(createRes.map(function(item) { + var template = item.options.fileContent || getTemplate(item.tech, item.levelOptions), + content = typeof template === 'string' ? + template : template(item.entity, bemNaming(item.levelOptions.naming)); + + return createEntity(item.path, content, item.levelOptions); + })); +}; From a295578c49a9f065a6be260eaec1106d9dea0c17 Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Sat, 24 Mar 2018 18:07:31 +0300 Subject: [PATCH 06/13] =?UTF-8?q?wip:=2022=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/test1.js | 338 +++++++++++++++++++++++++++++--------------------- 1 file changed, 199 insertions(+), 139 deletions(-) diff --git a/test/test1.js b/test/test1.js index acdb65e..fb4675f 100644 --- a/test/test1.js +++ b/test/test1.js @@ -10,7 +10,7 @@ const EOL = require('os').EOL; const assert = require('assert'); const stream = require('stream'); const sinon = require('sinon'); -const tmpDir = path.join(__dirname, '..'); +const tmpDir = process.env.PWD; const templates = { css: function(entity, namingScheme) { @@ -27,12 +27,17 @@ const templates = { function testEntityHelper(entities, levels, techs, options, expected, done) { const spy = sinon.spy(); - const create = proxyquire('../lib/create', { './create-entity': spy }); + const create = proxyquire('../lib/create', { + './create-entity': function(name, content, opts) { + console.log('name, content, opts', name, content, opts); + spy.apply(null, arguments); + } + }); create(entities, levels, techs, options).then(() => { - assert.equal(spy.callCount, entities.length); + assert.equal(spy.callCount, expected.length); expected.forEach(file => - assert(spy.calledWith(file.name, file.content, file.options)) + assert(spy.calledWithMatch(file.name, file.content)) ); done(); @@ -40,7 +45,7 @@ function testEntityHelper(entities, levels, techs, options, expected, done) { } describe('bem-tools-create', () => { - describe.only('default scheme and default naming', () => { + describe('default scheme and default naming', () => { it('should create a block using `nested` scheme and default naming', done => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], {}, [{ name: path.join(tmpDir, 'b', 'b.css'), @@ -90,7 +95,7 @@ describe('bem-tools-create', () => { }); describe('custom options', () => { - it('should create entities with naming from config', () => { + it('should create entities with naming from config', done => { const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; const namingScheme = { delims: { @@ -101,21 +106,23 @@ describe('bem-tools-create', () => { return testEntityHelper([entity], [tmpDir], ['css'], { defaults: { naming: namingScheme } }, [{ name: path.join(tmpDir, 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), - content: templates.css(entity, namingScheme) - }]); + content: templates.css(entity, namingScheme), + options: { naming: namingScheme } + }], done); }); - it('should create blocks with scheme from config', () => { + it('should create blocks with scheme from config', done => { const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; return testEntityHelper([entity], [tmpDir], ['css'], { defaults: { scheme: 'flat' } }, [{ name: path.join(tmpDir, 'b__e1_m1_v1.css'), - content: templates.css(entity) - }]); + content: templates.css(entity), + options: { scheme: 'flat' } + }], done); }); describe('levels', () => { - it('should create a block on default levels from config', () => { + it('should create a block on default levels from config', done => { const opts = { defaults: { levels: {} }, fsRoot: tmpDir, @@ -130,16 +137,18 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ { name: path.join(tmpDir, 'level1', 'b', 'b.css'), - content: templates.css('b') + content: templates.css('b'), + options: { default: true } }, { name: path.join(tmpDir, 'level2', 'b', 'b.css'), - content: templates.css('b') + content: templates.css('b'), + options: { default: true } } - ]); + ], done); }); - it('should create entities on levels with provided config', () => { + it('should create entities on levels with provided config', done => { const levels = [path.join(tmpDir, 'l1'), path.join(tmpDir, 'l2')]; const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; const namingScheme = { @@ -165,16 +174,18 @@ describe('bem-tools-create', () => { return testEntityHelper([entity], levels, ['css'], opts, [ { name: path.join(tmpDir, 'l1', 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), - content: templates.css(entity, namingScheme) + content: templates.css(entity, namingScheme), + options: { naming: namingScheme } }, { name: path.join(tmpDir, 'l2', 'b__e1_m1_v1.css'), - content: templates.css(entity) + content: templates.css(entity), + options: { scheme: 'flat' } } - ]); + ], done); }); - it('should bubble to parent level when cwd is inside an entity', () => { + it('should bubble to parent level when cwd is inside an entity', done => { const opts = { defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, fsRoot: tmpDir, @@ -193,12 +204,13 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ { name: path.join(tmpDir, 'level1', 'b', 'b.css'), - content: templates.css('b') + content: templates.css('b'), + options: { default: false } } - ]); + ], done); }); - it('should create an entity on default level when cwd is not inside a level folder', () => { + it('should create an entity on default level when cwd is not inside a level folder', done => { const opts = { defaults: { levels: {} }, fsRoot: tmpDir, @@ -217,16 +229,18 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ { name: path.join(tmpDir, 'level1', 'b', 'b.css'), - content: templates.css('b') + content: templates.css('b'), + options: { default: true } }, { name: path.join(tmpDir, 'level2', 'b', 'b.css'), - content: templates.css('b') + content: templates.css('b'), + options: { default: true } } - ]); + ], done); }); - it('should create an entity on provided not default level when cwd is not inside a level folder', () => { + it('should create an entity on provided not default level when cwd is not inside a level folder', done => { const opts = { defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, fsRoot: tmpDir, @@ -245,31 +259,34 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], 'level2', ['css'], opts, [ { name: path.join(tmpDir, 'level2', 'b', 'b.css'), - content: templates.css('b') + content: templates.css('b'), + options: { default: false } } - ]); + ], done); }); - it('should create a block on cwd as a fallback', () => { + it('should create a block on cwd as a fallback', done => { const fakeCwd = path.join(tmpDir, 'cwd'); mkdirp.sync(fakeCwd); process.chdir(fakeCwd); return testEntityHelper([{ block: 'b' }], null, ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ name: path.join(fakeCwd, 'b', 'b.css'), - content: templates.css('b') - }]); + content: templates.css('b'), + options: {} + }], done); }); - it('should create block on provided levels', () => { + it('should create block on provided levels', done => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ name: path.join(tmpDir, 'b', 'b.css'), - content: templates.css('b') - }]); + content: templates.css('b'), + options: {} + }], done); }); describe('level config in plugin config', () => { - it('should respect level techs', () => { + it('should respect level techs', done => { const createLevels = {}; const opts = { defaults: { @@ -297,13 +314,37 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], null, ['tech1', 'tech2'], opts, [ - { name: path.join(level, 'b', 'b.tech1') }, - { name: path.join(level, 'b', 'b.tech2') }, - { name: path.join(level, 'b', 'b.create-level-tech1') } - ]); + { + name: path.join(level, 'b', 'b.tech1'), + content: '', + options: { + modules: opts.defaults.modules, + techs: ['create-level-tech1'], + default: true + } + }, + { + name: path.join(level, 'b', 'b.tech2'), + content: '', + options: { + modules: opts.defaults.modules, + techs: ['create-level-tech1'], + default: true + } + }, + { + name: path.join(level, 'b', 'b.create-level-tech1'), + content: '', + options: { + modules: opts.defaults.modules, + techs: ['create-level-tech1'], + default: true + } + } + ], done); }); - it('should get default level from plugin config', () => { + it('should get default level from plugin config', done => { const createLevels = {}; const opts = { defaults: { @@ -331,13 +372,40 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], null, ['tech1', 'tech2'], opts, [ - { name: path.join(level, 'b', 'b.tech1') }, - { name: path.join(level, 'b', 'b.tech2') }, - { name: path.join(level, 'b', 'b.create-level-tech1') } - ]); + { + name: path.join(level, 'b', 'b.tech1'), + content: '', + options: { + modules: opts.defaults.modules, + techs: ['create-level-tech1'], + default: true + } + + }, + { + name: path.join(level, 'b', 'b.tech2'), + content: '', + options: { + modules: opts.defaults.modules, + techs: ['create-level-tech1'], + default: true + } + + }, + { + name: path.join(level, 'b', 'b.create-level-tech1'), + content: '', + options: { + modules: opts.defaults.modules, + techs: ['create-level-tech1'], + default: true + } + + } + ], done); }); - it('should respect level templates', () => { + it('should respect level templates', done => { const createLevels = {}; const opts = { defaults: { @@ -369,10 +437,10 @@ describe('bem-tools-create', () => { name: path.join(level, 'b', 'b.css'), content: '.b {\n}\n' } - ]); + ], done); }); - it('should support glob with absolute level path', () => { + it('should support glob with absolute level path', done => { const createPluginLevels = {}; const opts = { defaults: { @@ -401,14 +469,26 @@ describe('bem-tools-create', () => { mkdirp.sync(path.join(tmpDir, 'desktop.blocks')); return testEntityHelper([{ block: 'b' }], null, null, opts, [ - { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3') }, - { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4') }, - { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3') }, - { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4') } - ]); + { + name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3'), + content: '' + }, + { + name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4'), + content: '' + }, + { + name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3'), + content: '' + }, + { + name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4'), + content: '' + } + ], done); }); - it('should support glob resolution for levels', () => { + it('should support glob resolution for levels', done => { const levels = {}; const createPluginLevels = {}; const opts = { @@ -438,17 +518,29 @@ describe('bem-tools-create', () => { process.chdir(tmpDir); return testEntityHelper([{ block: 'b' }], null, null, opts, [ - { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3') }, - { name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4') }, - { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3') }, - { name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4') } - ]); + { + name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3'), + content: '' + }, + { + name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4'), + content: '' + }, + { + name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3'), + content: '' + }, + { + name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4'), + content: '' + } + ], done); }); }); }); describe('techs', () => { - it('should create block in techs from config', () => { + it('should create block in techs from config', done => { const opts = { defaults: { modules: { @@ -466,12 +558,18 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], [tmpDir], null, opts, [ - { name: path.join(tmpDir, 'b', 'b.tech1') }, - { name: path.join(tmpDir, 'b', 'b.tech2') } - ]); + { + name: path.join(tmpDir, 'b', 'b.tech1'), + content: '' + }, + { + name: path.join(tmpDir, 'b', 'b.tech2'), + content: '' + } + ], done); }); - it('should create block in techs from config and provided techs', () => { + it('should create block in techs from config and provided techs', done => { const opts = { defaults: { modules: { @@ -493,11 +591,11 @@ describe('bem-tools-create', () => { { name: path.join(tmpDir, 'b', 'b.tech2') }, { name: path.join(tmpDir, 'b', 'b.tech3') }, { name: path.join(tmpDir, 'b', 'b.tech4') } - ]); + ], done); }); // TODO: check that it fires only twice instead of four times - it('should create block in techs from config and the same provided techs', () => { + it('should create block in techs from config and the same provided techs', done => { const opts = { defaults: { modules: { @@ -517,10 +615,10 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech1', 'tech2'], opts, [ { name: path.join(tmpDir, 'b', 'b.tech1') }, { name: path.join(tmpDir, 'b', 'b.tech2') } - ]); + ], done); }); - it('should create block only in provided techs', () => { + it('should create block only in provided techs', done => { const opts = { onlyTech: ['only1', 'only2'], defaults: { @@ -541,12 +639,12 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech1', 'tech2'], opts, [ { name: path.join(tmpDir, 'b', 'b.only1') }, { name: path.join(tmpDir, 'b', 'b.only2') } - ]); + ], done); }); }); describe('template', () => { - it('should create a block using templates from config', () => { + it('should create a block using templates from config', done => { const opts = { defaults: { modules: { @@ -568,10 +666,10 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], opts, [{ name: path.join(tmpDir, 'b', 'b.css'), content: '.b { }' - }]); + }], done); }); - it('should create a block using template ID', () => { + it('should create a block using template ID', done => { const opts = { defaults: { modules: { @@ -598,21 +696,21 @@ describe('bem-tools-create', () => { "});", "" ].join(EOL) - }]); + }], done); }); }); }); describe('string parsing', () => { describe('entity parsing', () => { - it('should parse block from string with techs from args', () => { + it('should parse block from string with techs from args', done => { return testEntityHelper('b1', tmpDir, ['t1', 't2'], {}, [ { name: path.join(tmpDir, 'b1', 'b1.t1') }, { name: path.join(tmpDir, 'b1', 'b1.t2') } - ]); + ], done); }); - it('should parse block from string with techs from config', () => { + it('should parse block from string with techs from config', done => { const opts = { defaults: { modules: { @@ -632,10 +730,10 @@ describe('bem-tools-create', () => { return testEntityHelper('b1', tmpDir, null, opts, [ { name: path.join(tmpDir, 'b1', 'b1.tech1') }, { name: path.join(tmpDir, 'b1', 'b1.tech2') } - ]); + ], done); }); - it('should parse block with a tech from a string and ignore techs from config', () => { + it('should parse block with a tech from a string and ignore techs from config', done => { const opts = { defaults: { modules: { @@ -658,42 +756,42 @@ describe('bem-tools-create', () => { content: templates.css('b1') }, { name: path.join(tmpDir, 'b1', 'b1.argTech') } - ]); + ], done); }); - it('should parse elem from string', () => { + it('should parse elem from string', done => { return testEntityHelper('b1__e1', tmpDir, ['t1'], {}, [ { name: path.join(tmpDir, 'b1', '__e1', 'b1__e1.t1') } - ]); + ], done); }); - it('should parse block mod from string', () => { + it('should parse block mod from string', done => { return testEntityHelper('b1_m1', tmpDir, ['t1'], {}, [ { name: path.join(tmpDir, 'b1', '_m1', 'b1_m1.t1') } - ]); + ], done); }); - it('should parse block modVal from string', () => { + it('should parse block modVal from string', done => { return testEntityHelper('b1_m1_v1', tmpDir, ['t1'], {}, [ { name: path.join(tmpDir, 'b1', '_m1', 'b1_m1_v1.t1') } - ]); + ], done); }); - it('should parse elem mod from string', () => { + it('should parse elem mod from string', done => { return testEntityHelper('b1__e1_m1_v1', tmpDir, ['t1'], {}, [ { name: path.join(tmpDir, 'b1', '__e1', '_m1', 'b1__e1_m1_v1.t1') } - ]); + ], done); }); }); describe('levels from string', () => { - it('should get level from string', () => { + it('should get level from string', done => { return testEntityHelper(tmpDir + '/level1/b1.t1', null, null, {}, [ { name: path.join(tmpDir, 'level1', 'b1', 'b1.t1') } - ]); + ], done); }); - it('should resolve level from string by config', () => { + it('should resolve level from string by config', done => { const opts = { defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, fsRoot: tmpDir, @@ -713,17 +811,17 @@ describe('bem-tools-create', () => { { name: path.join(tmpDir, 'level1', 'b1', 'b1.t1') } - ]); + ], done); }); }); - it('should expand braces', () => { + it('should expand braces', done => { return testEntityHelper('{b1,b2}.{t1,t2}', tmpDir, null, {}, [ { name: path.join(tmpDir, 'b1', 'b1.t1') }, { name: path.join(tmpDir, 'b1', 'b1.t2') }, { name: path.join(tmpDir, 'b2', 'b2.t1') }, { name: path.join(tmpDir, 'b2', 'b2.t2') } - ]); + ], done); }); }); @@ -744,54 +842,16 @@ describe('bem-tools-create', () => { }); describe('command line arguments support', () => { - it('should exclude tech', () => { + it('should exclude tech', done => { const excludedTechs = ['css', 'js']; - return testEntityHelper([{ block: 'b' }], [tmpDir], ['css', 'js', 't1'], - { excludeTech: excludedTechs }, [{ - name: path.join(tmpDir, 'b', 'b.t1') - }] - ); - }); - - it('should support custom content', () => { - const content = 'Some testing content'; - return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], - { fileContent: content }, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: content - }] - ); - }); - - it('should force rewrite', () => { - const content = 'Some testing content'; - // run first time - return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: templates.css('b') - }]) - // run second time with force and another content - .then(() => testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], - { fileContent: content, forceRewrite: true }, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: content - }]) - ); - }); - - it('should support custom content with pipe', () => { - const content = 'Some piped testing content'; - const srcStream = new stream.Readable(); - srcStream.push(content); - srcStream.push(null); - - return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], - { fileContent: srcStream }, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: content - }] + return testEntityHelper( + [{ block: 'b' }], + [tmpDir], + ['css', 'js', 't1'], + { excludeTech: excludedTechs }, + [{name: path.join(tmpDir, 'b', 'b.t1')}], + done ); }); - }); }); From 23d4f2480d490b3918544645084fa14789e275d8 Mon Sep 17 00:00:00 2001 From: Alexandr Shleyko Date: Sat, 24 Mar 2018 20:57:19 +0300 Subject: [PATCH 07/13] templates specs --- lib/create.js | 12 ++-- lib/template.js | 44 +++++++++----- package.json | 1 + test/templates.js | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 23 deletions(-) create mode 100644 test/templates.js diff --git a/lib/create.js b/lib/create.js index 7e46e8e..c8b8107 100644 --- a/lib/create.js +++ b/lib/create.js @@ -8,7 +8,7 @@ var path = require('path'), bemNaming = require('@bem/naming'), braceExpansion = require('brace-expansion'), createEntity = require('./create-entity'), - getTemplate = require('./template'), + template = require('./template'), uniq = require('uniq'), Promise = require('pinkie-promise'), stream = require('stream'); @@ -115,11 +115,7 @@ function flatten(arr) { module.exports = function create(entities, levels, techs, options) { var createRes = prepareEntityData(entities, levels, techs, options); - return Promise.all(createRes.map(function(item) { - var template = item.options.fileContent || getTemplate(item.tech, item.levelOptions), - content = typeof template === 'string' ? - template : template(item.entity, bemNaming(item.levelOptions.naming)); - - return createEntity(item.path, content, item.levelOptions); - })); + return Promise.all(createRes.map(item => + template.apply(item).then(tmpl => createEntity(item.path, tmpl, item.levelOptions)) + )); }; diff --git a/lib/template.js b/lib/template.js index fd03c6b..65f191a 100644 --- a/lib/template.js +++ b/lib/template.js @@ -1,30 +1,44 @@ 'use strict'; var fs = require('fs'), - path = require('path'); + path = require('path'), + bemNaming = require('@bem/naming'), + stream = require('stream'), + streamToPromise = require('stream-to-promise'); function defaultTemplate() { return ''; } -module.exports = function getTemplate(tech, options) { - var templateFolder = options.templateFolder, - techId = (options.techsTemplates && options.techsTemplates[tech]) || tech, - templatePath = options.templates && options.templates[techId] && path.resolve(options.templates[techId]), - possiblePaths = [path.join(__dirname, 'templates')]; +module.exports = { + get: function(tech, options) { + var templateFolder = options.templateFolder, + techId = (options.techsTemplates && options.techsTemplates[tech]) || tech, + templatePath = options.templates && options.templates[techId] && path.resolve(options.templates[techId]), + possiblePaths = [path.join(__dirname, 'templates')]; - templateFolder && possiblePaths.unshift(templateFolder); + templateFolder && possiblePaths.unshift(templateFolder); - if (!templatePath) { - for (var i = 0; i < possiblePaths.length; i++) { - var possibleTemplatePath = path.resolve(possiblePaths[i], techId + '.js'); + if (!templatePath) { + for (var i = 0; i < possiblePaths.length; i++) { + var possibleTemplatePath = path.resolve(possiblePaths[i], techId + '.js'); - if (fs.existsSync(possibleTemplatePath)) { - templatePath = possibleTemplatePath; - break; + if (fs.existsSync(possibleTemplatePath)) { + templatePath = possibleTemplatePath; + break; + } } } - } - return templatePath ? require(templatePath) : defaultTemplate; + return templatePath ? require(templatePath) : defaultTemplate; + }, + + apply: function(item) { + var template = item.options.fileContent || this.get(item.tech, item.levelOptions), + isPiped = template instanceof stream.Readable; + + return isPiped ? streamToPromise(template) : Promise.resolve( + typeof template === 'string' ? template : template(item.entity, bemNaming(item.levelOptions.naming)) + ); + } }; diff --git a/package.json b/package.json index 3c7e99d..061f019 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "brace-expansion": "^1.1.6", "mkdirp": "^0.5.1", "pinkie-promise": "^2.0.1", + "stream-to-promise": "^2.2.0", "uniq": "^1.0.1" }, "devDependencies": { diff --git a/test/templates.js b/test/templates.js new file mode 100644 index 0000000..d4974db --- /dev/null +++ b/test/templates.js @@ -0,0 +1,146 @@ +'use strict'; + +const assert = require('assert'); +const bemNaming = require('@bem/naming'); + +const expected = { + 'default': { + 'bemhtml.js': { + block: "block('b').content()(function() {\n return;\n});\n", + element: "block('b').elem('e').content()(function() {\n return;\n});\n", + modifier: "block('b').mod('m', 'v').content()(function() {\n return;\n});\n", + 'element modifier': "block('b').elem('e').elemMod('em', 'ev').content()(function() {\n return;\n});\n" + }, + 'bemtree.js': { + block: "block('b').content()(function() {\n return;\n});\n", + element: "block('b').elem('e').content()(function() {\n return;\n});\n", + modifier: "block('b').mod('m', 'v').content()(function() {\n return;\n});\n", + 'element modifier': "block('b').elem('e').elemMod('em', 'ev').content()(function() {\n return;\n});\n" + }, + css: { + block: '.b {\n \n}\n', + element: '.b__e {\n \n}\n', + modifier: '.b_m_v {\n \n}\n', + 'element modifier': '.b__e_em_ev {\n \n}\n' + }, + js: { + block: "modules.define('b', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n", + element: "modules.define('b__e', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declElem('b', 'e', {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n", + modifier: "modules.define('b', function(provide, B) {\n\nprovide(B.declMod({ modName: 'm', modVal: 'v' }, {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n", + 'element modifier': "modules.define('b__e', function(provide, B__e) {\n\nprovide(B__e.declMod({ modName: 'em', modVal: 'ev' }, {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n" + } + }, + + custom: { + 'bemhtml.js': { + block: "block('b').content()(function() {\n return;\n});\n", + element: "block('b').elem('e').content()(function() {\n return;\n});\n", + modifier: "block('b').mod('m', 'v').content()(function() {\n return;\n});\n", + 'element modifier': "block('b').elem('e').elemMod('em', 'ev').content()(function() {\n return;\n});\n" + }, + 'bemtree.js': { + block: "block('b').content()(function() {\n return;\n});\n", + element: "block('b').elem('e').content()(function() {\n return;\n});\n", + modifier: "block('b').mod('m', 'v').content()(function() {\n return;\n});\n", + 'element modifier': "block('b').elem('e').elemMod('em', 'ev').content()(function() {\n return;\n});\n" + }, + css: { + block: '.b {\n \n}\n', + element: '.b-e {\n \n}\n', + modifier: '.b--m_v {\n \n}\n', + 'element modifier': '.b-e--em_ev {\n \n}\n' + }, + js: { + block: "modules.define('b', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n", + element: "modules.define('b-e', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declElem('b', 'e', {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n", + modifier: "modules.define('b', function(provide, B) {\n\nprovide(B.declMod({ modName: 'm', modVal: 'v' }, {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n", + 'element modifier': "modules.define('b-e', function(provide, BE) {\n\nprovide(BE.declMod({ modName: 'em', modVal: 'ev' }, {\n onSetMod: {\n js: {\n inited: function() {\n \n }\n }\n }\n}));\n\n});\n" + } + } +}; + +const selectors = { + block: { block: 'b' }, + element: { block: 'b', elem: 'e' }, + modifier: { block: 'b', modName: 'm', modVal: 'v' }, + 'element modifier': { block: 'b', elem: 'e', modName: 'em', modVal: 'ev' } +}; + +const namingSchemes = { + 'default': {}, + custom: { + delims: { + elem: '-', + mod: { name: '--', val: '_' } + } + } +}; + +Object.keys(namingSchemes).forEach(scheme => { + describe(`templates with ${scheme} scheme`, () => { + Object.keys(expected[scheme]).forEach(tmpl => { + describe(tmpl, () => { + const template = require(`../lib/templates/${tmpl}`); + Object.keys(selectors).forEach(selector => { + it(`provides content for ${selector}`, () => { + assert.equal( + template(selectors[selector], bemNaming(namingSchemes[scheme])), + expected[scheme][tmpl][selector] + ); + }); + }); + }); + }); + }); +}); + +describe('deps.js', () => { + it('provides content', () => { + const deps = require(`../lib/templates/deps.js`); + + assert.equal(deps(), '({\n shouldDeps: [\n \n ]\n})\n'); + }); +}); + +describe('template.apply', () => { + const template = require(`../lib/template`); + + it('should use template from templates directory', () => { + const item = { + levelOptions: { 'default': false }, + tech: 'css', + options: {}, + entity: { block: 'b' } + }; + + return template.apply(item).then(content => assert.equal(content, '.b {\n \n}\n')); + }); + + it('should use string fileContent if exists', () => { + const item = { + levelOptions: { 'default': false }, + tech: 'css', + options: { fileContent: 'test' }, + entity: { block: 'b' } + }; + + return template.apply(item).then(content => assert.equal(content, 'test')); + }); + + it('should use stream fileContent if exists', () => { + const Readable = require('stream').Readable; + const stream = new Readable; + + const item = { + levelOptions: { 'default': false }, + tech: 'css', + options: { fileContent: stream }, + entity: { block: 'b' } + }; + + stream.push('test'); + stream.push(null); + + return template.apply(item).then(content => assert.equal(content, 'test')); + }); +}); From 1600a6816e47ca77bbbbd85eef980bfa74242e7c Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Sat, 24 Mar 2018 21:26:41 +0300 Subject: [PATCH 08/13] wip: prepare-entity-data --- lib/create.js | 112 +---------- lib/prepare-entity-data.js | 108 ++++++++++ test/test1.js | 389 +++++++++++++++---------------------- 3 files changed, 271 insertions(+), 338 deletions(-) create mode 100644 lib/prepare-entity-data.js diff --git a/lib/create.js b/lib/create.js index c8b8107..be8a098 100644 --- a/lib/create.js +++ b/lib/create.js @@ -1,116 +1,8 @@ 'use strict'; -var path = require('path'), - bemConfig = require('bem-config'), - BemEntityName = require('@bem/entity-name'), - BemCell = require('@bem/cell'), - scheme = require('@bem/fs-scheme'), - bemNaming = require('@bem/naming'), - braceExpansion = require('brace-expansion'), +var prepareEntityData = require('./prepare-entity-data'), createEntity = require('./create-entity'), - template = require('./template'), - uniq = require('uniq'), - Promise = require('pinkie-promise'), - stream = require('stream'); - -//TODO: переименовать:) -function prepareEntityData(entities, levels, techs, options) { - options || (options = {}); - techs || (techs = []); - var baseConfig = bemConfig(options), - cwd = path.resolve(options.cwd || ''), - - bemToolsConf = baseConfig.moduleSync('bem-tools'), - - pluginConf = bemToolsConf && bemToolsConf.plugins && bemToolsConf.plugins.create || {}, - - config = bemConfig(Object.assign({}, options, { extendBy: pluginConf })); - - if (!levels || !levels.length) { - var levelsMap = config.levelMapSync(), - levelList = Object.keys(levelsMap); - - var defaultLevels = levelList.filter(function (level) { - return levelsMap[level].default; - }); - - var levelByCwd = levelList.filter(function (level) { - return cwd.indexOf(level) === 0; - }).sort().reverse()[0]; - - levels = levelByCwd || (defaultLevels.length ? defaultLevels : cwd); - } - - Array.isArray(entities) || (entities = [entities]); - Array.isArray(levels) || (levels = [levels]); - - return flatten(entities.map(function (input) { - var isFileGlob = typeof input === 'string'; - - return (isFileGlob ? braceExpansion(input) : [input]).map(function (filepathOrInput) { - var currentLevels = levels; - - if (typeof filepathOrInput === 'string') { - var currentLevel = path.dirname(filepathOrInput); - currentLevel !== '.' && (currentLevels = [currentLevel]); - } - - return currentLevels.map(function (relLevel) { - var rootDir = config.rootSync() || cwd, - level = path.resolve(rootDir, relLevel), - levelOptions = config.levelSync(level) || {}, - levelScheme = levelOptions.scheme, - buildPath = scheme(levelScheme).path, - currentTechs = uniq([].concat(levelOptions.techs || [], techs)), - entity; - - if (isFileGlob) { - var file = path.basename(filepathOrInput), - // split for entity key and tech (by first dot) - match = file.match(/^([^.]+)(?:\.(.+))?$/); - - entity = bemNaming(levelOptions.naming).parse(match[1]); - if (match[2]) { - currentTechs = uniq(techs.concat(match[2])); - } - } else { - entity = BemEntityName.create(filepathOrInput); - } - - options.onlyTech && (currentTechs = options.onlyTech); - - options.excludeTech && (currentTechs = currentTechs.filter(function (tech) { - return options.excludeTech.indexOf(tech) === -1; - })); - - return currentTechs.map(function (tech) { - var pathToFile = buildPath( - new BemCell({ entity: entity, tech: tech }), - levelOptions.schemeOptions || levelOptions), - absPathToFile = path.join(path.resolve(level), pathToFile); - - if (options.forceRewrite) { - levelOptions.forceRewrite = options.forceRewrite; - } - - return { - path: absPathToFile, - levelOptions: levelOptions, - tech: tech, - options: options, - entity: entity - }; - }); - }); - }); - })); -}; - -function flatten(arr) { - return arr.reduce(function (acc, item) { - return acc.concat(Array.isArray(item) ? flatten(item) : item); - }, []); -} + template = require('./template'); module.exports = function create(entities, levels, techs, options) { var createRes = prepareEntityData(entities, levels, techs, options); diff --git a/lib/prepare-entity-data.js b/lib/prepare-entity-data.js new file mode 100644 index 0000000..11e399b --- /dev/null +++ b/lib/prepare-entity-data.js @@ -0,0 +1,108 @@ +'use strict'; + +var path = require('path'), + bemNaming = require('@bem/naming'), + BemEntityName = require('@bem/entity-name'), + BemCell = require('@bem/cell'), + braceExpansion = require('brace-expansion'), + scheme = require('@bem/fs-scheme'), + uniq = require('uniq'), + bemConfig = require('bem-config'); + +function flatten(arr) { + return arr.reduce(function(acc, item) { + return acc.concat(Array.isArray(item) ? flatten(item) : item); + }, []); +} + +module.exports = function prepareEntityData(entities, levels, techs, options) { + options || (options = {}); + techs || (techs = []); + var baseConfig = bemConfig(options), + cwd = path.resolve(options.cwd || ''), + + bemToolsConf = baseConfig.moduleSync('bem-tools'), + + pluginConf = bemToolsConf && bemToolsConf.plugins && bemToolsConf.plugins.create || {}, + + config = bemConfig(Object.assign({}, options, { extendBy: pluginConf })); + + if (!levels || !levels.length) { + var levelsMap = config.levelMapSync(), + levelList = Object.keys(levelsMap); + + var defaultLevels = levelList.filter(function(level) { + return levelsMap[level].default; + }); + + var levelByCwd = levelList.filter(function(level) { + return cwd.indexOf(level) === 0; + }).sort().reverse()[0]; + + levels = levelByCwd || (defaultLevels.length ? defaultLevels : cwd); + } + + Array.isArray(entities) || (entities = [entities]); + Array.isArray(levels) || (levels = [levels]); + + return flatten(entities.map(function(input) { + var isFileGlob = typeof input === 'string'; + + return (isFileGlob ? braceExpansion(input) : [input]).map(function(filepathOrInput) { + var currentLevels = levels; + + if (typeof filepathOrInput === 'string') { + var currentLevel = path.dirname(filepathOrInput); + currentLevel !== '.' && (currentLevels = [currentLevel]); + } + + return currentLevels.map(function(relLevel) { + var rootDir = config.rootSync() || cwd, + level = path.resolve(rootDir, relLevel), + levelOptions = config.levelSync(level) || {}, + levelScheme = levelOptions.scheme, + buildPath = scheme(levelScheme).path, + currentTechs = uniq([].concat(levelOptions.techs || [], techs)), + entity; + + if (isFileGlob) { + var file = path.basename(filepathOrInput), + // split for entity key and tech (by first dot) + match = file.match(/^([^.]+)(?:\.(.+))?$/); + + entity = bemNaming(levelOptions.naming).parse(match[1]); + if (match[2]) { + currentTechs = uniq(techs.concat(match[2])); + } + } else { + entity = BemEntityName.create(filepathOrInput); + } + + options.onlyTech && (currentTechs = options.onlyTech); + + options.excludeTech && (currentTechs = currentTechs.filter(function(tech) { + return options.excludeTech.indexOf(tech) === -1; + })); + + return currentTechs.map(function(tech) { + var pathToFile = buildPath( + new BemCell({ entity: entity, tech: tech }), + levelOptions.schemeOptions || levelOptions), + absPathToFile = path.join(path.resolve(level), pathToFile); + + if (options.forceRewrite) { + levelOptions.forceRewrite = options.forceRewrite; + } + + return { + path: absPathToFile, + levelOptions: levelOptions, + tech: tech, + options: options, + entity: entity + }; + }); + }); + }); + })); +}; diff --git a/test/test1.js b/test/test1.js index fb4675f..72dd227 100644 --- a/test/test1.js +++ b/test/test1.js @@ -1,101 +1,70 @@ +/* eslint no-shadow: ["error", { "allow": ["path"] }] */ + 'use strict'; -const fs = require('fs'); const path = require('path'); const mkdirp = require('mkdirp'); -const rimraf = require('rimraf'); -const proxyquire = require('proxyquire'); -const naming = require('@bem/naming'); -const EOL = require('os').EOL; const assert = require('assert'); -const stream = require('stream'); -const sinon = require('sinon'); -const tmpDir = process.env.PWD; - -const templates = { - css: function(entity, namingScheme) { - const className = typeof entity === 'string' ? entity : naming(namingScheme).stringify(entity); - - return [ - '.' + className + ' {', - ' ', - '}', - '' - ].join(EOL); - } -}; - -function testEntityHelper(entities, levels, techs, options, expected, done) { - const spy = sinon.spy(); - const create = proxyquire('../lib/create', { - './create-entity': function(name, content, opts) { - console.log('name, content, opts', name, content, opts); - spy.apply(null, arguments); - } - }); - create(entities, levels, techs, options).then(() => { - assert.equal(spy.callCount, expected.length); - - expected.forEach(file => - assert(spy.calledWithMatch(file.name, file.content)) - ); - - done(); - }); +const prepareEntityData = require('../lib/prepare-entity-data'); +const tmpDir = process.cwd(); + +function testEntityHelper(entities, levels, techs, options, expected) { + const actualPaths = prepareEntityData(entities, levels, techs, options) + .map(({ path }) => path) + .sort(); + const expectdPaths = expected + .map(({ path }) => path) + .sort(); + + assert.deepEqual(actualPaths, expectdPaths); } describe('bem-tools-create', () => { describe('default scheme and default naming', () => { - it('should create a block using `nested` scheme and default naming', done => { + it('should create a block using `nested` scheme and default naming', () => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], {}, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: templates.css('b'), + path: path.join(tmpDir, 'b', 'b.css'), options: {} - }], done); + }]); }); - it('should create an element using `nested` scheme and default naming', done => { + it('should create an element using `nested` scheme and default naming', () => { return testEntityHelper([{ block: 'b', elem: 'e' }], [tmpDir], ['css'], {}, [{ - name: path.join(tmpDir, 'b', '__e', 'b__e.css'), - content: templates.css('b__e'), + path: path.join(tmpDir, 'b', '__e', 'b__e.css'), options: {} - }], done); + }]); }); - it('should create an block modifier using `nested` scheme and default naming', done => { + it('should create an block modifier using `nested` scheme and default naming', () => { return testEntityHelper([{ block: 'b', modName: 'm', modVal: 'v' }], [tmpDir], ['css'], {}, [{ - name: path.join(tmpDir, 'b', '_m', 'b_m_v.css'), - content: templates.css('b_m_v'), + path: path.join(tmpDir, 'b', '_m', 'b_m_v.css'), options: {} - }], done); + }]); }); - it('should create an element modifier using `nested` scheme and default naming', done => { + it('should create an element modifier using `nested` scheme and default naming', () => { return testEntityHelper([{ block: 'b', elem: 'e', modName: 'em', modVal: 'ev' }], [tmpDir], ['css'], {}, [{ - name: path.join(tmpDir, 'b', '__e', '_em', 'b__e_em_ev.css'), - content: templates.css('b__e_em_ev'), + path: path.join(tmpDir, 'b', '__e', '_em', 'b__e_em_ev.css'), options: {} - }], done); + }]); }); - it('should create a block with different techs', done => { + it('should create a block with different techs', () => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['css', 'deps.js'], {}, [ { - name: path.join(tmpDir, 'b', 'b.css'), - content: templates.css('b'), + path: path.join(tmpDir, 'b', 'b.css'), options: {} }, { - name: path.join(tmpDir, 'b', 'b.deps.js'), - content: ['({', ' shouldDeps: [', ' ', ' ]', '})', ''].join(EOL), + path: path.join(tmpDir, 'b', 'b.deps.js'), options: {} } - ], done); + ]); }); }); describe('custom options', () => { - it('should create entities with naming from config', done => { + it('should create entities with naming from config', () => { const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; const namingScheme = { delims: { @@ -105,24 +74,22 @@ describe('bem-tools-create', () => { }; return testEntityHelper([entity], [tmpDir], ['css'], { defaults: { naming: namingScheme } }, [{ - name: path.join(tmpDir, 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), - content: templates.css(entity, namingScheme), + path: path.join(tmpDir, 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), options: { naming: namingScheme } - }], done); + }]); }); - it('should create blocks with scheme from config', done => { + it('should create blocks with scheme from config', () => { const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; return testEntityHelper([entity], [tmpDir], ['css'], { defaults: { scheme: 'flat' } }, [{ - name: path.join(tmpDir, 'b__e1_m1_v1.css'), - content: templates.css(entity), + path: path.join(tmpDir, 'b__e1_m1_v1.css'), options: { scheme: 'flat' } - }], done); + }]); }); describe('levels', () => { - it('should create a block on default levels from config', done => { + it('should create a block on default levels from config', () => { const opts = { defaults: { levels: {} }, fsRoot: tmpDir, @@ -136,19 +103,17 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ { - name: path.join(tmpDir, 'level1', 'b', 'b.css'), - content: templates.css('b'), - options: { default: true } + path: path.join(tmpDir, 'level1', 'b', 'b.css'), + options: { 'default': true } }, { - name: path.join(tmpDir, 'level2', 'b', 'b.css'), - content: templates.css('b'), - options: { default: true } + path: path.join(tmpDir, 'level2', 'b', 'b.css'), + options: { 'default': true } } - ], done); + ]); }); - it('should create entities on levels with provided config', done => { + it('should create entities on levels with provided config', () => { const levels = [path.join(tmpDir, 'l1'), path.join(tmpDir, 'l2')]; const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; const namingScheme = { @@ -173,19 +138,17 @@ describe('bem-tools-create', () => { return testEntityHelper([entity], levels, ['css'], opts, [ { - name: path.join(tmpDir, 'l1', 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), - content: templates.css(entity, namingScheme), + path: path.join(tmpDir, 'l1', 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), options: { naming: namingScheme } }, { - name: path.join(tmpDir, 'l2', 'b__e1_m1_v1.css'), - content: templates.css(entity), + path: path.join(tmpDir, 'l2', 'b__e1_m1_v1.css'), options: { scheme: 'flat' } } - ], done); + ]); }); - it('should bubble to parent level when cwd is inside an entity', done => { + it('should bubble to parent level when cwd is inside an entity', () => { const opts = { defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, fsRoot: tmpDir, @@ -203,14 +166,13 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ { - name: path.join(tmpDir, 'level1', 'b', 'b.css'), - content: templates.css('b'), - options: { default: false } + path: path.join(tmpDir, 'level1', 'b', 'b.css'), + options: { 'default': false } } - ], done); + ]); }); - it('should create an entity on default level when cwd is not inside a level folder', done => { + it('should create an entity on default level when cwd is not inside a level folder', () => { const opts = { defaults: { levels: {} }, fsRoot: tmpDir, @@ -228,19 +190,17 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ { - name: path.join(tmpDir, 'level1', 'b', 'b.css'), - content: templates.css('b'), - options: { default: true } + path: path.join(tmpDir, 'level1', 'b', 'b.css'), + options: { 'default': true } }, { - name: path.join(tmpDir, 'level2', 'b', 'b.css'), - content: templates.css('b'), - options: { default: true } + path: path.join(tmpDir, 'level2', 'b', 'b.css'), + options: { 'default': true } } - ], done); + ]); }); - it('should create an entity on provided not default level when cwd is not inside a level folder', done => { + it('should create an entity on provided not default level when cwd is not inside a level folder', () => { const opts = { defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, fsRoot: tmpDir, @@ -258,35 +218,32 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], 'level2', ['css'], opts, [ { - name: path.join(tmpDir, 'level2', 'b', 'b.css'), - content: templates.css('b'), - options: { default: false } + path: path.join(tmpDir, 'level2', 'b', 'b.css'), + options: { 'default': false } } - ], done); + ]); }); - it('should create a block on cwd as a fallback', done => { + it('should create a block on cwd as a fallback', () => { const fakeCwd = path.join(tmpDir, 'cwd'); mkdirp.sync(fakeCwd); process.chdir(fakeCwd); return testEntityHelper([{ block: 'b' }], null, ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ - name: path.join(fakeCwd, 'b', 'b.css'), - content: templates.css('b'), + path: path.join(fakeCwd, 'b', 'b.css'), options: {} - }], done); + }]); }); - it('should create block on provided levels', done => { + it('should create block on provided levels', () => { return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], { fsRoot: tmpDir, fsHome: tmpDir }, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: templates.css('b'), + path: path.join(tmpDir, 'b', 'b.css'), options: {} - }], done); + }]); }); describe('level config in plugin config', () => { - it('should respect level techs', done => { + it('should respect level techs', () => { const createLevels = {}; const opts = { defaults: { @@ -315,36 +272,33 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['tech1', 'tech2'], opts, [ { - name: path.join(level, 'b', 'b.tech1'), - content: '', + path: path.join(level, 'b', 'b.tech1'), options: { modules: opts.defaults.modules, techs: ['create-level-tech1'], - default: true + 'default': true } }, { - name: path.join(level, 'b', 'b.tech2'), - content: '', + path: path.join(level, 'b', 'b.tech2'), options: { modules: opts.defaults.modules, techs: ['create-level-tech1'], - default: true + 'default': true } }, { - name: path.join(level, 'b', 'b.create-level-tech1'), - content: '', + path: path.join(level, 'b', 'b.create-level-tech1'), options: { modules: opts.defaults.modules, techs: ['create-level-tech1'], - default: true + 'default': true } } - ], done); + ]); }); - it('should get default level from plugin config', done => { + it('should get default level from plugin config', () => { const createLevels = {}; const opts = { defaults: { @@ -373,39 +327,36 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['tech1', 'tech2'], opts, [ { - name: path.join(level, 'b', 'b.tech1'), - content: '', + path: path.join(level, 'b', 'b.tech1'), options: { modules: opts.defaults.modules, techs: ['create-level-tech1'], - default: true + 'default': true } }, { - name: path.join(level, 'b', 'b.tech2'), - content: '', + path: path.join(level, 'b', 'b.tech2'), options: { modules: opts.defaults.modules, techs: ['create-level-tech1'], - default: true + 'default': true } }, { - name: path.join(level, 'b', 'b.create-level-tech1'), - content: '', + path: path.join(level, 'b', 'b.create-level-tech1'), options: { modules: opts.defaults.modules, techs: ['create-level-tech1'], - default: true + 'default': true } } - ], done); + ]); }); - it('should respect level templates', done => { + it('should respect level templates', () => { const createLevels = {}; const opts = { defaults: { @@ -434,13 +385,12 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, ['css'], opts, [ { - name: path.join(level, 'b', 'b.css'), - content: '.b {\n}\n' + path: path.join(level, 'b', 'b.css') } - ], done); + ]); }); - it('should support glob with absolute level path', done => { + it('should support glob with absolute level path', () => { const createPluginLevels = {}; const opts = { defaults: { @@ -470,25 +420,21 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, null, opts, [ { - name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3'), - content: '' + path: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3') }, { - name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4'), - content: '' + path: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4') }, { - name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3'), - content: '' + path: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3') }, { - name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4'), - content: '' + path: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4') } - ], done); + ]); }); - it('should support glob resolution for levels', done => { + it('should support glob resolution for levels', () => { const levels = {}; const createPluginLevels = {}; const opts = { @@ -519,28 +465,24 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], null, null, opts, [ { - name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3'), - content: '' + path: path.join(tmpDir, 'common.blocks', 'b', 'b.tech3') }, { - name: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4'), - content: '' + path: path.join(tmpDir, 'common.blocks', 'b', 'b.tech4') }, { - name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3'), - content: '' + path: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech3') }, { - name: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4'), - content: '' + path: path.join(tmpDir, 'desktop.blocks', 'b', 'b.tech4') } - ], done); + ]); }); }); }); describe('techs', () => { - it('should create block in techs from config', done => { + it('should create block in techs from config', () => { const opts = { defaults: { modules: { @@ -559,17 +501,15 @@ describe('bem-tools-create', () => { return testEntityHelper([{ block: 'b' }], [tmpDir], null, opts, [ { - name: path.join(tmpDir, 'b', 'b.tech1'), - content: '' + path: path.join(tmpDir, 'b', 'b.tech1') }, { - name: path.join(tmpDir, 'b', 'b.tech2'), - content: '' + path: path.join(tmpDir, 'b', 'b.tech2') } - ], done); + ]); }); - it('should create block in techs from config and provided techs', done => { + it('should create block in techs from config and provided techs', () => { const opts = { defaults: { modules: { @@ -587,15 +527,15 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech3', 'tech4'], opts, [ - { name: path.join(tmpDir, 'b', 'b.tech1') }, - { name: path.join(tmpDir, 'b', 'b.tech2') }, - { name: path.join(tmpDir, 'b', 'b.tech3') }, - { name: path.join(tmpDir, 'b', 'b.tech4') } - ], done); + { path: path.join(tmpDir, 'b', 'b.tech1') }, + { path: path.join(tmpDir, 'b', 'b.tech2') }, + { path: path.join(tmpDir, 'b', 'b.tech3') }, + { path: path.join(tmpDir, 'b', 'b.tech4') } + ]); }); // TODO: check that it fires only twice instead of four times - it('should create block in techs from config and the same provided techs', done => { + it('should create block in techs from config and the same provided techs', () => { const opts = { defaults: { modules: { @@ -613,12 +553,12 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech1', 'tech2'], opts, [ - { name: path.join(tmpDir, 'b', 'b.tech1') }, - { name: path.join(tmpDir, 'b', 'b.tech2') } - ], done); + { path: path.join(tmpDir, 'b', 'b.tech1') }, + { path: path.join(tmpDir, 'b', 'b.tech2') } + ]); }); - it('should create block only in provided techs', done => { + it('should create block only in provided techs', () => { const opts = { onlyTech: ['only1', 'only2'], defaults: { @@ -637,14 +577,14 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], [tmpDir], ['tech1', 'tech2'], opts, [ - { name: path.join(tmpDir, 'b', 'b.only1') }, - { name: path.join(tmpDir, 'b', 'b.only2') } - ], done); + { path: path.join(tmpDir, 'b', 'b.only1') }, + { path: path.join(tmpDir, 'b', 'b.only2') } + ]); }); }); describe('template', () => { - it('should create a block using templates from config', done => { + it('should create a block using templates from config', () => { const opts = { defaults: { modules: { @@ -664,12 +604,11 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], opts, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: '.b { }' - }], done); + path: path.join(tmpDir, 'b', 'b.css') + }]); }); - it('should create a block using template ID', done => { + it('should create a block using template ID', () => { const opts = { defaults: { modules: { @@ -689,28 +628,22 @@ describe('bem-tools-create', () => { }; return testEntityHelper([{ block: 'b' }], [tmpDir], ['bemtree.js'], opts, [{ - name: path.join(tmpDir, 'b', 'b.bemtree.js'), - content: [ - "block('b').content()(function() {", - " return;", - "});", - "" - ].join(EOL) - }], done); + path: path.join(tmpDir, 'b', 'b.bemtree.js') + }]); }); }); }); describe('string parsing', () => { describe('entity parsing', () => { - it('should parse block from string with techs from args', done => { + it('should parse block from string with techs from args', () => { return testEntityHelper('b1', tmpDir, ['t1', 't2'], {}, [ - { name: path.join(tmpDir, 'b1', 'b1.t1') }, - { name: path.join(tmpDir, 'b1', 'b1.t2') } - ], done); + { path: path.join(tmpDir, 'b1', 'b1.t1') }, + { path: path.join(tmpDir, 'b1', 'b1.t2') } + ]); }); - it('should parse block from string with techs from config', done => { + it('should parse block from string with techs from config', () => { const opts = { defaults: { modules: { @@ -728,12 +661,12 @@ describe('bem-tools-create', () => { }; return testEntityHelper('b1', tmpDir, null, opts, [ - { name: path.join(tmpDir, 'b1', 'b1.tech1') }, - { name: path.join(tmpDir, 'b1', 'b1.tech2') } - ], done); + { path: path.join(tmpDir, 'b1', 'b1.tech1') }, + { path: path.join(tmpDir, 'b1', 'b1.tech2') } + ]); }); - it('should parse block with a tech from a string and ignore techs from config', done => { + it('should parse block with a tech from a string and ignore techs from config', () => { const opts = { defaults: { modules: { @@ -752,46 +685,45 @@ describe('bem-tools-create', () => { return testEntityHelper('b1.css', tmpDir, ['argTech'], opts, [ { - name: path.join(tmpDir, 'b1', 'b1.css'), - content: templates.css('b1') + path: path.join(tmpDir, 'b1', 'b1.css') }, - { name: path.join(tmpDir, 'b1', 'b1.argTech') } - ], done); + { path: path.join(tmpDir, 'b1', 'b1.argTech') } + ]); }); - it('should parse elem from string', done => { + it('should parse elem from string', () => { return testEntityHelper('b1__e1', tmpDir, ['t1'], {}, [ - { name: path.join(tmpDir, 'b1', '__e1', 'b1__e1.t1') } - ], done); + { path: path.join(tmpDir, 'b1', '__e1', 'b1__e1.t1') } + ]); }); - it('should parse block mod from string', done => { + it('should parse block mod from string', () => { return testEntityHelper('b1_m1', tmpDir, ['t1'], {}, [ - { name: path.join(tmpDir, 'b1', '_m1', 'b1_m1.t1') } - ], done); + { path: path.join(tmpDir, 'b1', '_m1', 'b1_m1.t1') } + ]); }); - it('should parse block modVal from string', done => { + it('should parse block modVal from string', () => { return testEntityHelper('b1_m1_v1', tmpDir, ['t1'], {}, [ - { name: path.join(tmpDir, 'b1', '_m1', 'b1_m1_v1.t1') } - ], done); + { path: path.join(tmpDir, 'b1', '_m1', 'b1_m1_v1.t1') } + ]); }); - it('should parse elem mod from string', done => { + it('should parse elem mod from string', () => { return testEntityHelper('b1__e1_m1_v1', tmpDir, ['t1'], {}, [ - { name: path.join(tmpDir, 'b1', '__e1', '_m1', 'b1__e1_m1_v1.t1') } - ], done); + { path: path.join(tmpDir, 'b1', '__e1', '_m1', 'b1__e1_m1_v1.t1') } + ]); }); }); describe('levels from string', () => { - it('should get level from string', done => { + it('should get level from string', () => { return testEntityHelper(tmpDir + '/level1/b1.t1', null, null, {}, [ - { name: path.join(tmpDir, 'level1', 'b1', 'b1.t1') } - ], done); + { path: path.join(tmpDir, 'level1', 'b1', 'b1.t1') } + ]); }); - it('should resolve level from string by config', done => { + it('should resolve level from string by config', () => { const opts = { defaults: { levels: {}, root: true, __source: path.join(tmpDir, '.bemrc') }, fsRoot: tmpDir, @@ -809,19 +741,19 @@ describe('bem-tools-create', () => { return testEntityHelper(tmpDir + '/level1/b1.t1', null, null, opts, [ { - name: path.join(tmpDir, 'level1', 'b1', 'b1.t1') + path: path.join(tmpDir, 'level1', 'b1', 'b1.t1') } - ], done); + ]); }); }); - it('should expand braces', done => { + it('should expand braces', () => { return testEntityHelper('{b1,b2}.{t1,t2}', tmpDir, null, {}, [ - { name: path.join(tmpDir, 'b1', 'b1.t1') }, - { name: path.join(tmpDir, 'b1', 'b1.t2') }, - { name: path.join(tmpDir, 'b2', 'b2.t1') }, - { name: path.join(tmpDir, 'b2', 'b2.t2') } - ], done); + { path: path.join(tmpDir, 'b1', 'b1.t1') }, + { path: path.join(tmpDir, 'b1', 'b1.t2') }, + { path: path.join(tmpDir, 'b2', 'b2.t1') }, + { path: path.join(tmpDir, 'b2', 'b2.t2') } + ]); }); }); @@ -842,15 +774,16 @@ describe('bem-tools-create', () => { }); describe('command line arguments support', () => { - it('should exclude tech', done => { + it('should exclude tech', () => { const excludedTechs = ['css', 'js']; return testEntityHelper( [{ block: 'b' }], [tmpDir], ['css', 'js', 't1'], { excludeTech: excludedTechs }, - [{name: path.join(tmpDir, 'b', 'b.t1')}], - done + [{ + path: '/Users/deemidroll/projects/bem-tools/bem-tools-create/b/b.t1' + }] ); }); }); From 93238553060b8c43a27817898efe0943f6791213 Mon Sep 17 00:00:00 2001 From: Lisbon Date: Sat, 24 Mar 2018 22:42:23 +0300 Subject: [PATCH 09/13] add tests for create-entity --- test/create-entity.js | 116 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 test/create-entity.js diff --git a/test/create-entity.js b/test/create-entity.js new file mode 100644 index 0000000..38c067a --- /dev/null +++ b/test/create-entity.js @@ -0,0 +1,116 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const mkdirp = require('mkdirp'); +const rimraf = require('rimraf'); +const naming = require('@bem/naming'); +const EOL = require('os').EOL; +const assert = require('assert'); +const createEntity = require('../lib/create-entity'); + +const tmpDir = path.join(__dirname, 'tmp'); +const initialCwd = process.cwd(); + +const templates = { + css: function(entity, namingScheme) { + const className = typeof entity === 'string' ? entity : naming(namingScheme).stringify(entity); + + return [ + '.' + className + ' {', + ' ', + '}', + '' + ].join(EOL); + } +}; + +function testEntityHelper(fileName, content, options) { + return createEntity(fileName, content, options).then(() => { + let actualContent; + const relativeFileName = path.relative(initialCwd, fileName); + + if (typeof content === 'undefined') { + content = ''; + } + + try { + actualContent = fs.readFileSync(fileName, 'utf8'); + } catch (err) { + throw new Error(`${relativeFileName} was not created`); + } + + assert.equal(actualContent, content, `${relativeFileName} content is not correct`); + }); +} + +describe('bem-tools-create', () => { + beforeEach(() => mkdirp.sync(tmpDir)); + + afterEach(() => { + rimraf.sync(tmpDir); + process.chdir(initialCwd); + }); + + describe('default scheme and default naming', () => { + it('should create a block using `nested` scheme and default naming', done => { + testEntityHelper( + path.join(tmpDir, 'b', 'b.css'), + templates.css('b'), + {} + ).then(done); + }); + + it('should create an element using `nested` scheme and default naming', done => { + testEntityHelper( + path.join(tmpDir, 'b', '__e', 'b__e.css'), + templates.css('b__e'), + {} + ).then(done); + }); + + it('should create an block modifier using `nested` scheme and default naming', done => { + testEntityHelper( + path.join(tmpDir, 'b', '_m', 'b_m_v.css'), + templates.css('b_m_v'), + {} + ).then(done); + }); + + it('should create an element modifier using `nested` scheme and default naming', done => { + testEntityHelper( + path.join(tmpDir, 'b', '__e', '_em', 'b__e_em_ev.css'), + templates.css('b__e_em_ev'), + {} + ).then(done); + }); + }); + + describe('custom options', () => { + it('should create entities with naming from config', () => { + const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; + const namingScheme = { + delims: { + elem: '-', + mod: { name: '--', val: '_' } + } + }; + + return testEntityHelper( + path.join(tmpDir, 'b', '-e1', '--m1', 'b-e1--m1_v1.css'), + templates.css(entity, namingScheme), + {} + ); + }); + + it('should create blocks with scheme from config', () => { + const entity = { block: 'b', elem: 'e1', modName: 'm1', modVal: 'v1' }; + + return testEntityHelper( + path.join(tmpDir, 'b__e1_m1_v1.css'), + templates.css(entity), + {} + ); + }); + }); +}); From 57755da2ef81656ef69891866334df26dda7361c Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Sun, 25 Mar 2018 13:30:42 +0300 Subject: [PATCH 10/13] remove proxyquire and sinon --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 061f019..fe37c2f 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,6 @@ "eslint": "^3.16.0", "eslint-config-pedant": "^0.8.0", "mocha": "^3.1.0", - "proxyquire": "^2.0.1", - "rimraf": "^2.5.4", - "sinon": "^4.4.8" + "rimraf": "^2.5.4" } } From cde5af239306e83684697da0601ce74db61b775a Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Sun, 25 Mar 2018 13:31:01 +0300 Subject: [PATCH 11/13] fix path in test --- test/test1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test1.js b/test/test1.js index 72dd227..556f10e 100644 --- a/test/test1.js +++ b/test/test1.js @@ -782,7 +782,7 @@ describe('bem-tools-create', () => { ['css', 'js', 't1'], { excludeTech: excludedTechs }, [{ - path: '/Users/deemidroll/projects/bem-tools/bem-tools-create/b/b.t1' + path: path.join(tmpDir, 'b', 'b.t1') }] ); }); From 83bfbdf1c9a7535fecab52e897ac3a34819abce1 Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Sun, 25 Mar 2018 13:40:16 +0300 Subject: [PATCH 12/13] fix eslint errors --- test/test1.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/test1.js b/test/test1.js index 556f10e..61b51c5 100644 --- a/test/test1.js +++ b/test/test1.js @@ -1,5 +1,3 @@ -/* eslint no-shadow: ["error", { "allow": ["path"] }] */ - 'use strict'; const path = require('path'); @@ -10,10 +8,10 @@ const tmpDir = process.cwd(); function testEntityHelper(entities, levels, techs, options, expected) { const actualPaths = prepareEntityData(entities, levels, techs, options) - .map(({ path }) => path) + .map(item => item.path) .sort(); const expectdPaths = expected - .map(({ path }) => path) + .map(item => item.path) .sort(); assert.deepEqual(actualPaths, expectdPaths); From 503416a24d02855d4b1e7accb135ed8b58f331bf Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Sun, 25 Mar 2018 17:24:43 +0300 Subject: [PATCH 13/13] wip: get-path --- lib/create.js | 21 +++++++++++-------- ...pare-entity-data.js => get-entity-data.js} | 16 ++++---------- lib/get-path.js | 15 +++++++++++++ test/test.js | 16 -------------- test/test1.js | 7 ++++--- 5 files changed, 35 insertions(+), 40 deletions(-) rename lib/{prepare-entity-data.js => get-entity-data.js} (84%) create mode 100644 lib/get-path.js diff --git a/lib/create.js b/lib/create.js index be8a098..75269ca 100644 --- a/lib/create.js +++ b/lib/create.js @@ -1,13 +1,16 @@ -'use strict'; - -var prepareEntityData = require('./prepare-entity-data'), - createEntity = require('./create-entity'), - template = require('./template'); +const getEntityData = require('./get-entity-data'); +const getPath = require('./get-path'); +const template = require('./template'); +const createEntity = require('./create-entity'); module.exports = function create(entities, levels, techs, options) { - var createRes = prepareEntityData(entities, levels, techs, options); + var entityData = getEntityData(entities, levels, techs, options); - return Promise.all(createRes.map(item => - template.apply(item).then(tmpl => createEntity(item.path, tmpl, item.levelOptions)) - )); + return Promise.all(entityData.map(item => + template.apply(item) + .then(content => + createEntity(getPath(item), content, item.levelOptions) + ) + ) + ); }; diff --git a/lib/prepare-entity-data.js b/lib/get-entity-data.js similarity index 84% rename from lib/prepare-entity-data.js rename to lib/get-entity-data.js index 11e399b..73e4aa5 100644 --- a/lib/prepare-entity-data.js +++ b/lib/get-entity-data.js @@ -3,9 +3,7 @@ var path = require('path'), bemNaming = require('@bem/naming'), BemEntityName = require('@bem/entity-name'), - BemCell = require('@bem/cell'), braceExpansion = require('brace-expansion'), - scheme = require('@bem/fs-scheme'), uniq = require('uniq'), bemConfig = require('bem-config'); @@ -15,7 +13,7 @@ function flatten(arr) { }, []); } -module.exports = function prepareEntityData(entities, levels, techs, options) { +module.exports = function getEntityData(entities, levels, techs, options) { options || (options = {}); techs || (techs = []); var baseConfig = bemConfig(options), @@ -60,8 +58,6 @@ module.exports = function prepareEntityData(entities, levels, techs, options) { var rootDir = config.rootSync() || cwd, level = path.resolve(rootDir, relLevel), levelOptions = config.levelSync(level) || {}, - levelScheme = levelOptions.scheme, - buildPath = scheme(levelScheme).path, currentTechs = uniq([].concat(levelOptions.techs || [], techs)), entity; @@ -85,21 +81,17 @@ module.exports = function prepareEntityData(entities, levels, techs, options) { })); return currentTechs.map(function(tech) { - var pathToFile = buildPath( - new BemCell({ entity: entity, tech: tech }), - levelOptions.schemeOptions || levelOptions), - absPathToFile = path.join(path.resolve(level), pathToFile); - if (options.forceRewrite) { levelOptions.forceRewrite = options.forceRewrite; } return { - path: absPathToFile, + config: config, levelOptions: levelOptions, tech: tech, options: options, - entity: entity + entity: entity, + level: level }; }); }); diff --git a/lib/get-path.js b/lib/get-path.js new file mode 100644 index 0000000..ef1b6f7 --- /dev/null +++ b/lib/get-path.js @@ -0,0 +1,15 @@ +const path = require('path'); +const scheme = require('@bem/fs-scheme'); +const BemCell = require('@bem/cell'); + +module.exports = function getPath(item) { + const levelOptions = item.config.levelSync(item.level) || {}; + const levelScheme = levelOptions.scheme; + const buildPath = scheme(levelScheme).path; + const pathToFile = buildPath( + new BemCell({ entity: item.entity, tech: item.tech }), + item.levelOptions.schemeOptions || item.levelOptions + ); + + return path.join(path.resolve(item.level), pathToFile); +}; diff --git a/test/test.js b/test/test.js index a594224..6a56435 100644 --- a/test/test.js +++ b/test/test.js @@ -8,7 +8,6 @@ const create = require('..'); const naming = require('@bem/naming'); const EOL = require('os').EOL; const assert = require('assert'); -const stream = require('stream'); const tmpDir = path.join(__dirname, 'tmp'); const initialCwd = process.cwd(); @@ -798,20 +797,5 @@ describe('bem-tools-create', () => { }]) ); }); - - it('should support custom content with pipe', () => { - const content = 'Some piped testing content'; - const srcStream = new stream.Readable(); - srcStream.push(content); - srcStream.push(null); - - return testEntityHelper([{ block: 'b' }], [tmpDir], ['css'], - { fileContent: srcStream }, [{ - name: path.join(tmpDir, 'b', 'b.css'), - content: content - }] - ); - }); - }); }); diff --git a/test/test1.js b/test/test1.js index 61b51c5..f2b62f4 100644 --- a/test/test1.js +++ b/test/test1.js @@ -3,12 +3,13 @@ const path = require('path'); const mkdirp = require('mkdirp'); const assert = require('assert'); -const prepareEntityData = require('../lib/prepare-entity-data'); +const getEntityData = require('../lib/get-entity-data'); +const getPath = require('../lib/get-path'); const tmpDir = process.cwd(); function testEntityHelper(entities, levels, techs, options, expected) { - const actualPaths = prepareEntityData(entities, levels, techs, options) - .map(item => item.path) + const actualPaths = getEntityData(entities, levels, techs, options) + .map(getPath) .sort(); const expectdPaths = expected .map(item => item.path)