Skip to content

Commit

Permalink
compiler: support adding templates at runtime
Browse files Browse the repository at this point in the history
Fix: #46
  • Loading branch information
indutny committed Jul 23, 2015
1 parent fccd9d4 commit 9b547cd
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 10 deletions.
10 changes: 8 additions & 2 deletions lib/bemhtml/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ Compiler.prototype.wrap = function wrap(code) {
};

Compiler.prototype.generate = function generate(code) {
var locals = bemhtml.runtime.locals.join(', ');
// It is fine to compile without templates at first
if (!code)
code = '';

// Yeah, let people pass functions to us!
if (typeof code === 'function')
code = code.toString().replace(/^function\s*\(\)\s*{|}$/g, '');

var source = '/// -------------------------------------\n' +
'/// --------- BEM-XJST Runtime Start ----\n' +
Expand All @@ -64,7 +70,7 @@ Compiler.prototype.generate = function generate(code) {
'/// -------------------------------------\n' +
'/// ------ BEM-XJST User-code Start -----\n' +
'/// -------------------------------------\n' +
'api.compile(function(' + locals + ') {\n' +
'api.compile(function() {\n' +
code + ';\n' +
'});\n' +
'api.exportApply(exports);\n' +
Expand Down
27 changes: 25 additions & 2 deletions lib/bemhtml/runtime/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ function BEMHTML() {
this.entities = null;
this.defaultEnt = null;

// Current tree
this.tree = null;

// Current match
this.match = null;

Expand All @@ -36,7 +39,7 @@ module.exports = BEMHTML;

BEMHTML.locals = Tree.methods.concat('local', 'applyCtx', 'applyNext', 'apply');

BEMHTML.prototype.compile = function compile(templates) {
BEMHTML.prototype.compile = function compile(code) {
var self = this;

function applyCtx() {
Expand Down Expand Up @@ -70,6 +73,10 @@ BEMHTML.prototype.compile = function compile(templates) {
}
});

// Yeah, let people pass functions to us!
var templates = code.toString().replace(/^function[^{]+{|}$/g, '');
templates = new Function(BEMHTML.locals.join(', '), templates);

var out = tree.build(templates, [
localWrap,
applyCtxWrap,
Expand All @@ -81,6 +88,16 @@ BEMHTML.prototype.compile = function compile(templates) {
apply
]);

// Concatenate templates with existing ones
// TODO(indutny): it should be possible to incrementally add templates
if (this.tree) {
out = {
templates: out.templates.concat(this.tree.templates),
oninit: this.tree.oninit.concat(out.oninit)
};
}
this.tree = out;

// Group block+elem entities into a hashmap
var ent = this.groupEntities(out.templates);

Expand All @@ -94,7 +111,8 @@ BEMHTML.prototype.compile = function compile(templates) {
BEMHTML.prototype.groupEntities = function groupEntities(tree) {
var res = {};
for (var i = 0; i < tree.length; i++) {
var template = tree[i];
// Make sure to change only the copy, the original is cached in `this.tree`
var template = tree[i].clone();
var block = null;
var elem;

Expand Down Expand Up @@ -667,6 +685,11 @@ BEMHTML.prototype.exportApply = function exportApply(exports) {
return self.run(context || this);
};

// Add templates at run time
exports.compile = function compile(templates) {
return self.compile(templates);
};

var sharedContext = {};

exports.BEMContext = this.contextConstructor;
Expand Down
4 changes: 4 additions & 0 deletions lib/bemhtml/runtime/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ function Template(predicates, body) {
}
exports.Template = Template;

Template.prototype.clone = function clone() {
return new Template(this.predicates.slice(), this.body);
};

function MatchBase() {
}
exports.MatchBase = MatchBase;
Expand Down
7 changes: 1 addition & 6 deletions test/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@ var assert = require('assert');
var utile = require('utile');
var vm = require('vm');

function fn2str(fn) {
return fn.toString().replace(/^function\s*\(\)\s*{|}$/g, '');
}

function test(fn, data, expected, options) {
if (!options) options = {};

var body = fn2str(fn);
var template = bemxjst.compile(body, options)
var template = bemxjst.compile(fn, options)

if (options.flush) {
template._buf = [];
Expand Down
30 changes: 30 additions & 0 deletions test/runtime-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var assert = require('assert');
var fixtures = require('./fixtures');
var bemxjst = require('../');

var test = fixtures.test;

Expand Down Expand Up @@ -568,4 +569,33 @@ describe('BEMHTML compiler/Runtime', function() {
'<div class="b3">#ya#</div>');
});
});

describe('adding templates at runtime', function() {
it('should work', function() {
var template = bemxjst.compile();

assert.equal(template.apply({ block: 'b1' }), '<div class="b1"></div>');

template.compile(function() {
block('b1').content()('ok');
});

assert.equal(template.apply({ block: 'b1' }), '<div class="b1">ok</div>');
assert.equal(template.apply({ block: 'b2' }), '<div class="b2"></div>');

template.compile(function() {
block('b2').content()('ok');
});

assert.equal(template.apply({ block: 'b1' }), '<div class="b1">ok</div>');
assert.equal(template.apply({ block: 'b2' }), '<div class="b2">ok</div>');

template.compile(function() {
block('b1').tag()('a');
});

assert.equal(template.apply({ block: 'b1' }), '<a class="b1">ok</a>');
assert.equal(template.apply({ block: 'b2' }), '<div class="b2">ok</div>');
});
});
});

0 comments on commit 9b547cd

Please sign in to comment.