Skip to content

Commit 8e72e86

Browse files
authored
Merge pull request #11 from cho45/v2
V2: `with` の完全排除
2 parents cef5ae8 + 936082d commit 8e72e86

File tree

6 files changed

+316
-164
lines changed

6 files changed

+316
-164
lines changed

README.md

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,21 @@ import { template } from 'micro-template';
101101
template.get = function (id) { return require('fs').readFileSync('tmpl/' + id + '.tmpl', 'utf-8') };
102102
```
103103

104-
DEFINE DATA VARIABLE EXPLICITLY
105-
-------------------------------
104+
DEFINE DATA VARIABLE SCOPE
105+
----------------------------
106106

107-
By default, micro-template uses `with` syntax to expand data variables. This behavior is almost convenience, but if you want to expressly fast template function, you can do without `with` by specify `template.varible`.
107+
micro-template now always expands data variables as local variables in the template function. The template API only supports two arguments: the template source/id and the data object. All keys of the data object are available as local variables in the template code.
108108

109-
```js
110-
template.variable = 'tmpl';
109+
For example:
111110

112-
var func = template('aaa <% tmpl.foo %> bbb');
113-
var result = func({ foo : 'foo' });
111+
```js
112+
const result = template('aaa <% foo %> bbb', { foo: 'bar' });
113+
// result: 'aaa bar bbb'
114114
```
115115

116-
`template.variable` is used to data variable name in template code. And `with` syntax is not used any more. So you can't refer to variable without `tmpl.` prefix in this case.
116+
You can access all properties of the data object directly as variables inside the template.
117+
118+
**Note:** The previous API that allowed calling `template(tmpl)` to return a function is removed. Always use the two-argument form: `template(tmpl, data)`.
117119

118120
EXTENDED FEATURES
119121
-----------------
@@ -170,25 +172,44 @@ node:
170172
* node misc/benchmark.js
171173

172174
```log
173-
A larger number (count) means faster. A smaller number (msec) means faster.
174-
Linux (linux) x64 6.6.87.1-microsoft-standard-WSL2 13th Gen Intel(R) Core(TM) i7-13700K 24 cpus
175-
running... micro-template
176-
running... micro-template (escaped)
177-
running... micro-template (without `with`)
178-
running... John Resig's tmpl
179-
running... ejs.render
180-
running... ejs.render pre compiled
181-
=== result ===
182-
52736.3: (0.019 msec) micro-template (without `with`)
183-
6226.1: (0.161 msec) John Resig's tmpl
184-
4771: (0.21 msec) micro-template
185-
4424.8: (0.226 msec) micro-template (escaped)
186-
4322.8: (0.231 msec) ejs.render pre compiled
187-
3853.6: (0.26 msec) ejs.render
188-
node misc/benchmark.js 7.41s user 0.01s system 101% cpu 7.349 total
175+
> node --expose-gc ./misc/benchmark.js
176+
177+
clk: ~3.03 GHz
178+
cpu: Apple M1
179+
runtime: node 20.10.0 (arm64-darwin)
180+
181+
benchmark avg (min … max) p75 / p99 (min top 1%)
182+
------------------------------------------------- -------------------------------
183+
micro-template 24.54 µs/iter 23.04 µs
184+
(21.83 µs … 230.92 µs) 67.67 µs
185+
( 56.00 b … 361.68 kb) 146.04 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
186+
187+
micro-template (template.variable) 24.65 µs/iter 24.65 µs █
188+
(24.52 µs … 25.17 µs) 24.75 µs ▅▅█ ▅█ ▅ ▅▅ ▅
189+
(945.85 b … 952.58 b) 946.55 b ███▁▁▁██▁█▁██▁▁▁▁▁▁▁█
190+
191+
ejs.render pre compiled 342.25 µs/iter 347.42 µs
192+
(328.13 µs … 617.17 µs) 384.12 µs ▃
193+
( 11.10 kb … 645.10 kb) 72.46 kb ▂███▆▆▄▄▅▄▃▄▃▂▂▂▂▁▁▁▁
194+
195+
John Resig's tmpl 222.23 µs/iter 229.08 µs
196+
(207.71 µs … 428.33 µs) 267.04 µs
197+
( 26.91 kb … 438.41 kb) 101.27 kb ▃▄██▃▃▃▃▄▄▄▃▂▂▁▁▁▁▁▁▁
198+
199+
┌ ┐
200+
micro-template ┤ 24.54 s
201+
micro-template (template.variable) ┤ 24.65 s
202+
ejs.render pre compiled ┤■■■■■■■■■■■■■■■■■■■■■s ■■■■■■■■■■■■ 342.25 µ
203+
John Resig's tmpl ┤■■■■■■■■■■■■■■ s■■■■■■ 222.23 µ
204+
└ ┘
205+
206+
summary
207+
micro-template
208+
1x faster than micro-template (template.variable)
209+
9.06x faster than John Resig's tmpl
210+
13.95x faster than ejs.render pre compiled
189211
```
190212

191-
192213
LICENSE
193214
-------
194215

lib/micro-template.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
* (c) cho45 http://cho45.github.com/mit-license
44
*/
55
const template = function (id, data) {
6-
const me = template;
7-
if (!me.cache[id]) me.cache[id] = (function () {
6+
if (arguments.length < 2) throw new Error('template() must be called with (template, data)');
7+
const me = template, keys = Object.keys(data || {}), key = `data:${id}:${keys.join(':')}`;
8+
if (!me.cache.has(key)) me.cache.set(key, (function () {
89
let name = id, string = /^[\w\-]+$/.test(id) ? me.get(id): (name = 'template(string)', id); // no warnings
910
let line = 1;
1011
const body = (
1112
`try { ` +
12-
(me.variable ? `let ${me.variable} = __this.stash;` : `with (__this.stash) { /*start*/`) +
13+
(me.variable ? `let ${me.variable} = __this.stash;` : ``) +
1314
string.trim().split(/(<%.+?%>)/g).map(part =>
1415
part.startsWith('<%=raw') && part.endsWith('%>') ? `/*raw*/__this.ret+=(${part.slice(6, -2)});` :
1516
part.startsWith('<%=') && part.endsWith('%>') ? `/*=*/__this.ret+=__this.escapeHTML(${part.slice(3, -2)});` :
@@ -21,18 +22,18 @@ const template = function (id, data) {
2122
part ? `/*+*/__this.ret+='${part}';` : ''
2223
)
2324
).join('') +
24-
`/*end*/ ${me.variable ? "" : "}"} return __this.ret;` +
25+
`/*end*/ return __this.ret;` +
2526
`} catch (e) { throw new Error('TemplateError: ' + e + ' (on ${name} line ' + __this.line + ')'); }` +
2627
`//@ sourceURL=template.js\n`
2728
);
28-
const func = new Function("__this", body);
29+
const func = new Function("__this", ...keys, body);
2930
const map = { '&' : '&amp;', '<' : '&lt;', '>' : '&gt;', '\x22' : '&#x22;', '\x27' : '&#x27;' };
3031
const escapeHTML = function (string) { return (''+string).replace(/[&<>\'\"]/g, function (_) { return map[_] }) };
31-
return function (stash) { return func.call(null, me.context = { escapeHTML: escapeHTML, line: 1, ret : '', stash: stash }) };
32-
})();
33-
return data ? me.cache[id](data) : me.cache[id];
32+
return function (stash, ...args) { return func.call(null, me.context = { escapeHTML: escapeHTML, line: 1, ret : '', stash: stash }, ...args) };
33+
})());
34+
return me.cache.get(key)(data, ...keys.map(key => data[key]));
3435
}
35-
template.cache = {};
36+
template.cache = new Map();
3637
template.get = function (id) { return document.getElementById(id).innerHTML };
3738

3839
/**
@@ -71,7 +72,7 @@ function extended (id, data) {
7172
return template(id, data);
7273
};
7374

74-
return data ? fun(data) : fun;
75+
return fun(data);
7576
}
7677

7778
template.get = function (id) {

misc/benchmark.js

Lines changed: 21 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import assert from 'assert';
55
import { template } from '../lib/micro-template.js';
66
import ejs from 'ejs';
77
import fs from 'fs';
8-
import os from 'os';
8+
import { bench, run, summary, barplot } from 'mitata';
99

1010
// ============================================================================
1111
// Simple JavaScript Templating
@@ -45,10 +45,8 @@ import os from 'os';
4545
}).call(global);
4646
// ============================================================================
4747
const fizzbuzz = fs.readFileSync('test/data-fizzbuzz.tmpl', 'utf-8');
48-
const fizzbuzzRaw1 = fizzbuzz.replace(/<%=/g, '<%=raw');
49-
const fizzbuzzRaw2 = fizzbuzz.replace(/<%=/g, '<%-');
5048
const fizzbuzzVar = fizzbuzz.replace(/^/, '<% var n = stash.n; %>');
51-
const ejsFunc = ejs.compile(fizzbuzzRaw2);
49+
const ejsFunc = ejs.compile(fizzbuzz);
5250

5351
const output1 = template(fizzbuzz, {n: 30}).replace(/\s+/g, ' ');
5452
const output2 = ejsFunc({n: 30 }).replace(/\s+/g, ' ');
@@ -57,61 +55,25 @@ const output3 = template(fizzbuzzVar, {n: 30}).replace(/\s+/g, ' ');
5755
assert.equal(output1, output2, 'output should be same');
5856
assert.equal(output1, output3, 'output should be same');
5957

60-
benchmark({
61-
"micro-template" : function () {
62-
template.variable = null;
63-
template(fizzbuzzRaw1, {n : 300 });
64-
},
65-
"micro-template (escaped)" : function () {
66-
template.variable = null;
67-
template(fizzbuzz, {n : 300 });
68-
},
69-
"micro-template (without `with`)" : function () {
70-
template.variable = 'stash';
71-
template(fizzbuzzVar, {n : 300 });
72-
},
73-
"John Resig's tmpl" : function () {
74-
tmpl(fizzbuzz, {n : 300 });
75-
},
76-
"ejs.render": function () {
77-
ejs.render(fizzbuzzRaw2, {n : 300 });
78-
},
79-
"ejs.render pre compiled": function () {
80-
ejsFunc({n : 300});
81-
}
82-
});
83-
84-
85-
// ============================================================================
86-
// try n counts in 1sec
87-
function measure (fun) {
88-
for (let i = 0; i < 1000; i++) fun(); // warm up
58+
barplot(() => {
59+
summary(() => {
60+
bench('micro-template', () => {
61+
template.variable = null;
62+
template(fizzbuzz, { n: 300 });
63+
});
64+
bench('micro-template (template.variable)', () => {
65+
template.variable = 'stash';
66+
template(fizzbuzzVar, { n: 300 });
67+
});
68+
bench('ejs.render pre compiled', () => {
69+
ejsFunc({ n: 300 });
70+
});
71+
bench("John Resig's tmpl", () => {
72+
tmpl(fizzbuzz, {n : 300 });
73+
});
8974

90-
let now, count = 0, n = 500;
91-
const start = new Date().getTime();
92-
do {
93-
for (let i = 0; i < n; i++) fun();
94-
count += n;
95-
now = new Date().getTime();
96-
} while ( (now - start) < 1000);
97-
return (count / (now - start)) * 1000;
98-
}
99-
100-
function benchmark (funcs) {
101-
console.log(' A larger number (count) means faster. A smaller number (msec) means faster.');
102-
console.log('%s (%s) %s %s %s %d cpus', os.type(), os.platform(), os.arch(), os.release(), os.cpus()[0].model, os.cpus().length);
103-
// console.log(os.cpus());
104-
105-
const result = [];
106-
for (const key of Object.keys(funcs)) {
107-
console.log('running... %s', key);
108-
result.push({ name : key, counts : measure(funcs[key]) });
109-
}
110-
result.sort(function (a, b) { return b.counts - a.counts });
75+
});
76+
});
11177

112-
console.log('=== result ===');
78+
await run();
11379

114-
for (var i = 0, it; (it = result[i]); i++) {
115-
console.log("%d: (%d msec) %s", it.counts.toFixed(1), (1000 / it.counts).toFixed(3), it.name);
116-
}
117-
}

0 commit comments

Comments
 (0)