miki-template 1.2.0 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/release-notes/v1.3.1.md +55 -0
- package/CHANGELOG.md +72 -0
- package/README.md +43 -26
- package/assets/banner.png +0 -0
- package/benchmarks/stress.mjs +647 -0
- package/dir/base.html +23 -0
- package/dir/cmpnt.html +11 -0
- package/dir/footer.html +3 -0
- package/dir/home.html +80 -0
- package/dir/navbar.html +9 -0
- package/docs/api.md +20 -3
- package/docs/filters.md +301 -133
- package/docs/partialdef.md +30 -1
- package/docs/tags.md +63 -0
- package/docs/usage.md +50 -3
- package/eslint.config.mjs +9 -1
- package/ex.mjs +33 -0
- package/miki-template-extension/.github/workflows/ci.yml +116 -0
- package/miki-template-extension/.vscodeignore +7 -0
- package/miki-template-extension/CHANGELOG.md +99 -0
- package/miki-template-extension/README.md +244 -53
- package/miki-template-extension/extension.js +1013 -0
- package/miki-template-extension/icon.png +0 -0
- package/miki-template-extension/miki-template-1.7.1.vsix +0 -0
- package/miki-template-extension/package.json +244 -10
- package/miki-template-extension/snippets/miki-template.json +612 -72
- package/miki-template-extension/syntaxes/language-configuration.json +101 -13
- package/miki-template-extension/syntaxes/miki-template.tmLanguage.json +270 -61
- package/miki-template-extension/tests/grammar-tests.json +162 -0
- package/miki-template-extension/tests/run-grammar-tests.js +82 -0
- package/package.json +7 -4
- package/scripts/build-vsix.js +129 -0
- package/scripts/build-vsix.ps1 +15 -0
- package/src/cache.js +41 -2
- package/src/context.js +9 -5
- package/src/context_processors.js +9 -2
- package/src/esm.mjs +12 -0
- package/src/filters.js +472 -24
- package/src/index.js +571 -85
- package/src/lexer.js +76 -54
- package/src/libraries.js +134 -3
- package/src/parser.js +22 -2
- package/src/security.js +4 -2
- package/src/tags/control.js +150 -21
- package/src/tags/extra.js +154 -0
- package/src/tags/i18n.js +49 -23
- package/src/tags/inheritance.js +142 -23
- package/src/tags/util.js +102 -24
- package/tests/esm.test.mjs +37 -2
- package/tests/filters.test.js +155 -0
- package/tests/integration/README.md +32 -0
- package/tests/integration/features.test.cjs +1681 -0
- package/tests/integration/features.test.mjs +1697 -0
- package/tests/integration/templates/base.miki +6 -0
- package/tests/integration/templates/child.miki +6 -0
- package/tests/integration/templates/index.html +17 -0
- package/tests/parser.test.js +5 -3
- package/tests/partialdef.test.js +40 -1
- package/tests/tags.test.js +30 -0
- package/miki-template-1.2.0.vsix +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>{{ title }}</title>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<h1>{{ heading }}</h1>
|
|
8
|
+
<ul>
|
|
9
|
+
{% for item in items %}
|
|
10
|
+
<li>{{ forloop.counter }}: {{ item|upper }}</li>
|
|
11
|
+
{% empty %}
|
|
12
|
+
<li>No items</li>
|
|
13
|
+
{% endfor %}
|
|
14
|
+
</ul>
|
|
15
|
+
<p>User: {{ user.name|title }}</p>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
package/tests/parser.test.js
CHANGED
|
@@ -30,19 +30,21 @@ test('Context - basic dotted lookups', () => {
|
|
|
30
30
|
assert.strictEqual(ctx.get('user.profile.name'), 'Miki');
|
|
31
31
|
assert.strictEqual(ctx.get('user.profile.age'), 30);
|
|
32
32
|
assert.strictEqual(ctx.get('items.1'), 'banana');
|
|
33
|
-
|
|
33
|
+
// Missing key now returns undefined (so filters like default_if_none
|
|
34
|
+
// can detect missing values) instead of empty string.
|
|
35
|
+
assert.strictEqual(ctx.get('user.profile.missing'), undefined);
|
|
34
36
|
});
|
|
35
37
|
|
|
36
38
|
test('Context - scope pushing and popping', () => {
|
|
37
39
|
const ctx = new Context({ name: 'Global' });
|
|
38
40
|
ctx.push({ name: 'Local', age: 25 });
|
|
39
|
-
|
|
41
|
+
|
|
40
42
|
assert.strictEqual(ctx.get('name'), 'Local');
|
|
41
43
|
assert.strictEqual(ctx.get('age'), 25);
|
|
42
44
|
|
|
43
45
|
ctx.pop();
|
|
44
46
|
assert.strictEqual(ctx.get('name'), 'Global');
|
|
45
|
-
assert.strictEqual(ctx.get('age'),
|
|
47
|
+
assert.strictEqual(ctx.get('age'), undefined);
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
test('Context - automatic function calling', () => {
|
package/tests/partialdef.test.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// tests for partialdef support, including conditionals, loops, and inline rendering
|
|
2
2
|
const test = require('node:test');
|
|
3
3
|
const assert = require('node:assert');
|
|
4
|
-
const
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const { render, compile, renderPartialFromSource } = require('../src/index');
|
|
5
8
|
|
|
6
9
|
// Basic partialdef rendering via {% partial %}
|
|
7
10
|
test('Partialdef basic rendering', () => {
|
|
@@ -38,3 +41,39 @@ test('renderPartial API works', () => {
|
|
|
38
41
|
const out = compiled.renderPartial('api', { data: 123 });
|
|
39
42
|
assert.strictEqual(out, 'API 123');
|
|
40
43
|
});
|
|
44
|
+
|
|
45
|
+
// Bug fix: partials defined inside for loops are found by renderPartialFromSource
|
|
46
|
+
test('renderPartialFromSource finds partials inside for loops', () => {
|
|
47
|
+
const tpl = `{% for item in items %}{% partialdef loop_item %}{{ item }}{% endpartialdef %}{% endfor %}`;
|
|
48
|
+
const out = renderPartialFromSource(tpl, 'loop_item', { item: 'X' });
|
|
49
|
+
assert.strictEqual(out, 'X');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Bug fix: include with partial selector does not leak other partials from included file
|
|
53
|
+
test('include with partial selector does not leak other partials', () => {
|
|
54
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'miki-partial-'));
|
|
55
|
+
fs.writeFileSync(path.join(dir, 'main.html'), `{% include "frag.html#only_me" %}`);
|
|
56
|
+
fs.writeFileSync(path.join(dir, 'frag.html'), `{% partialdef only_me %}ME{% endpartialdef %}{% partialdef secret %}SECRET{% endpartialdef %}`);
|
|
57
|
+
|
|
58
|
+
const mainTpl = fs.readFileSync(path.join(dir, 'main.html'), 'utf8');
|
|
59
|
+
assert.throws(
|
|
60
|
+
() => renderPartialFromSource(mainTpl, 'secret', {}, { views: dir }),
|
|
61
|
+
/Partial 'secret' not found/
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
fs.rmSync(dir, { recursive: true });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Bug fix: full include makes all partials from included file available
|
|
68
|
+
test('full include makes all included partials available', () => {
|
|
69
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'miki-partial-full-'));
|
|
70
|
+
fs.writeFileSync(path.join(dir, 'main.html'), `{% include "frag.html" %}`);
|
|
71
|
+
fs.writeFileSync(path.join(dir, 'frag.html'), `{% partialdef only_me %}ME{% endpartialdef %}{% partialdef secret %}SECRET{% endpartialdef %}`);
|
|
72
|
+
|
|
73
|
+
const mainTpl = fs.readFileSync(path.join(dir, 'main.html'), 'utf8');
|
|
74
|
+
const out = renderPartialFromSource(mainTpl, 'secret', {}, { views: dir });
|
|
75
|
+
assert.strictEqual(out, 'SECRET');
|
|
76
|
+
|
|
77
|
+
fs.rmSync(dir, { recursive: true });
|
|
78
|
+
});
|
|
79
|
+
|
package/tests/tags.test.js
CHANGED
|
@@ -201,3 +201,33 @@ test('Filters - strftime with date-fns', () => {
|
|
|
201
201
|
const result2 = strftime(new Date('2026-08-31T22:00:00'), 'HH:mm');
|
|
202
202
|
assert.ok(result2.startsWith('22:'));
|
|
203
203
|
});
|
|
204
|
+
|
|
205
|
+
test('Tags - now tag', () => {
|
|
206
|
+
const { render } = require('../src/index');
|
|
207
|
+
const result = render('{% now "yyyy-MM-dd" %}');
|
|
208
|
+
assert.ok(result.match(/^\d{4}-\d{2}-\d{2}$/));
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('Tags - set tag inline', () => {
|
|
212
|
+
const { render } = require('../src/index');
|
|
213
|
+
const result = render('{% set x = 42 %}{{ x }}');
|
|
214
|
+
assert.strictEqual(result, '42');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('Tags - set tag block form', () => {
|
|
218
|
+
const { render } = require('../src/index');
|
|
219
|
+
const result = render('{% set greeting %}Hello{% endset %}{{ greeting }}');
|
|
220
|
+
assert.strictEqual(result, 'Hello');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('Tags - ifchanged tag', () => {
|
|
224
|
+
const { render } = require('../src/index');
|
|
225
|
+
const result = render('{% for i in items %}{% ifchanged i %}{{ i }}{% endifchanged %}{% endfor %}', { items: [1, 1, 2, 2, 3] });
|
|
226
|
+
assert.strictEqual(result, '123');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('Tags - ifchanged with else', () => {
|
|
230
|
+
const { render } = require('../src/index');
|
|
231
|
+
const result = render('{% for i in items %}{% ifchanged i %}{{ i }}{% else %}*{% endifchanged %}{% endfor %}', { items: [1, 1, 2, 2, 3] });
|
|
232
|
+
assert.strictEqual(result, '1*2*3');
|
|
233
|
+
});
|
package/miki-template-1.2.0.vsix
DELETED
|
Binary file
|