Skip to content

Commit eaed402

Browse files
committed
[ Add ] Template evaluator
1 parent 6b3687a commit eaed402

File tree

6 files changed

+259
-6
lines changed

6 files changed

+259
-6
lines changed

package-lock.json

Lines changed: 117 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dom-renderer",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Template engine based on HTML 5 & ECMAScript 6",
55
"keywords": [
66
"template",
@@ -29,7 +29,7 @@
2929
"lint": "eslint source/ test/ --fix",
3030
"pack": "amd-bundle source/index dist/dom-renderer -m",
3131
"build": "npm run format && npm run lint && npm run pack",
32-
"debug": "npm run build && mocha --inspect-brk",
32+
"debug": "npm run pack && mocha --inspect-brk",
3333
"test": "npm run build && mocha --exit && esdoc",
3434
"prepublishOnly": "npm test",
3535
"help": "esdoc && web-server docs/ -o"
@@ -57,7 +57,9 @@
5757
"koapache": "^1.0.5",
5858
"mocha": "^5.2.0",
5959
"prettier": "^1.15.3",
60-
"should": "^13.2.3"
60+
"should": "^13.2.3",
61+
"should-sinon": "0.0.6",
62+
"sinon": "^7.2.3"
6163
},
6264
"babel": {
6365
"presets": [

source/Template.js

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,88 @@
1-
export default class Template extends Array {}
1+
const template_raw = Symbol('Template raw'),
2+
template_scope = Symbol('Template scope'),
3+
template_value = Symbol('Template value');
24

3-
Template.Expression = /\$\{([^}]+)\}/;
5+
export default class Template extends Array {
6+
/**
7+
* @param {String} raw
8+
* @param {String[]} [scope=[]] - Names of Scoped varibles
9+
* @param {function(value: *, old: *): void} onChange
10+
*/
11+
constructor(raw, scope, onChange) {
12+
Object.assign(super(), {
13+
[template_raw]: raw,
14+
[template_scope]: scope || [],
15+
[template_value]: null,
16+
onChange: onChange instanceof Function && onChange
17+
});
18+
19+
this.parse(), this.reset();
20+
}
21+
22+
valueOf() {
23+
return this[template_value];
24+
}
25+
26+
toString() {
27+
return this[template_value] + '';
28+
}
29+
30+
/**
31+
* @param {String} expression - JavaScript expression
32+
*
33+
* @return {Function}
34+
*/
35+
evaluatorOf(expression) {
36+
return new (Function.bind.apply(
37+
Function,
38+
[null].concat(this[template_scope], `return (${expression});`)
39+
))();
40+
}
41+
42+
/**
43+
* @private
44+
*/
45+
parse() {
46+
this[template_raw] = this[template_raw].replace(
47+
Template.Expression,
48+
(_, expression) =>
49+
'${' + (this.push(this.evaluatorOf(expression)) - 1) + '}'
50+
);
51+
}
52+
53+
/**
54+
* @param {?Object} context - `this` in the expression
55+
* @param {...Object} [scope] - Scoped varible objects
56+
*
57+
* @return {*}
58+
*/
59+
evaluate(context, ...scope) {
60+
var value = this[1]
61+
? this[template_raw].replace(/\$\{(\d+)\}/g, (_, index) => {
62+
const value = this[index].apply(context, scope);
63+
64+
return value != null ? value : '';
65+
})
66+
: this[0].apply(context, scope);
67+
68+
if (this[template_value] !== value) {
69+
if (this.onChange) this.onChange(value, this[template_value]);
70+
71+
this[template_value] = value;
72+
}
73+
74+
return value;
75+
}
76+
77+
/**
78+
* @return {*} {@link Template#evaluate}
79+
*/
80+
reset() {
81+
return this.evaluate.apply(
82+
this,
83+
Array(this[template_scope].length + 1).fill({})
84+
);
85+
}
86+
}
87+
88+
Template.Expression = /\$\{([^}]+)\}/g;

source/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export * from './utility';
2+
3+
export { default as Template } from './Template';

test/Template.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { spy } from 'sinon';
2+
3+
import Template from '../source/Template';
4+
5+
var onChange = spy(),
6+
template;
7+
8+
describe('Template evaluator', () => {
9+
/**
10+
* @test {Template#evaluatorOf}
11+
*/
12+
it('Parsing', () => {
13+
template = new Template(
14+
'test${this.test},example${view.example}',
15+
['view'],
16+
onChange
17+
);
18+
19+
template.should.have.length(2);
20+
21+
template[0].should.be.instanceOf(Function);
22+
23+
onChange.should.be.calledWith('test,example', null);
24+
});
25+
26+
/**
27+
* @test {Template#reset}
28+
*/
29+
it('No changing', () => {
30+
template.reset();
31+
32+
onChange.should.be.calledOnce();
33+
});
34+
35+
/**
36+
* @test {Template#evaluate}
37+
*/
38+
it('Evaluating', () => {
39+
template.valueOf().should.be.equal('test,example');
40+
41+
template
42+
.evaluate({ test: 1 }, { example: 2 })
43+
.should.be.equal('test1,example2');
44+
45+
onChange.should.be.calledWith('test1,example2', 'test,example');
46+
});
47+
});

test/mocha.opts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
--require @babel/polyfill
33
--require ./source/DOM-polyfill
44
--require should
5+
--require should-sinon
56
--recursive
67
--colors
78
--inline-diffs

0 commit comments

Comments
 (0)