ventojs 0.7.2 → 0.8.0
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/esm/mod.d.ts +1 -0
- package/esm/mod.js +5 -0
- package/esm/plugins/echo.d.ts +2 -0
- package/esm/plugins/echo.js +13 -0
- package/esm/plugins/include.js +2 -2
- package/esm/plugins/layout.js +1 -1
- package/esm/plugins/unescape.d.ts +2 -0
- package/esm/plugins/unescape.js +16 -0
- package/esm/src/environment.d.ts +3 -2
- package/esm/src/environment.js +14 -8
- package/esm/src/tokenizer.d.ts +1 -1
- package/esm/src/tokenizer.js +13 -25
- package/package.json +1 -1
package/esm/mod.d.ts
CHANGED
package/esm/mod.js
CHANGED
|
@@ -10,7 +10,9 @@ import layoutTag from "./plugins/layout.js";
|
|
|
10
10
|
import functionTag from "./plugins/function.js";
|
|
11
11
|
import importTag from "./plugins/import.js";
|
|
12
12
|
import exportTag from "./plugins/export.js";
|
|
13
|
+
import echoTag from "./plugins/echo.js";
|
|
13
14
|
import escape from "./plugins/escape.js";
|
|
15
|
+
import unescape from "./plugins/unescape.js";
|
|
14
16
|
export default function (options = {}) {
|
|
15
17
|
const loader = typeof options.includes === "object"
|
|
16
18
|
? options.includes
|
|
@@ -18,6 +20,7 @@ export default function (options = {}) {
|
|
|
18
20
|
const env = new Environment({
|
|
19
21
|
loader,
|
|
20
22
|
dataVarname: options.dataVarname || "it",
|
|
23
|
+
autoescape: options.autoescape || false,
|
|
21
24
|
});
|
|
22
25
|
// Register basic plugins
|
|
23
26
|
env.use(ifTag());
|
|
@@ -29,6 +32,8 @@ export default function (options = {}) {
|
|
|
29
32
|
env.use(functionTag());
|
|
30
33
|
env.use(importTag());
|
|
31
34
|
env.use(exportTag());
|
|
35
|
+
env.use(echoTag());
|
|
32
36
|
env.use(escape());
|
|
37
|
+
env.use(unescape());
|
|
33
38
|
return env;
|
|
34
39
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default function () {
|
|
2
|
+
return (env) => {
|
|
3
|
+
env.tags.push(setTag);
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
function setTag(env, code, output, tokens) {
|
|
7
|
+
if (!code.startsWith("echo ")) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const value = code.replace(/^echo\s+/, "");
|
|
11
|
+
const val = env.compileFilters(tokens, value, env.options.autoescape);
|
|
12
|
+
return `${output} += ${val};`;
|
|
13
|
+
}
|
package/esm/plugins/include.js
CHANGED
|
@@ -3,7 +3,7 @@ export default function () {
|
|
|
3
3
|
env.tags.push(includeTag);
|
|
4
4
|
};
|
|
5
5
|
}
|
|
6
|
-
function includeTag(
|
|
6
|
+
function includeTag(env, code, output, tokens) {
|
|
7
7
|
if (!code.startsWith("include ")) {
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
@@ -17,6 +17,6 @@ function includeTag(_env, code, output) {
|
|
|
17
17
|
{...__data${data ? `, ${data}` : ""}},
|
|
18
18
|
__file
|
|
19
19
|
);
|
|
20
|
-
${output} += __tmp.content;
|
|
20
|
+
${output} += ${env.compileFilters(tokens, "__tmp.content")};
|
|
21
21
|
}`;
|
|
22
22
|
}
|
package/esm/plugins/layout.js
CHANGED
|
@@ -24,7 +24,7 @@ function layoutTag(env, code, output, tokens) {
|
|
|
24
24
|
tokens.shift();
|
|
25
25
|
compiled.push(`${varname} = ${compiledFilters};`);
|
|
26
26
|
compiled.push(`__tmp = await __env.run(${file},
|
|
27
|
-
{...__data${data ? `, ${data}` : ""}, content: ${varname}},
|
|
27
|
+
{...__data${data ? `, ${data}` : ""}, content: ${env.compileFilters(tokens, varname)}},
|
|
28
28
|
__file
|
|
29
29
|
);
|
|
30
30
|
${output} += __tmp.content;`);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default function () {
|
|
2
|
+
return (env) => {
|
|
3
|
+
env.filters.unescape = unescape;
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
const unescapeMap = {
|
|
7
|
+
"&": "&",
|
|
8
|
+
"<": "<",
|
|
9
|
+
">": ">",
|
|
10
|
+
""": '"',
|
|
11
|
+
"'": "'",
|
|
12
|
+
"`": "`",
|
|
13
|
+
};
|
|
14
|
+
function unescape(str) {
|
|
15
|
+
return str.replace(/(&|<|>|"|'|`)/g, (match) => unescapeMap[match]);
|
|
16
|
+
}
|
package/esm/src/environment.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ export type Filter = (...args: any[]) => any;
|
|
|
19
19
|
export type Plugin = (env: Environment) => void;
|
|
20
20
|
export interface Options {
|
|
21
21
|
loader: Loader;
|
|
22
|
-
dataVarname
|
|
22
|
+
dataVarname: string;
|
|
23
|
+
autoescape: boolean;
|
|
23
24
|
}
|
|
24
25
|
export declare class Environment {
|
|
25
26
|
cache: Map<string, Template>;
|
|
@@ -36,5 +37,5 @@ export declare class Environment {
|
|
|
36
37
|
compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: true): TemplateSync;
|
|
37
38
|
load(file: string, from?: string): Promise<Template>;
|
|
38
39
|
compileTokens(tokens: Token[], outputVar?: string, stopAt?: string[]): string[];
|
|
39
|
-
compileFilters(tokens: Token[], output: string): string;
|
|
40
|
+
compileFilters(tokens: Token[], output: string, autoescape?: boolean): string;
|
|
40
41
|
}
|
package/esm/src/environment.js
CHANGED
|
@@ -67,9 +67,9 @@ export class Environment {
|
|
|
67
67
|
if (!this.cache.has(path)) {
|
|
68
68
|
const { source, data } = await this.options.loader.load(path);
|
|
69
69
|
const template = this.compile(source, path, data);
|
|
70
|
-
this.cache.set(
|
|
70
|
+
this.cache.set(path, template);
|
|
71
71
|
}
|
|
72
|
-
return this.cache.get(
|
|
72
|
+
return this.cache.get(path);
|
|
73
73
|
}
|
|
74
74
|
compileTokens(tokens, outputVar = "__exports.content", stopAt) {
|
|
75
75
|
const compiled = [];
|
|
@@ -81,10 +81,8 @@ export class Environment {
|
|
|
81
81
|
if (type === "comment") {
|
|
82
82
|
continue;
|
|
83
83
|
}
|
|
84
|
-
if (type === "string"
|
|
85
|
-
compiled.push(`${outputVar} +=
|
|
86
|
-
.replaceAll("`", "\\`")
|
|
87
|
-
.replaceAll("${", "\\${")}\`;`);
|
|
84
|
+
if (type === "string") {
|
|
85
|
+
compiled.push(`${outputVar} += ${JSON.stringify(code)};`);
|
|
88
86
|
continue;
|
|
89
87
|
}
|
|
90
88
|
if (type === "tag") {
|
|
@@ -96,7 +94,7 @@ export class Environment {
|
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
96
|
// Unknown tag, just print it
|
|
99
|
-
const expression = this.compileFilters(tokens, code);
|
|
97
|
+
const expression = this.compileFilters(tokens, code, this.options.autoescape);
|
|
100
98
|
compiled.push(`${outputVar} += (${expression}) ?? "";`);
|
|
101
99
|
continue;
|
|
102
100
|
}
|
|
@@ -104,7 +102,8 @@ export class Environment {
|
|
|
104
102
|
}
|
|
105
103
|
return compiled;
|
|
106
104
|
}
|
|
107
|
-
compileFilters(tokens, output) {
|
|
105
|
+
compileFilters(tokens, output, autoescape = false) {
|
|
106
|
+
let unescaped = false;
|
|
108
107
|
while (tokens.length > 0 && tokens[0][0] === "filter") {
|
|
109
108
|
const [, code] = tokens.shift();
|
|
110
109
|
const match = code.match(/^(await\s+)?([\w.]+)(?:\((.*)\))?$/);
|
|
@@ -123,10 +122,17 @@ export class Environment {
|
|
|
123
122
|
}
|
|
124
123
|
}
|
|
125
124
|
else {
|
|
125
|
+
if (name === "unescape") {
|
|
126
|
+
unescaped = true;
|
|
127
|
+
}
|
|
126
128
|
// It's a filter (e.g. filters.upper())
|
|
127
129
|
output = `${isAsync ? "await " : ""}__env.filters.${name}(${output}${args ? `, ${args}` : ""})`;
|
|
128
130
|
}
|
|
129
131
|
}
|
|
132
|
+
// Escape by default
|
|
133
|
+
if (autoescape && !unescaped) {
|
|
134
|
+
output = `__env.filters.escape(${output})`;
|
|
135
|
+
}
|
|
130
136
|
return output;
|
|
131
137
|
}
|
|
132
138
|
}
|
package/esm/src/tokenizer.d.ts
CHANGED
package/esm/src/tokenizer.js
CHANGED
|
@@ -17,15 +17,6 @@ export default function tokenize(source) {
|
|
|
17
17
|
break;
|
|
18
18
|
}
|
|
19
19
|
source = source.slice(index);
|
|
20
|
-
// Check if it's a {{raw}} tag
|
|
21
|
-
const raw = parseRawTag(source);
|
|
22
|
-
if (raw) {
|
|
23
|
-
const rawCode = source.slice(raw[0], raw[1]);
|
|
24
|
-
tokens.push(["raw", rawCode]);
|
|
25
|
-
source = source.slice(raw[2]);
|
|
26
|
-
type = "string";
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
20
|
type = source.startsWith("{{#") ? "comment" : "tag";
|
|
30
21
|
continue;
|
|
31
22
|
}
|
|
@@ -44,6 +35,7 @@ export default function tokenize(source) {
|
|
|
44
35
|
if (type === "tag") {
|
|
45
36
|
const indexes = parseTag(source);
|
|
46
37
|
const lastIndex = indexes.length - 1;
|
|
38
|
+
let tag;
|
|
47
39
|
indexes.reduce((prev, curr, index) => {
|
|
48
40
|
let code = source.slice(prev, curr - 2);
|
|
49
41
|
// Tag
|
|
@@ -59,7 +51,8 @@ export default function tokenize(source) {
|
|
|
59
51
|
code = code.slice(0, -1);
|
|
60
52
|
trimNext = true;
|
|
61
53
|
}
|
|
62
|
-
|
|
54
|
+
tag = [type, code.trim()];
|
|
55
|
+
tokens.push(tag);
|
|
63
56
|
return curr;
|
|
64
57
|
}
|
|
65
58
|
// Right trim
|
|
@@ -73,6 +66,16 @@ export default function tokenize(source) {
|
|
|
73
66
|
});
|
|
74
67
|
source = source.slice(indexes[indexes.length - 1]);
|
|
75
68
|
type = "string";
|
|
69
|
+
// Search the closing echo tag {{ /echo }}
|
|
70
|
+
if (tag?.[1] === "echo") {
|
|
71
|
+
const end = source.match(/{{\s*\/echo\s*}}/);
|
|
72
|
+
if (!end) {
|
|
73
|
+
throw new Error("Unclosed echo tag");
|
|
74
|
+
}
|
|
75
|
+
const rawCode = source.slice(0, end.index);
|
|
76
|
+
tag[1] = `echo ${JSON.stringify(rawCode)}`;
|
|
77
|
+
source = source.slice(Number(end.index) + end[0].length);
|
|
78
|
+
}
|
|
76
79
|
continue;
|
|
77
80
|
}
|
|
78
81
|
}
|
|
@@ -180,18 +183,3 @@ export function parseTag(source) {
|
|
|
180
183
|
}
|
|
181
184
|
throw new Error("Unclosed tag");
|
|
182
185
|
}
|
|
183
|
-
function parseRawTag(source) {
|
|
184
|
-
const startResult = source.match(/^{{\s*raw\s*}}/);
|
|
185
|
-
if (!startResult) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
const endResult = source.match(/{{\s*\/raw\s*}}/);
|
|
189
|
-
if (!endResult) {
|
|
190
|
-
throw new Error("Unclosed raw tag");
|
|
191
|
-
}
|
|
192
|
-
return [
|
|
193
|
-
startResult[0].length,
|
|
194
|
-
endResult.index,
|
|
195
|
-
endResult.index + endResult[0].length,
|
|
196
|
-
];
|
|
197
|
-
}
|