ventojs 0.7.3 → 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 +13 -5
- 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,7 +81,7 @@ export class Environment {
|
|
|
81
81
|
if (type === "comment") {
|
|
82
82
|
continue;
|
|
83
83
|
}
|
|
84
|
-
if (type === "string"
|
|
84
|
+
if (type === "string") {
|
|
85
85
|
compiled.push(`${outputVar} += ${JSON.stringify(code)};`);
|
|
86
86
|
continue;
|
|
87
87
|
}
|
|
@@ -94,7 +94,7 @@ export class Environment {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
// Unknown tag, just print it
|
|
97
|
-
const expression = this.compileFilters(tokens, code);
|
|
97
|
+
const expression = this.compileFilters(tokens, code, this.options.autoescape);
|
|
98
98
|
compiled.push(`${outputVar} += (${expression}) ?? "";`);
|
|
99
99
|
continue;
|
|
100
100
|
}
|
|
@@ -102,7 +102,8 @@ export class Environment {
|
|
|
102
102
|
}
|
|
103
103
|
return compiled;
|
|
104
104
|
}
|
|
105
|
-
compileFilters(tokens, output) {
|
|
105
|
+
compileFilters(tokens, output, autoescape = false) {
|
|
106
|
+
let unescaped = false;
|
|
106
107
|
while (tokens.length > 0 && tokens[0][0] === "filter") {
|
|
107
108
|
const [, code] = tokens.shift();
|
|
108
109
|
const match = code.match(/^(await\s+)?([\w.]+)(?:\((.*)\))?$/);
|
|
@@ -121,10 +122,17 @@ export class Environment {
|
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
else {
|
|
125
|
+
if (name === "unescape") {
|
|
126
|
+
unescaped = true;
|
|
127
|
+
}
|
|
124
128
|
// It's a filter (e.g. filters.upper())
|
|
125
129
|
output = `${isAsync ? "await " : ""}__env.filters.${name}(${output}${args ? `, ${args}` : ""})`;
|
|
126
130
|
}
|
|
127
131
|
}
|
|
132
|
+
// Escape by default
|
|
133
|
+
if (autoescape && !unescaped) {
|
|
134
|
+
output = `__env.filters.escape(${output})`;
|
|
135
|
+
}
|
|
128
136
|
return output;
|
|
129
137
|
}
|
|
130
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
|
-
}
|