ventojs 2.0.0-canary.0 → 2.0.0-canary.1
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/CHANGELOG.md +27 -2
- package/core/environment.js +85 -38
- package/core/errors.js +161 -13
- package/core/reserved.js +1 -0
- package/core/tokenizer.js +11 -5
- package/loaders/file.js +1 -1
- package/loaders/filesystem.js +36 -0
- package/loaders/memory.js +7 -7
- package/loaders/module.js +50 -0
- package/loaders/url.js +3 -9
- package/loaders/utils.js +17 -0
- package/package.json +26 -8
- package/plugins/auto_trim.js +2 -1
- package/plugins/echo.js +2 -6
- package/plugins/escape.js +32 -9
- package/plugins/export.js +5 -7
- package/plugins/for.js +7 -6
- package/plugins/function.js +7 -11
- package/plugins/if.js +6 -5
- package/plugins/import.js +7 -5
- package/plugins/include.js +5 -3
- package/plugins/js.js +1 -1
- package/plugins/layout.js +6 -8
- package/plugins/set.js +5 -7
- package/types/core/environment.d.ts +13 -14
- package/types/core/errors.d.ts +35 -13
- package/types/core/tokenizer.d.ts +1 -1
- package/types/loaders/file.d.ts +1 -1
- package/types/loaders/filesystem.d.ts +12 -0
- package/types/loaders/memory.d.ts +1 -1
- package/types/loaders/module.d.ts +19 -0
- package/types/loaders/utils.d.ts +1 -0
- /package/types/{browser.d.ts → web.d.ts} +0 -0
- /package/{browser.js → web.js} +0 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ventojs",
|
3
|
-
"version": "2.0.0-canary.
|
3
|
+
"version": "2.0.0-canary.1",
|
4
4
|
"description": "🌬 A minimal but powerful template engine",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -23,12 +23,6 @@
|
|
23
23
|
"default": "./mod.js"
|
24
24
|
}
|
25
25
|
},
|
26
|
-
"./browser.js": {
|
27
|
-
"import": {
|
28
|
-
"types": "./types/browser.d.ts",
|
29
|
-
"default": "./browser.js"
|
30
|
-
}
|
31
|
-
},
|
32
26
|
"./core/environment.js": {
|
33
27
|
"import": {
|
34
28
|
"types": "./types/core/environment.d.ts",
|
@@ -66,18 +60,36 @@
|
|
66
60
|
"default": "./loaders/file.js"
|
67
61
|
}
|
68
62
|
},
|
63
|
+
"./loaders/filesystem.js": {
|
64
|
+
"import": {
|
65
|
+
"types": "./types/loaders/filesystem.d.ts",
|
66
|
+
"default": "./loaders/filesystem.js"
|
67
|
+
}
|
68
|
+
},
|
69
69
|
"./loaders/memory.js": {
|
70
70
|
"import": {
|
71
71
|
"types": "./types/loaders/memory.d.ts",
|
72
72
|
"default": "./loaders/memory.js"
|
73
73
|
}
|
74
74
|
},
|
75
|
+
"./loaders/module.js": {
|
76
|
+
"import": {
|
77
|
+
"types": "./types/loaders/module.d.ts",
|
78
|
+
"default": "./loaders/module.js"
|
79
|
+
}
|
80
|
+
},
|
75
81
|
"./loaders/url.js": {
|
76
82
|
"import": {
|
77
83
|
"types": "./types/loaders/url.d.ts",
|
78
84
|
"default": "./loaders/url.js"
|
79
85
|
}
|
80
86
|
},
|
87
|
+
"./loaders/utils.js": {
|
88
|
+
"import": {
|
89
|
+
"types": "./types/loaders/utils.d.ts",
|
90
|
+
"default": "./loaders/utils.js"
|
91
|
+
}
|
92
|
+
},
|
81
93
|
"./mod.js": {
|
82
94
|
"import": {
|
83
95
|
"types": "./types/mod.d.ts",
|
@@ -174,6 +186,12 @@
|
|
174
186
|
"default": "./plugins/unescape.js"
|
175
187
|
}
|
176
188
|
},
|
177
|
-
"./prism-vento.js": "./prism-vento.js"
|
189
|
+
"./prism-vento.js": "./prism-vento.js",
|
190
|
+
"./web.js": {
|
191
|
+
"import": {
|
192
|
+
"types": "./types/web.d.ts",
|
193
|
+
"default": "./web.js"
|
194
|
+
}
|
195
|
+
}
|
178
196
|
}
|
179
197
|
}
|
package/plugins/auto_trim.js
CHANGED
@@ -26,7 +26,8 @@ export function autoTrim(tokens, options) {
|
|
26
26
|
const token = tokens[i];
|
27
27
|
const next = tokens[i + 1];
|
28
28
|
const [type, code] = token;
|
29
|
-
if (type === "tag" &&
|
29
|
+
if (type === "tag" &&
|
30
|
+
options.tags.find((tag) => code === tag || code.startsWith(tag + " "))) {
|
30
31
|
// Remove leading horizontal space
|
31
32
|
previous[1] = previous[1].replace(/(^|\n)[ \t]*$/, "$1");
|
32
33
|
// Remove trailing horizontal space + newline
|
package/plugins/echo.js
CHANGED
@@ -3,7 +3,7 @@ export default function () {
|
|
3
3
|
env.tags.push(echoTag);
|
4
4
|
};
|
5
5
|
}
|
6
|
-
function echoTag(env, code, output, tokens) {
|
6
|
+
function echoTag(env, [, code], output, tokens) {
|
7
7
|
if (!/^echo\b/.test(code)) {
|
8
8
|
return;
|
9
9
|
}
|
@@ -16,14 +16,10 @@ function echoTag(env, code, output, tokens) {
|
|
16
16
|
// Captured echo, e.g. {{ echo |> toUpperCase }} foo {{ /echo }}
|
17
17
|
const compiled = [`let __tmp = "";`];
|
18
18
|
const filters = env.compileFilters(tokens, "__tmp");
|
19
|
-
compiled.push(...env.compileTokens(tokens, "__tmp",
|
19
|
+
compiled.push(...env.compileTokens(tokens, "__tmp", "/echo"));
|
20
20
|
if (filters != "__tmp") {
|
21
21
|
compiled.push(`__tmp = ${filters}`);
|
22
22
|
}
|
23
|
-
const closeToken = tokens.shift();
|
24
|
-
if (!closeToken || closeToken[0] != "tag" || closeToken[1] != "/echo") {
|
25
|
-
throw new Error("Unclosed echo tag");
|
26
|
-
}
|
27
23
|
return `{
|
28
24
|
${compiled.join("\n")}
|
29
25
|
${output} += __tmp;
|
package/plugins/escape.js
CHANGED
@@ -1,17 +1,40 @@
|
|
1
|
-
|
2
|
-
const
|
3
|
-
"'": "'",
|
4
|
-
'"': """,
|
5
|
-
"&": "&",
|
6
|
-
"<": "<",
|
7
|
-
">": ">",
|
8
|
-
};
|
1
|
+
import { SafeString } from "../core/environment.js";
|
2
|
+
const UNSAFE = /[<>"&']/;
|
9
3
|
export default function () {
|
10
4
|
return (env) => {
|
11
5
|
env.filters.escape = (value) => {
|
12
6
|
if (!value)
|
13
7
|
return "";
|
14
|
-
|
8
|
+
if (value instanceof SafeString)
|
9
|
+
return value.toString();
|
10
|
+
const str = value.toString();
|
11
|
+
const match = UNSAFE.exec(str);
|
12
|
+
if (match === null) {
|
13
|
+
return str;
|
14
|
+
}
|
15
|
+
let html = "";
|
16
|
+
for (let i = match.index; i < str.length; i++) {
|
17
|
+
switch (str.charCodeAt(i)) {
|
18
|
+
case 34: // "
|
19
|
+
html += """;
|
20
|
+
break;
|
21
|
+
case 39: // '
|
22
|
+
html += "'";
|
23
|
+
break;
|
24
|
+
case 38: // &
|
25
|
+
html += "&";
|
26
|
+
break;
|
27
|
+
case 60: // <
|
28
|
+
html += "<";
|
29
|
+
break;
|
30
|
+
case 62: // >
|
31
|
+
html += ">";
|
32
|
+
break;
|
33
|
+
default:
|
34
|
+
html += str[i];
|
35
|
+
}
|
36
|
+
}
|
37
|
+
return html;
|
15
38
|
};
|
16
39
|
};
|
17
40
|
}
|
package/plugins/export.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
export default function () {
|
2
3
|
return (env) => {
|
3
4
|
env.tags.push(exportTag);
|
@@ -8,7 +9,8 @@ const BLOCK_EXPORT = /^([a-zA-Z_]\w*)\s*$/;
|
|
8
9
|
const INLINE_NAMED_EXPORT = /^([a-zA-Z_]\w*)\s*=([^]*)$/;
|
9
10
|
const NAMED_EXPORTS = /^{[^]*?}$/;
|
10
11
|
const AS = /\s+\bas\b\s+/;
|
11
|
-
function exportTag(env,
|
12
|
+
function exportTag(env, token, _output, tokens) {
|
13
|
+
const [, code] = token;
|
12
14
|
const exportStart = code.match(EXPORT_START);
|
13
15
|
if (!exportStart) {
|
14
16
|
return;
|
@@ -22,11 +24,7 @@ function exportTag(env, code, _output, tokens) {
|
|
22
24
|
const [, name] = blockExport;
|
23
25
|
const compiledFilters = env.compileFilters(tokens, name);
|
24
26
|
compiled.push(`var ${name} = "";`);
|
25
|
-
compiled.push(...env.compileTokens(tokens, name,
|
26
|
-
if (tokens[0]?.[0] !== "tag" || tokens[0]?.[1] !== "/export") {
|
27
|
-
throw new Error(`Missing closing tag for export tag: ${code}`);
|
28
|
-
}
|
29
|
-
tokens.shift();
|
27
|
+
compiled.push(...env.compileTokens(tokens, name, "/export"));
|
30
28
|
compiled.push(`${name} = ${compiledFilters}`);
|
31
29
|
compiled.push(`${dataVarname}["${name}"] = ${name};`);
|
32
30
|
compiled.push(`__exports["${name}"] = ${name};`);
|
@@ -60,7 +58,7 @@ function exportTag(env, code, _output, tokens) {
|
|
60
58
|
compiled.push(`__exports["${rename}"] = ${value};`);
|
61
59
|
}
|
62
60
|
else {
|
63
|
-
throw new
|
61
|
+
throw new TokenError("Invalid export", token);
|
64
62
|
}
|
65
63
|
}
|
66
64
|
return compiled.join("\n");
|
package/plugins/for.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
import iterateTopLevel from "../core/js.js";
|
2
3
|
export default function () {
|
3
4
|
return (env) => {
|
@@ -5,7 +6,8 @@ export default function () {
|
|
5
6
|
env.utils.toIterator = toIterator;
|
6
7
|
};
|
7
8
|
}
|
8
|
-
function forTag(env,
|
9
|
+
function forTag(env, token, output, tokens) {
|
10
|
+
const [, code] = token;
|
9
11
|
if (code === "break" || code === "continue") {
|
10
12
|
return `${code};`;
|
11
13
|
}
|
@@ -15,7 +17,7 @@ function forTag(env, code, output, tokens) {
|
|
15
17
|
const compiled = [];
|
16
18
|
const match = code.match(/^for\s+(await\s+)?([\s\S]*)$/);
|
17
19
|
if (!match) {
|
18
|
-
throw new
|
20
|
+
throw new TokenError("Invalid for loop", token);
|
19
21
|
}
|
20
22
|
let [, aw, tagCode] = match;
|
21
23
|
let var1;
|
@@ -27,7 +29,7 @@ function forTag(env, code, output, tokens) {
|
|
27
29
|
else {
|
28
30
|
const parts = tagCode.match(/(^[^\s,]+)([\s|\S]+)$/);
|
29
31
|
if (!parts) {
|
30
|
-
throw new
|
32
|
+
throw new TokenError(`Invalid for loop variable: ${tagCode}`, token);
|
31
33
|
}
|
32
34
|
var1 = parts[1].trim();
|
33
35
|
tagCode = parts[2].trim();
|
@@ -41,7 +43,7 @@ function forTag(env, code, output, tokens) {
|
|
41
43
|
else {
|
42
44
|
const parts = tagCode.match(/^([\w]+)\s+of\s+([\s|\S]+)$/);
|
43
45
|
if (!parts) {
|
44
|
-
throw new
|
46
|
+
throw new TokenError(`Invalid for loop variable: ${tagCode}`, token);
|
45
47
|
}
|
46
48
|
var2 = parts[1].trim();
|
47
49
|
collection = parts[2].trim();
|
@@ -56,8 +58,7 @@ function forTag(env, code, output, tokens) {
|
|
56
58
|
else {
|
57
59
|
compiled.push(`for ${aw || ""}(let ${var1} of __env.utils.toIterator(${env.compileFilters(tokens, collection)})) {`);
|
58
60
|
}
|
59
|
-
compiled.push(...env.compileTokens(tokens, output,
|
60
|
-
tokens.shift();
|
61
|
+
compiled.push(...env.compileTokens(tokens, output, "/for"));
|
61
62
|
compiled.push("}");
|
62
63
|
return compiled.join("\n");
|
63
64
|
}
|
package/plugins/function.js
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
export default function () {
|
2
3
|
return (env) => {
|
3
4
|
env.tags.push(functionTag);
|
4
5
|
};
|
5
6
|
}
|
6
|
-
function functionTag(env,
|
7
|
+
function functionTag(env, token, _output, tokens) {
|
8
|
+
const [, code] = token;
|
7
9
|
if (!code.match(/^(export\s+)?(async\s+)?function\s/)) {
|
8
10
|
return;
|
9
11
|
}
|
10
12
|
const match = code.match(/^(export\s+)?(async\s+)?function\s+(\w+)\s*(\([^)]+\))?$/);
|
11
13
|
if (!match) {
|
12
|
-
throw new
|
14
|
+
throw new TokenError("Invalid function tag", token);
|
13
15
|
}
|
14
16
|
const [_, exp, as, name, args] = match;
|
15
17
|
const compiled = [];
|
@@ -17,19 +19,13 @@ function functionTag(env, code, _output, tokens) {
|
|
17
19
|
compiled.push(`let __output = "";`);
|
18
20
|
const result = env.compileFilters(tokens, "__output");
|
19
21
|
if (exp) {
|
20
|
-
compiled.push(...env.compileTokens(tokens, "__output",
|
21
|
-
if (tokens.length && (tokens[0][0] !== "tag" || tokens[0][1] !== "/export")) {
|
22
|
-
throw new Error(`Missing closing tag for export function tag: ${code}`);
|
23
|
-
}
|
22
|
+
compiled.push(...env.compileTokens(tokens, "__output", "/export"));
|
24
23
|
}
|
25
24
|
else {
|
26
|
-
compiled.push(...env.compileTokens(tokens, "__output",
|
27
|
-
if (tokens.length && (tokens[0][0] !== "tag" || tokens[0][1] !== "/function")) {
|
28
|
-
throw new Error(`Missing closing tag for function tag: ${code}`);
|
29
|
-
}
|
25
|
+
compiled.push(...env.compileTokens(tokens, "__output", "/function"));
|
30
26
|
}
|
31
27
|
tokens.shift();
|
32
|
-
compiled.push(`return ${result};`);
|
28
|
+
compiled.push(`return __env.utils.safeString(${result});`);
|
33
29
|
compiled.push(`}`);
|
34
30
|
if (exp) {
|
35
31
|
compiled.push(`__exports["${name}"] = ${name}`);
|
package/plugins/if.js
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
export default function () {
|
2
3
|
return (env) => {
|
3
4
|
env.tags.push(ifTag);
|
4
5
|
env.tags.push(elseTag);
|
5
6
|
};
|
6
7
|
}
|
7
|
-
function ifTag(env, code, output, tokens) {
|
8
|
+
function ifTag(env, [, code], output, tokens) {
|
8
9
|
if (!code.startsWith("if ")) {
|
9
10
|
return;
|
10
11
|
}
|
@@ -12,18 +13,18 @@ function ifTag(env, code, output, tokens) {
|
|
12
13
|
const compiled = [];
|
13
14
|
const val = env.compileFilters(tokens, condition);
|
14
15
|
compiled.push(`if (${val}) {`);
|
15
|
-
compiled.push(...env.compileTokens(tokens, output,
|
16
|
-
tokens.shift();
|
16
|
+
compiled.push(...env.compileTokens(tokens, output, "/if"));
|
17
17
|
compiled.push("}");
|
18
18
|
return compiled.join("\n");
|
19
19
|
}
|
20
|
-
function elseTag(_env,
|
20
|
+
function elseTag(_env, token) {
|
21
|
+
const [, code] = token;
|
21
22
|
if (!code.startsWith("else ") && code !== "else") {
|
22
23
|
return;
|
23
24
|
}
|
24
25
|
const match = code.match(/^else(\s+if\s+(.*))?$/);
|
25
26
|
if (!match) {
|
26
|
-
throw new
|
27
|
+
throw new TokenError("Invalid else tag", token);
|
27
28
|
}
|
28
29
|
const [_, ifTag, condition] = match;
|
29
30
|
if (ifTag) {
|
package/plugins/import.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
export default function () {
|
2
3
|
return (env) => {
|
3
4
|
env.tags.push(importTag);
|
@@ -7,13 +8,14 @@ const IMPORT_STATEMENT = /^import\b\s*([^]*?)\s*\bfrom\b([^]*)$/;
|
|
7
8
|
const DEFAULT_IMPORT = /^\b[a-zA-Z_]\w*\b$/i;
|
8
9
|
const NAMED_IMPORTS = /^{[^]*?}$/;
|
9
10
|
const AS = /\s+\bas\b\s+/;
|
10
|
-
function importTag(env,
|
11
|
+
function importTag(env, token) {
|
12
|
+
const [, code] = token;
|
11
13
|
if (!code.startsWith("import ")) {
|
12
14
|
return;
|
13
15
|
}
|
14
16
|
const match = code.match(IMPORT_STATEMENT);
|
15
17
|
if (!match) {
|
16
|
-
throw new
|
18
|
+
throw new TokenError("Invalid import tag", token);
|
17
19
|
}
|
18
20
|
const compiled = [];
|
19
21
|
const variables = [];
|
@@ -41,18 +43,18 @@ function importTag(env, code) {
|
|
41
43
|
return `${name}: ${rename}`;
|
42
44
|
}
|
43
45
|
else {
|
44
|
-
throw new
|
46
|
+
throw new TokenError("Invalid named import", token);
|
45
47
|
}
|
46
48
|
});
|
47
49
|
compiled.push(`({${chunks.join(",")}} = __tmp);`);
|
48
50
|
}
|
49
51
|
else {
|
50
|
-
throw new
|
52
|
+
throw new TokenError("Invalid import tag", token);
|
51
53
|
}
|
52
54
|
}
|
53
55
|
const { dataVarname } = env.options;
|
54
56
|
return `let ${variables.join(",")}; {
|
55
|
-
let __tmp = await __env.run(${specifier}, {...${dataVarname}},
|
57
|
+
let __tmp = await __env.run(${specifier}, {...${dataVarname}}, __template.path);
|
56
58
|
${compiled.join("\n")}
|
57
59
|
}`;
|
58
60
|
}
|
package/plugins/include.js
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
import iterateTopLevel from "../core/js.js";
|
2
3
|
export default function () {
|
3
4
|
return (env) => {
|
4
5
|
env.tags.push(includeTag);
|
5
6
|
};
|
6
7
|
}
|
7
|
-
function includeTag(env,
|
8
|
+
function includeTag(env, token, output, tokens) {
|
9
|
+
const [, code] = token;
|
8
10
|
if (!code.startsWith("include ")) {
|
9
11
|
return;
|
10
12
|
}
|
@@ -19,7 +21,7 @@ function includeTag(env, code, output, tokens) {
|
|
19
21
|
bracketIndex = index;
|
20
22
|
}
|
21
23
|
if (bracketIndex == -1) {
|
22
|
-
throw
|
24
|
+
throw new TokenError("Invalid include tag", token);
|
23
25
|
}
|
24
26
|
file = tagCode.slice(0, bracketIndex).trim();
|
25
27
|
data = tagCode.slice(bracketIndex).trim();
|
@@ -28,7 +30,7 @@ function includeTag(env, code, output, tokens) {
|
|
28
30
|
return `{
|
29
31
|
const __tmp = await __env.run(${file},
|
30
32
|
{...${dataVarname}${data ? `, ...${data}` : ""}},
|
31
|
-
|
33
|
+
__template.path
|
32
34
|
);
|
33
35
|
${output} += ${env.compileFilters(tokens, "__tmp.content")};
|
34
36
|
}`;
|
package/plugins/js.js
CHANGED
package/plugins/layout.js
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
export default function () {
|
2
3
|
return (env) => {
|
3
4
|
env.tags.push(layoutTag);
|
4
5
|
};
|
5
6
|
}
|
6
|
-
function layoutTag(env,
|
7
|
+
function layoutTag(env, token, output, tokens) {
|
8
|
+
const [, code] = token;
|
7
9
|
if (!code.startsWith("layout ")) {
|
8
10
|
return;
|
9
11
|
}
|
10
12
|
const match = code?.match(/^layout\s+([^{]+|`[^`]+`)+(?:\{([\s|\S]*)\})?$/);
|
11
13
|
if (!match) {
|
12
|
-
throw new
|
14
|
+
throw new TokenError("Invalid layout tag", token);
|
13
15
|
}
|
14
16
|
const [_, file, data] = match;
|
15
17
|
const varname = output.startsWith("__layout")
|
@@ -19,16 +21,12 @@ function layoutTag(env, code, output, tokens) {
|
|
19
21
|
const compiledFilters = env.compileFilters(tokens, varname);
|
20
22
|
compiled.push("{");
|
21
23
|
compiled.push(`let ${varname} = "";`);
|
22
|
-
compiled.push(...env.compileTokens(tokens, varname,
|
23
|
-
if (tokens.length && (tokens[0][0] !== "tag" || tokens[0][1] !== "/layout")) {
|
24
|
-
throw new Error(`Missing closing tag for layout tag: ${code}`);
|
25
|
-
}
|
26
|
-
tokens.shift();
|
24
|
+
compiled.push(...env.compileTokens(tokens, varname, "/layout"));
|
27
25
|
compiled.push(`${varname} = ${compiledFilters};`);
|
28
26
|
const { dataVarname } = env.options;
|
29
27
|
compiled.push(`const __tmp = await __env.run(${file},
|
30
28
|
{...${dataVarname}${data ? `, ${data}` : ""}, content: ${env.compileFilters(tokens, varname)}},
|
31
|
-
|
29
|
+
__template.path
|
32
30
|
);
|
33
31
|
${output} += __tmp.content;`);
|
34
32
|
compiled.push("}");
|
package/plugins/set.js
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
import { TokenError } from "../core/errors.js";
|
1
2
|
export default function () {
|
2
3
|
return (env) => {
|
3
4
|
env.tags.push(setTag);
|
4
5
|
};
|
5
6
|
}
|
6
|
-
function setTag(env,
|
7
|
+
function setTag(env, token, _output, tokens) {
|
8
|
+
const [, code] = token;
|
7
9
|
if (!code.startsWith("set ")) {
|
8
10
|
return;
|
9
11
|
}
|
@@ -13,7 +15,7 @@ function setTag(env, code, _output, tokens) {
|
|
13
15
|
if (expression.includes("=")) {
|
14
16
|
const match = code.match(/^set\s+([\w]+)\s*=\s*([\s\S]+)$/);
|
15
17
|
if (!match) {
|
16
|
-
throw new
|
18
|
+
throw new TokenError("Invalid set tag", token);
|
17
19
|
}
|
18
20
|
const [, variable, value] = match;
|
19
21
|
const val = env.compileFilters(tokens, value);
|
@@ -25,11 +27,7 @@ function setTag(env, code, _output, tokens) {
|
|
25
27
|
const subvarName = `${dataVarname}["${varName}"]`;
|
26
28
|
const compiledFilters = env.compileFilters(tokens, subvarName);
|
27
29
|
compiled.push(`${subvarName} = "";`);
|
28
|
-
compiled.push(...env.compileTokens(tokens, subvarName,
|
29
|
-
if (tokens.length && (tokens[0][0] !== "tag" || tokens[0][1] !== "/set")) {
|
30
|
-
throw new Error(`Missing closing tag for set tag: ${code}`);
|
31
|
-
}
|
32
|
-
tokens.shift();
|
30
|
+
compiled.push(...env.compileTokens(tokens, subvarName, "/set"));
|
33
31
|
compiled.push(`var ${varName} = ${subvarName} = ${compiledFilters};`);
|
34
32
|
return compiled.join("\n");
|
35
33
|
}
|
@@ -3,20 +3,18 @@ export interface TemplateResult {
|
|
3
3
|
content: string;
|
4
4
|
[key: string]: unknown;
|
5
5
|
}
|
6
|
-
export interface
|
7
|
-
(data?: Record<string, unknown>): Promise<TemplateResult>;
|
6
|
+
export interface TemplateContext {
|
8
7
|
source: string;
|
9
8
|
code: string;
|
10
|
-
|
9
|
+
path?: string;
|
10
|
+
defaults?: Record<string, unknown>;
|
11
|
+
tokens?: Token[];
|
11
12
|
}
|
12
|
-
export interface
|
13
|
-
(data?: Record<string, unknown>): TemplateResult
|
14
|
-
source: string;
|
15
|
-
code: string;
|
16
|
-
file?: string;
|
13
|
+
export interface Template extends TemplateContext {
|
14
|
+
(data?: Record<string, unknown>): Promise<TemplateResult>;
|
17
15
|
}
|
18
16
|
export type TokenPreprocessor = (env: Environment, tokens: Token[], path?: string) => Token[] | void;
|
19
|
-
export type Tag = (env: Environment,
|
17
|
+
export type Tag = (env: Environment, token: Token, output: string, tokens: Token[]) => string | undefined;
|
20
18
|
export type FilterThis = {
|
21
19
|
data: Record<string, unknown>;
|
22
20
|
env: Environment;
|
@@ -27,8 +25,9 @@ export interface TemplateSource {
|
|
27
25
|
source: string;
|
28
26
|
data?: Record<string, unknown>;
|
29
27
|
}
|
28
|
+
export type PrecompiledTemplate = (env: Environment) => Template;
|
30
29
|
export interface Loader {
|
31
|
-
load(file: string): Promise<TemplateSource>;
|
30
|
+
load(file: string): Promise<TemplateSource | PrecompiledTemplate>;
|
32
31
|
resolve(from: string, file: string): string;
|
33
32
|
}
|
34
33
|
export interface Options {
|
@@ -48,11 +47,11 @@ export declare class Environment {
|
|
48
47
|
use(plugin: Plugin): void;
|
49
48
|
run(file: string, data?: Record<string, unknown>, from?: string): Promise<TemplateResult>;
|
50
49
|
runString(source: string, data?: Record<string, unknown>, file?: string): Promise<TemplateResult>;
|
51
|
-
|
52
|
-
compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: false): Template;
|
53
|
-
compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: true): TemplateSync;
|
50
|
+
compile(source: string, path?: string, defaults?: Record<string, unknown>): Template;
|
54
51
|
tokenize(source: string, path?: string): Token[];
|
55
52
|
load(file: string, from?: string): Promise<Template>;
|
56
|
-
compileTokens(tokens: Token[], outputVar?: string,
|
53
|
+
compileTokens(tokens: Token[], outputVar?: string, closeToken?: string): string[];
|
57
54
|
compileFilters(tokens: Token[], output: string, autoescape?: boolean): string;
|
58
55
|
}
|
56
|
+
export declare class SafeString extends String {
|
57
|
+
}
|
package/types/core/errors.d.ts
CHANGED
@@ -1,16 +1,38 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
import type { Token } from "./tokenizer.d.ts";
|
2
|
+
import type { TemplateContext } from "./environment.d.ts";
|
3
|
+
export interface ErrorContext {
|
4
|
+
type: string;
|
5
|
+
message: string;
|
6
6
|
source: string;
|
7
7
|
position: number;
|
8
|
-
|
8
|
+
file?: string;
|
9
|
+
}
|
10
|
+
export declare abstract class VentoError extends Error {
|
11
|
+
abstract getContext(): ErrorContext | undefined | Promise<ErrorContext | undefined>;
|
12
|
+
}
|
13
|
+
export declare class TokenError extends VentoError {
|
14
|
+
token: Token | number;
|
15
|
+
source?: string;
|
16
|
+
file?: string;
|
17
|
+
constructor(message: string, token: Token | number, source?: string, file?: string);
|
18
|
+
getContext(): {
|
19
|
+
type: string;
|
20
|
+
message: string;
|
21
|
+
source: string;
|
22
|
+
position: number;
|
23
|
+
file: string;
|
24
|
+
};
|
25
|
+
}
|
26
|
+
export declare class RuntimeError extends VentoError {
|
27
|
+
context: TemplateContext;
|
28
|
+
constructor(error: Error, context: TemplateContext);
|
29
|
+
getContext(): ErrorContext | Promise<ErrorContext>;
|
30
|
+
}
|
31
|
+
export declare function createError(error: Error, context: TemplateContext): Error;
|
32
|
+
export declare function printError(error: unknown): Promise<void>;
|
33
|
+
export interface ErrorFormat {
|
34
|
+
number: (n: string) => string;
|
35
|
+
dim: (line: string) => string;
|
36
|
+
error: (msg: string) => string;
|
9
37
|
}
|
10
|
-
|
11
|
-
export declare function errorLine(source: string, position: number): {
|
12
|
-
line: number;
|
13
|
-
column: number;
|
14
|
-
code: string;
|
15
|
-
};
|
16
|
-
export {};
|
38
|
+
export declare function stringifyContext(context: ErrorContext, format?: ErrorFormat): string;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
export type TokenType = "string" | "tag" | "filter" | "comment";
|
2
|
-
export type Token = [TokenType, string, number
|
2
|
+
export type Token = [TokenType, string, number];
|
3
3
|
export default function tokenize(source: string): Token[];
|
4
4
|
/**
|
5
5
|
* Parse a tag and return the indexes of the start and end brackets, and the filters between.
|
package/types/loaders/file.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import type { Loader, TemplateSource } from "../core/environment.d.ts";
|
2
2
|
/**
|
3
3
|
* Vento file loader for loading templates from the file system.
|
4
|
-
* Used by
|
4
|
+
* Used by Node-like runtimes (Node, Deno, Bun, ...)
|
5
5
|
*/
|
6
6
|
export declare class FileLoader implements Loader {
|
7
7
|
#private;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import type { Loader, TemplateSource } from "../core/environment.d.ts";
|
2
|
+
/**
|
3
|
+
* Vento FileSystem API loader for loading templates.
|
4
|
+
* Used by browser environments.
|
5
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/File_System_API
|
6
|
+
*/
|
7
|
+
export declare class FileSystemLoader implements Loader {
|
8
|
+
#private;
|
9
|
+
constructor(handle: FileSystemDirectoryHandle);
|
10
|
+
load(file: string): Promise<TemplateSource>;
|
11
|
+
resolve(from: string, file: string): string;
|
12
|
+
}
|
@@ -4,7 +4,7 @@ import type { Loader, TemplateSource } from "../core/environment.d.ts";
|
|
4
4
|
* Used for testing or in-memory operations.
|
5
5
|
*/
|
6
6
|
export declare class MemoryLoader implements Loader {
|
7
|
-
files:
|
7
|
+
files: Map<string, string>;
|
8
8
|
constructor(files: Record<string, string>);
|
9
9
|
load(file: string): Promise<TemplateSource>;
|
10
10
|
resolve(from: string, file: string): string;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import type { Loader, PrecompiledTemplate, Template } from "../core/environment.d.ts";
|
2
|
+
/**
|
3
|
+
* Vento loader for loading templates from a ES modules.
|
4
|
+
* Used to load precompiled templates.
|
5
|
+
*/
|
6
|
+
export declare class ModuleLoader implements Loader {
|
7
|
+
#private;
|
8
|
+
constructor(root: URL);
|
9
|
+
load(file: string): Promise<PrecompiledTemplate>;
|
10
|
+
resolve(from: string, file: string): string;
|
11
|
+
}
|
12
|
+
export interface ExportOptions {
|
13
|
+
source?: boolean;
|
14
|
+
}
|
15
|
+
/**
|
16
|
+
* Exports a template as a string that can be used in an ES module.
|
17
|
+
* This is useful for precompiled templates.
|
18
|
+
*/
|
19
|
+
export declare function exportTemplate(template: Template, options?: ExportOptions): string;
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function join(...paths: string[]): string;
|
File without changes
|
/package/{browser.js → web.js}
RENAMED
File without changes
|