miki-template 1.3.3 → 1.3.7
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/.eslintrc.json +16 -0
- package/.github/release-notes/v1.3.1.md +55 -55
- package/.github/release-notes/v1.3.3.md +77 -0
- package/.github/workflows/ci.yml +38 -54
- package/.github/workflows/release.yml +106 -0
- package/AGENT.md +71 -71
- package/API_REFERENCE.md +314 -314
- package/CHANGELOG.md +173 -169
- package/CODE_OF_CONDUCT.md +14 -14
- package/CONTRIBUTING.md +27 -27
- package/README.md +342 -321
- package/ROADMAP.md +40 -40
- package/benchmarks/report.json +16 -16
- package/benchmarks/run.js +49 -49
- package/benchmarks/stress.mjs +647 -647
- package/benchmarks/templates/large.dtpl +7 -7
- package/benchmarks/templates/medium.dtpl +3 -3
- package/benchmarks/templates/small.dtpl +7 -7
- package/context/component.md +109 -109
- package/context/prd.md +131 -131
- package/context/project-structure.md +33 -33
- package/dir/base.html +22 -22
- package/dir/cmpnt.html +10 -10
- package/dir/footer.html +2 -2
- package/dir/home.html +80 -80
- package/dir/index.html +80 -0
- package/dir/navbar.html +8 -8
- package/docs/README.md +18 -18
- package/docs/advanced_usage.md +71 -71
- package/docs/api.md +119 -119
- package/docs/filters.md +708 -708
- package/docs/installation.md +106 -106
- package/docs/overview.md +79 -57
- package/docs/partialdef.md +70 -70
- package/docs/security.md +27 -27
- package/docs/tags.md +673 -673
- package/docs/usage.md +646 -646
- package/eslint.config.mjs +42 -42
- package/ex.mjs +34 -32
- package/live-test/package-lock.json +915 -0
- package/live-test/package.json +9 -0
- package/live-test/server.js +14 -0
- package/live-test/views/base.html +8 -0
- package/live-test/views/child.html +7 -0
- package/live-test/views/index.html +1 -0
- package/miki-template-extension/.github/workflows/ci.yml +116 -116
- package/miki-template-extension/.vscodeignore +7 -7
- package/miki-template-extension/CHANGELOG.md +99 -99
- package/miki-template-extension/LICENSE +21 -21
- package/miki-template-extension/README.md +273 -273
- package/miki-template-extension/extension.js +1013 -1013
- package/miki-template-extension/icon.svg +10 -10
- package/miki-template-extension/package.json +280 -280
- package/miki-template-extension/snippets/miki-template.json +717 -717
- package/miki-template-extension/syntaxes/language-configuration.json +114 -114
- package/miki-template-extension/syntaxes/miki-template.tmLanguage.json +355 -355
- package/miki-template-extension/tests/grammar-tests.json +162 -162
- package/miki-template-extension/tests/run-grammar-tests.js +82 -82
- package/package.json +40 -34
- package/sample-app/package-lock.json +901 -0
- package/sample-app/package.json +9 -0
- package/sample-app/server.js +14 -0
- package/sample-app/views/index.html +1 -0
- package/scripts/build-vsix.js +129 -129
- package/scripts/build-vsix.ps1 +15 -15
- package/snippets/miki-template.json +177 -177
- package/src/asyncRender.js +20 -20
- package/src/cache.js +80 -80
- package/src/context.js +126 -126
- package/src/context_processors.js +48 -48
- package/src/esm.mjs +89 -84
- package/src/filters.js +975 -975
- package/src/i18n.js +171 -171
- package/src/index.js +1112 -940
- package/src/lexer.js +114 -114
- package/src/libraries.js +371 -371
- package/src/parser.js +270 -270
- package/src/security.js +53 -53
- package/src/tags/control.js +719 -719
- package/src/tags/extra.js +154 -154
- package/src/tags/helpers.js +26 -26
- package/src/tags/i18n.js +256 -256
- package/src/tags/inheritance.js +335 -335
- package/src/tags/registry.js +18 -18
- package/src/tags/util.js +400 -400
- package/src/types.d.ts +107 -107
- package/syntaxes/language-configuration.json +26 -26
- package/syntaxes/miki-template.tmLanguage.json +146 -146
- package/tests/asyncRender.test.js +17 -17
- package/tests/base.html +6 -6
- package/tests/child.html +3 -3
- package/tests/context_processors.test.js +13 -13
- package/tests/esm.test.mjs +61 -61
- package/tests/filters.test.js +254 -254
- package/tests/finder-appdirs.test.js +19 -0
- package/tests/finder.test.js +17 -0
- package/tests/fixtures/views/nested/index.html +1 -0
- package/tests/fixtures/views/partial.html +1 -0
- package/tests/fixtures/views/sub/deepfile.html +1 -0
- package/tests/fixtures/views-appdirs/product/site/detail.html +1 -0
- package/tests/include_security.test.js +9 -9
- package/tests/integration/README.md +32 -32
- package/tests/integration/features.test.cjs +1681 -1681
- package/tests/integration/features.test.mjs +1697 -1697
- package/tests/integration/finder.esm.test.mjs +13 -0
- package/tests/integration/templates/base.miki +6 -6
- package/tests/integration/templates/child.miki +6 -6
- package/tests/integration/templates/index.html +17 -17
- package/tests/lexer.test.js +45 -45
- package/tests/parser.test.js +57 -57
- package/tests/partial.html +1 -1
- package/tests/partialdef.test.js +79 -79
- package/tests/production_checks.js +57 -57
- package/tests/security.test.js +28 -28
- package/tests/tags.test.js +233 -233
package/src/lexer.js
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lexer for tokenizing Django-style templates.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Token types:
|
|
7
|
-
* - 'text': Raw template content.
|
|
8
|
-
* - 'var': Variable interpolation, e.g. {{ user.name }}.
|
|
9
|
-
* - 'block': Structural tag block, e.g. {% if user.is_active %}.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
function tokenize(template) {
|
|
13
|
-
if (typeof template !== 'string') {
|
|
14
|
-
throw new TypeError('Template must be a string');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const tokens = [];
|
|
18
|
-
let i = 0;
|
|
19
|
-
const len = template.length;
|
|
20
|
-
let inVerbatim = false;
|
|
21
|
-
let verbatimBuffer = [];
|
|
22
|
-
|
|
23
|
-
while (i < len) {
|
|
24
|
-
// Detect the start of a variable, block, or comment.
|
|
25
|
-
if (template[i] === '{' && i + 1 < len) {
|
|
26
|
-
const next = template[i + 1];
|
|
27
|
-
if (next === '{' || next === '%' || next === '#') {
|
|
28
|
-
// Find the matching closer, accounting for nested {{ }} and {% %}
|
|
29
|
-
// so expressions like {{ x|default:"{{ y }}" }} parse correctly.
|
|
30
|
-
const isVar = next === '{';
|
|
31
|
-
const isBlock = next === '%';
|
|
32
|
-
const isComment = next === '#';
|
|
33
|
-
const closer = isVar ? '}}' : (isBlock ? '%}' : '#}');
|
|
34
|
-
let j = i + 2;
|
|
35
|
-
let depth = 1;
|
|
36
|
-
while (j < len && depth > 0) {
|
|
37
|
-
if (isVar && template[j] === '{' && template[j + 1] === '{') {
|
|
38
|
-
depth++;
|
|
39
|
-
j += 2;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (isBlock && template[j] === '{' && template[j + 1] === '%') {
|
|
43
|
-
depth++;
|
|
44
|
-
j += 2;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (template.substr(j, closer.length) === closer) {
|
|
48
|
-
depth--;
|
|
49
|
-
if (depth === 0) break;
|
|
50
|
-
j += closer.length;
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
j++;
|
|
54
|
-
}
|
|
55
|
-
if (depth !== 0) {
|
|
56
|
-
throw new Error(`Unclosed template tag at position ${i}`);
|
|
57
|
-
}
|
|
58
|
-
const raw = template.slice(i, j + closer.length);
|
|
59
|
-
const inner = template.slice(i + 2, j).trim();
|
|
60
|
-
|
|
61
|
-
if (inVerbatim) {
|
|
62
|
-
if (isBlock && inner === 'endverbatim') {
|
|
63
|
-
inVerbatim = false;
|
|
64
|
-
if (verbatimBuffer.length > 0) {
|
|
65
|
-
tokens.push({ type: 'text', content: verbatimBuffer.join('') });
|
|
66
|
-
verbatimBuffer = [];
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
69
|
-
verbatimBuffer.push(raw);
|
|
70
|
-
}
|
|
71
|
-
} else if (isComment) {
|
|
72
|
-
// Comments are ignored
|
|
73
|
-
} else if (isVar) {
|
|
74
|
-
tokens.push({ type: 'var', content: inner, raw });
|
|
75
|
-
} else {
|
|
76
|
-
// Block tag
|
|
77
|
-
if (inner === 'verbatim') {
|
|
78
|
-
inVerbatim = true;
|
|
79
|
-
} else {
|
|
80
|
-
tokens.push({ type: 'block', content: inner, raw });
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
i = j + closer.length;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Plain text — collect until the next special delimiter.
|
|
89
|
-
let textStart = i;
|
|
90
|
-
while (i < len) {
|
|
91
|
-
if (template[i] === '{' && i + 1 < len) {
|
|
92
|
-
const nx = template[i + 1];
|
|
93
|
-
if (nx === '{' || nx === '%' || nx === '#') break;
|
|
94
|
-
}
|
|
95
|
-
i++;
|
|
96
|
-
}
|
|
97
|
-
const text = template.slice(textStart, i);
|
|
98
|
-
if (inVerbatim) {
|
|
99
|
-
verbatimBuffer.push(text);
|
|
100
|
-
} else if (text !== '') {
|
|
101
|
-
tokens.push({ type: 'text', content: text });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (inVerbatim && verbatimBuffer.length > 0) {
|
|
106
|
-
tokens.push({ type: 'text', content: verbatimBuffer.join('') });
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return tokens;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
module.exports = {
|
|
113
|
-
tokenize
|
|
114
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Lexer for tokenizing Django-style templates.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Token types:
|
|
7
|
+
* - 'text': Raw template content.
|
|
8
|
+
* - 'var': Variable interpolation, e.g. {{ user.name }}.
|
|
9
|
+
* - 'block': Structural tag block, e.g. {% if user.is_active %}.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
function tokenize(template) {
|
|
13
|
+
if (typeof template !== 'string') {
|
|
14
|
+
throw new TypeError('Template must be a string');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const tokens = [];
|
|
18
|
+
let i = 0;
|
|
19
|
+
const len = template.length;
|
|
20
|
+
let inVerbatim = false;
|
|
21
|
+
let verbatimBuffer = [];
|
|
22
|
+
|
|
23
|
+
while (i < len) {
|
|
24
|
+
// Detect the start of a variable, block, or comment.
|
|
25
|
+
if (template[i] === '{' && i + 1 < len) {
|
|
26
|
+
const next = template[i + 1];
|
|
27
|
+
if (next === '{' || next === '%' || next === '#') {
|
|
28
|
+
// Find the matching closer, accounting for nested {{ }} and {% %}
|
|
29
|
+
// so expressions like {{ x|default:"{{ y }}" }} parse correctly.
|
|
30
|
+
const isVar = next === '{';
|
|
31
|
+
const isBlock = next === '%';
|
|
32
|
+
const isComment = next === '#';
|
|
33
|
+
const closer = isVar ? '}}' : (isBlock ? '%}' : '#}');
|
|
34
|
+
let j = i + 2;
|
|
35
|
+
let depth = 1;
|
|
36
|
+
while (j < len && depth > 0) {
|
|
37
|
+
if (isVar && template[j] === '{' && template[j + 1] === '{') {
|
|
38
|
+
depth++;
|
|
39
|
+
j += 2;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (isBlock && template[j] === '{' && template[j + 1] === '%') {
|
|
43
|
+
depth++;
|
|
44
|
+
j += 2;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (template.substr(j, closer.length) === closer) {
|
|
48
|
+
depth--;
|
|
49
|
+
if (depth === 0) break;
|
|
50
|
+
j += closer.length;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
j++;
|
|
54
|
+
}
|
|
55
|
+
if (depth !== 0) {
|
|
56
|
+
throw new Error(`Unclosed template tag at position ${i}`);
|
|
57
|
+
}
|
|
58
|
+
const raw = template.slice(i, j + closer.length);
|
|
59
|
+
const inner = template.slice(i + 2, j).trim();
|
|
60
|
+
|
|
61
|
+
if (inVerbatim) {
|
|
62
|
+
if (isBlock && inner === 'endverbatim') {
|
|
63
|
+
inVerbatim = false;
|
|
64
|
+
if (verbatimBuffer.length > 0) {
|
|
65
|
+
tokens.push({ type: 'text', content: verbatimBuffer.join('') });
|
|
66
|
+
verbatimBuffer = [];
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
verbatimBuffer.push(raw);
|
|
70
|
+
}
|
|
71
|
+
} else if (isComment) {
|
|
72
|
+
// Comments are ignored
|
|
73
|
+
} else if (isVar) {
|
|
74
|
+
tokens.push({ type: 'var', content: inner, raw });
|
|
75
|
+
} else {
|
|
76
|
+
// Block tag
|
|
77
|
+
if (inner === 'verbatim') {
|
|
78
|
+
inVerbatim = true;
|
|
79
|
+
} else {
|
|
80
|
+
tokens.push({ type: 'block', content: inner, raw });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
i = j + closer.length;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Plain text — collect until the next special delimiter.
|
|
89
|
+
let textStart = i;
|
|
90
|
+
while (i < len) {
|
|
91
|
+
if (template[i] === '{' && i + 1 < len) {
|
|
92
|
+
const nx = template[i + 1];
|
|
93
|
+
if (nx === '{' || nx === '%' || nx === '#') break;
|
|
94
|
+
}
|
|
95
|
+
i++;
|
|
96
|
+
}
|
|
97
|
+
const text = template.slice(textStart, i);
|
|
98
|
+
if (inVerbatim) {
|
|
99
|
+
verbatimBuffer.push(text);
|
|
100
|
+
} else if (text !== '') {
|
|
101
|
+
tokens.push({ type: 'text', content: text });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (inVerbatim && verbatimBuffer.length > 0) {
|
|
106
|
+
tokens.push({ type: 'text', content: verbatimBuffer.join('') });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return tokens;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = {
|
|
113
|
+
tokenize
|
|
114
|
+
};
|