Skip to content

Commit

Permalink
Added requires option
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewblond committed May 26, 2015
1 parent 65d6916 commit a2b4f51
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 72 deletions.
86 changes: 52 additions & 34 deletions lib/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ core.contents = fs.readFileSync(core.path, 'utf-8');
* @param {Boolean} [opts.jsElem] Use `jsElem` option for bh core
* @param {String} [opts.scope=template] Scope of the executing code templates
* @param {String[]} [opts.mimic] Names for exports
* @param {Object} [opts.dependencies] Names for requires to `BH.lib.name`
* @param {Object} [opts.requires] Names for requires to `BH.lib.name`
* @returns {String} compiled code of bh module
*/
function compile(sources, opts) {
Expand All @@ -37,8 +37,7 @@ function compile(sources, opts) {
var file = new File(opts.filename, opts.sourcemap),
isTemplateScope = opts.hasOwnProperty('scope') ? opts.scope === 'template' : true,
mimic = opts.mimic || [],
dependencies = opts.dependencies || {},
depNames = Object.keys(dependencies);
requires = opts.requires || {};

// IIFE start
file.writeLine('(function (global) {');
Expand All @@ -56,9 +55,9 @@ function compile(sources, opts) {
'});'
].join(EOL));

// wrap in IIFE to ignore utility variables for templates
file.writeLine('(function (global, BH) {');
// Source Templates
// `init()` will be called after all the dependencies (bh.lib) will be provided
// wrap in function scope not to pollute templates scope
file.writeLine('var init = function (global, BH) {');
sources.forEach(function (source) {
var relPath = source.relPath || source.path;

Expand All @@ -69,32 +68,32 @@ function compile(sources, opts) {
file.writeLine('// end: ' + relPath);
isTemplateScope && file.writeLine('}());');
});
file.writeLine('}());');
file.writeLine('};');

// Export bh
file.writeLine([
'var defineAsGlobal = true;',
// Provide to YModules
'if (typeof modules === "object") {',
compileYModule('BH', dependencies),
compileYModule('BH', requires),
' init();',
mimic.map(function (name) {
return compileYModule(name);
}).join(EOL),
' defineAsGlobal = false;',

// Provide libs from global scope
depNames.length ?
[
'} else {',
depNames.map(function (name) {
return ' bh.lib.' + name + ' = ' + dependencies[name] + ';';
}).join(EOL),
'}'
].join(EOL) :
'}',

// Provide with CommonJS
'if (typeof exports === "object") {',
'} else if (typeof exports === "object") {',
Object.keys(requires).map(function (name) {
var item = requires[name];

if (item.commonJS) {
return ' bh.lib.' + name + ' = require("' + item.commonJS + '");';
} else if (item.globals) {
return ' bh.lib.' + name + ' = global["' + item.globals + '"];';
}
}).join(EOL),
' init();',
mimic.map(function (name) {
return ' bh["' + name + '"] = bh;';
}).join(EOL),
Expand All @@ -104,6 +103,14 @@ function compile(sources, opts) {

// Provide to Global Scope
'if (defineAsGlobal) {',
Object.keys(requires).map(function (name) {
var item = requires[name];

if (item.globals) {
return ' bh.lib.' + name + ' = global["' + item.globals + '"];';
}
}).join(EOL),
' init();',
' global.BH = bh;',
mimic.map(function (name) {
return ' global["' + name + '"] = bh;';
Expand All @@ -120,24 +127,35 @@ function compile(sources, opts) {
/**
* Compile code with YModule definition that exports `BH` module.
*
* @param {String} name Module name
* @param {Object} [dependencies] Names for requires to `bh.lib.name`.
* @param {String} name Module name
* @param {Object} [requires] Names for requires to `bh.lib.name`.
* @returns {String}
*/
function compileYModule(name, dependencies) {
dependencies || (dependencies = {});

var libs = Object.keys(dependencies),
deps = libs.map(function (name) {
return dependencies[name];
});
function compileYModule(name, requires) {
var modules = [],
deps = [],
globals = {};

requires && Object.keys(requires).forEach(function (name) {
var item = requires[name];

if (item.ym) {
modules.push(item.ym);
deps.push(name);
} else if (item.globals) {
globals[name] = item.globals;
}
});

return [
' modules.define("' + name + '"' + (deps ? ', ' + JSON.stringify(deps) : '') +
', function(provide' + (libs && libs.length ? ', ' + libs.join(', ') : '') + ') {',
libs.map(function (name) {
return ' bh.lib.' + name + ' = ' + dependencies[name] + ';';
}),
' modules.define("' + name + '"' + (modules ? ', ' + JSON.stringify(modules) : '') +
', function(provide' + (deps && deps.length ? ', ' + deps.join(', ') : '') + ') {',
deps.map(function (name) {
return ' bh.lib.' + name + ' = ' + name + ';';
}).join(EOL),
Object.keys(globals).map(function (name) {
return ' bh.lib.' + name + ' = global["' + globals[name] + '"];';
}).join(EOL),
' provide(bh);',
' });'
].join(EOL);
Expand Down
4 changes: 2 additions & 2 deletions techs/bh-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var vow = require('vow'),
module.exports = require('enb/lib/build-flow').create()
.name('bh-bundle')
.target('target', '?.bh.js')
.defineOption('dependencies', {})
.defineOption('requires', {})
.defineOption('mimic', [])
.defineOption('jsAttrName', 'data-bem')
.defineOption('jsAttrScheme', 'json')
Expand Down Expand Up @@ -82,7 +82,7 @@ module.exports = require('enb/lib/build-flow').create()
escapeContent: this._escapeContent,
scope: this._scope,
mimic: [].concat(this._mimic),
dependencies: this._dependencies
requires: this._requires
};

return compile(sources, opts);
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/browser--global.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<div id="mocha"></div>
<script src="mocha.js"></script>
<script src="chai.js"></script>
<script src="some-lib.js"></script>
<script src="bundle/bundle.bh.js"></script>
<script>
mocha.ui('bdd');
Expand Down
91 changes: 68 additions & 23 deletions test/techs/bh-bundle/bh-bundle--node.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
var fs = require('fs'),
var path = require('path'),
fs = require('fs'),
mock = require('mock-fs'),
TestNode = require('enb/lib/test/mocks/test-node'),
Tech = require('../../../techs/bh-bundle'),
FileList = require('enb/lib/file-list'),
bhCoreFilename = require.resolve('bh/lib/bh.js');
dropRequireCache = require('enb/lib/fs/drop-require-cache'),
EOL = require('os').EOL;

describe('bh-bundle --node', function () {
afterEach(function () {
Expand Down Expand Up @@ -34,51 +36,94 @@ describe('bh-bundle --node', function () {
});
});

it('dependencies', function () {
var templates = [
'bh.match("block", function(ctx) { ctx.content(bh.lib.test); });'
],
bemjson = { block: 'block' },
html = '<div class="block">^_^</div>',
options = {
dependencies: { test: '"^_^"' }
};

return assert(bemjson, html, templates, options);
describe('requires', function () {
it('must get dependency from global scope', function () {
var templates = [
'bh.match("block", function(ctx) { ctx.content(bh.lib.text); });'
],
bemjson = { block: 'block' },
html = '<div class="block">Hello world!</div>',
options = {
requires: {
text: {
globals: 'text'
}
}
},
lib = 'this.text = "Hello world!";';

return build(templates, options, lib)
.then(function (BH) {
BH.apply(bemjson).must.equal(html);
});
});

it('must require module from CommonJS', function () {
var templates = [
[
'var url = bh.lib.url.resolve("http://example.com/", "/one");',
'bh.match("block", function(ctx) { ',
' ctx.tag("a");',
' ctx.attr("href", url);',
'});'
].join(EOL)
],
bemjson = { block: 'block' },
html = '<a class="block" href="http://example.com/one"></a>',
options = {
requires: {
url: {
commonJS: 'url'
}
}
};

return assert(bemjson, html, templates, options);
});
});
});

function bhWrap(str) {
return 'module.exports = function(bh) {' + str + '};';
}

function assert(bemjson, html, templates, options) {
function build(templates, options, lib) {
var scheme = {
blocks: {},
bundle: {}
},
bundle, fileList;

if (options && options.bhFile) {
scheme['bh.js'] = options.bhFile;
options.bhFile = 'bh.js';
}

templates && templates.forEach(function (item, i) {
scheme.blocks['block-' + i + '.bh.js'] = bhWrap(item);
});

scheme[bhCoreFilename] = fs.readFileSync(bhCoreFilename, 'utf-8');

mock(scheme);

bundle = new TestNode('bundle');
fileList = new FileList();
fileList.loadFromDirSync('blocks');
bundle.provideTechData('?.files', fileList);

return bundle.runTechAndRequire(Tech, options)
.spread(function (BH) {
return bundle.runTech(Tech, options)
.spread(function () {
var filename = path.resolve('bundle', 'bundle.bh.js'),
contents = [
lib,
fs.readFileSync(filename, 'utf-8')
].join(EOL);

fs.writeFileSync(filename, contents);

dropRequireCache(require, filename);

return require(filename);
});
}

function assert(bemjson, html, templates, options) {
return build(templates, options)
.then(function (BH) {
BH.apply(bemjson).must.be(html);

options && options.mimic && [].concat(options.mimic).forEach(function (name) {
Expand Down
16 changes: 11 additions & 5 deletions test/techs/bh-bundle/browser--global.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,27 @@ describe('bh-bundle --browser --global-scope', function () {
});
});

it('dependencies', function () {
it('must get dependency from global scope', function () {
var test = generateTest({ block: 'block' }, '<div class="block">^_^</div>'),
options = {
dependencies: { test: '"^_^"' }
requires: {
depend: {
globals: 'depend'
}
}
},
template = 'bh.match("block", function(ctx) { ctx.content(bh.lib.test); });';
template = 'bh.match("block", function(ctx) { ctx.content(bh.lib.depend); });',
lib = 'var depend = "^_^";';

return runTest(test, options, template);
return runTest(test, options, template, lib);
});
});

function bhWrap(str) {
return 'module.exports = function(bh) {' + str + '};';
}

function runTest(testContent, options, template) {
function runTest(testContent, options, template, lib) {
var bhTemplate = bhWrap(template || 'bh.match("block", function(ctx) { ctx.tag("a"); });'),
bundle,
fileList,
Expand All @@ -90,6 +95,7 @@ function runTest(testContent, options, template) {
}

scheme[bhCoreFilename] = fs.readFileSync(bhCoreFilename, 'utf-8');
scheme['some-lib.js'] = lib || '';

mock(scheme);

Expand Down
37 changes: 29 additions & 8 deletions test/techs/bh-bundle/browser--ym.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,36 @@ describe('bh-bundle --browser --ym', function () {
});
});

it('dependencies', function () {
var test = generateTest({ block: 'block' }, '<div class="block">^_^</div>'),
options = {
dependencies: { A: 'A' }
},
template = 'bh.match("block", function(ctx) { ctx.content(bh.lib.A); });',
lib = 'modules.define("A", function (provide) { provide("^_^"); });';
describe('requires', function () {
it('must get dependency from global scope', function () {
var test = generateTest({ block: 'block' }, '<div class="block">^_^</div>'),
options = {
requires: {
depend: {
globals: 'depend'
}
}
},
template = 'bh.match("block", function(ctx) { ctx.content(bh.lib.depend); });',
lib = 'var depend = "^_^";';

return runTest(test, options, template, lib);
});

return runTest(test, options, template, lib);
it('must require depend from ym', function () {
var test = generateTest({ block: 'block' }, '<div class="block">^_^</div>'),
options = {
requires: {
depend: {
ym: 'depend'
}
}
},
template = 'bh.match("block", function(ctx) { ctx.content(bh.lib.depend); });',
lib = 'modules.define("depend", function (provide) { provide("^_^"); });';

return runTest(test, options, template, lib);
});
});
});

Expand Down

0 comments on commit a2b4f51

Please sign in to comment.