ventojs 0.10.2 → 0.11.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.js +2 -0
- package/esm/plugins/auto_trim.d.ts +8 -0
- package/esm/plugins/auto_trim.js +36 -0
- package/esm/plugins/escape.js +2 -1
- package/esm/plugins/trim.d.ts +4 -0
- package/esm/plugins/trim.js +28 -0
- package/esm/plugins/unescape.js +2 -1
- package/esm/src/environment.d.ts +3 -0
- package/esm/src/environment.js +22 -5
- package/esm/src/tokenizer.js +6 -29
- package/package.json +5 -1
- package/script/mod.js +2 -0
- package/script/plugins/auto_trim.d.ts +8 -0
- package/script/plugins/auto_trim.js +41 -0
- package/script/plugins/escape.js +2 -1
- package/script/plugins/trim.d.ts +4 -0
- package/script/plugins/trim.js +33 -0
- package/script/plugins/unescape.js +2 -1
- package/script/src/environment.d.ts +3 -0
- package/script/src/environment.js +22 -5
- package/script/src/tokenizer.js +6 -29
package/esm/mod.js
CHANGED
|
@@ -13,6 +13,7 @@ import exportTag from "./plugins/export.js";
|
|
|
13
13
|
import echoTag from "./plugins/echo.js";
|
|
14
14
|
import escape from "./plugins/escape.js";
|
|
15
15
|
import unescape from "./plugins/unescape.js";
|
|
16
|
+
import trim from "./plugins/trim.js";
|
|
16
17
|
export default function (options = {}) {
|
|
17
18
|
const loader = typeof options.includes === "object"
|
|
18
19
|
? options.includes
|
|
@@ -36,5 +37,6 @@ export default function (options = {}) {
|
|
|
36
37
|
env.use(echoTag());
|
|
37
38
|
env.use(escape());
|
|
38
39
|
env.use(unescape());
|
|
40
|
+
env.use(trim());
|
|
39
41
|
return env;
|
|
40
42
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Token } from "../src/tokenizer.js";
|
|
2
|
+
import type { Environment } from "../src/environment.js";
|
|
3
|
+
export declare const defaultTags: string[];
|
|
4
|
+
export type AutoTrimOptions = {
|
|
5
|
+
tags: string[];
|
|
6
|
+
};
|
|
7
|
+
export default function (options?: AutoTrimOptions): (env: Environment) => void;
|
|
8
|
+
export declare function autoTrim(tokens: Token[], options: AutoTrimOptions): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const defaultTags = [
|
|
2
|
+
">",
|
|
3
|
+
"#",
|
|
4
|
+
"set",
|
|
5
|
+
"/set",
|
|
6
|
+
"if",
|
|
7
|
+
"/if",
|
|
8
|
+
"else",
|
|
9
|
+
"for",
|
|
10
|
+
"/for",
|
|
11
|
+
"function",
|
|
12
|
+
"async",
|
|
13
|
+
"/function",
|
|
14
|
+
"export",
|
|
15
|
+
"/export",
|
|
16
|
+
"import",
|
|
17
|
+
];
|
|
18
|
+
export default function (options = { tags: defaultTags }) {
|
|
19
|
+
return (env) => {
|
|
20
|
+
env.tokenPreprocessors.push((_, tokens) => autoTrim(tokens, options));
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function autoTrim(tokens, options) {
|
|
24
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
25
|
+
const previous = tokens[i - 1];
|
|
26
|
+
const token = tokens[i];
|
|
27
|
+
const next = tokens[i + 1];
|
|
28
|
+
const [type, code] = token;
|
|
29
|
+
if (type === "tag" && options.tags.find((tag) => code.startsWith(tag))) {
|
|
30
|
+
// Remove leading horizontal space
|
|
31
|
+
previous[1] = previous[1].replace(/[ \t]*$/, "");
|
|
32
|
+
// Remove trailing horizontal space + newline
|
|
33
|
+
next[1] = next[1].replace(/^[ \t]*(?:\r\n|\n)/, "");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
package/esm/plugins/escape.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default function () {
|
|
2
|
+
return (env) => {
|
|
3
|
+
env.tokenPreprocessors.push(trim);
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
export function trim(_, tokens) {
|
|
7
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
8
|
+
const previous = tokens[i - 1];
|
|
9
|
+
const token = tokens[i];
|
|
10
|
+
const next = tokens[i + 1];
|
|
11
|
+
let [type, code] = token;
|
|
12
|
+
if (type === "tag" && code.startsWith("-")) {
|
|
13
|
+
previous[1] = previous[1].trimEnd();
|
|
14
|
+
code = code.slice(1);
|
|
15
|
+
}
|
|
16
|
+
if (type === "tag" && code.endsWith("-")) {
|
|
17
|
+
next[1] = next[1].trimStart();
|
|
18
|
+
code = code.slice(0, -1);
|
|
19
|
+
}
|
|
20
|
+
// Trim tag and filter code
|
|
21
|
+
switch (type) {
|
|
22
|
+
case "tag":
|
|
23
|
+
case "filter":
|
|
24
|
+
token[1] = code.trim();
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/esm/plugins/unescape.js
CHANGED
package/esm/src/environment.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface TemplateSync {
|
|
|
16
16
|
code: string;
|
|
17
17
|
file?: string;
|
|
18
18
|
}
|
|
19
|
+
export type TokenPreprocessor = (env: Environment, tokens: Token[], path?: string) => Token[] | void;
|
|
19
20
|
export type Tag = (env: Environment, code: string, output: string, tokens: Token[]) => string | undefined;
|
|
20
21
|
export type FilterThis = {
|
|
21
22
|
data: Record<string, unknown>;
|
|
@@ -33,6 +34,7 @@ export declare class Environment {
|
|
|
33
34
|
cache: Map<string, Template>;
|
|
34
35
|
options: Options;
|
|
35
36
|
tags: Tag[];
|
|
37
|
+
tokenPreprocessors: TokenPreprocessor[];
|
|
36
38
|
filters: Record<string, Filter>;
|
|
37
39
|
utils: Record<string, unknown>;
|
|
38
40
|
constructor(options: Options);
|
|
@@ -42,6 +44,7 @@ export declare class Environment {
|
|
|
42
44
|
runStringSync(source: string, data?: Record<string, unknown>): TemplateResult;
|
|
43
45
|
compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: false): Template;
|
|
44
46
|
compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: true): TemplateSync;
|
|
47
|
+
tokenize(source: string, path?: string): Token[];
|
|
45
48
|
load(file: string, from?: string): Promise<Template>;
|
|
46
49
|
compileTokens(tokens: Token[], outputVar?: string, stopAt?: string[]): string[];
|
|
47
50
|
compileFilters(tokens: Token[], output: string, autoescape?: boolean): string;
|
package/esm/src/environment.js
CHANGED
|
@@ -4,6 +4,7 @@ export class Environment {
|
|
|
4
4
|
cache = new Map();
|
|
5
5
|
options;
|
|
6
6
|
tags = [];
|
|
7
|
+
tokenPreprocessors = [];
|
|
7
8
|
filters = {};
|
|
8
9
|
utils = {};
|
|
9
10
|
constructor(options) {
|
|
@@ -34,10 +35,7 @@ export class Environment {
|
|
|
34
35
|
return template(data);
|
|
35
36
|
}
|
|
36
37
|
compile(source, path, defaults, sync = false) {
|
|
37
|
-
const
|
|
38
|
-
if (error) {
|
|
39
|
-
throw this.createError(path || "unknown", source, position, error);
|
|
40
|
-
}
|
|
38
|
+
const tokens = this.tokenize(source, path);
|
|
41
39
|
const code = this.compileTokens(tokens).join("\n");
|
|
42
40
|
const { dataVarname, useWith } = this.options;
|
|
43
41
|
const constructor = new Function("__file", "__env", "__defaults", `return${sync ? "" : " async"} function (${dataVarname}) {
|
|
@@ -59,10 +57,29 @@ export class Environment {
|
|
|
59
57
|
template.source = source;
|
|
60
58
|
return template;
|
|
61
59
|
}
|
|
60
|
+
tokenize(source, path) {
|
|
61
|
+
const result = tokenize(source);
|
|
62
|
+
let { tokens } = result;
|
|
63
|
+
const { position, error } = result;
|
|
64
|
+
if (error) {
|
|
65
|
+
throw this.createError(path || "unknown", source, position, error);
|
|
66
|
+
}
|
|
67
|
+
for (const tokenPreprocessor of this.tokenPreprocessors) {
|
|
68
|
+
const result = tokenPreprocessor(this, tokens, path);
|
|
69
|
+
if (result !== undefined) {
|
|
70
|
+
tokens = result;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return tokens;
|
|
74
|
+
}
|
|
62
75
|
async load(file, from) {
|
|
63
76
|
const path = from ? this.options.loader.resolve(from, file) : file;
|
|
64
77
|
if (!this.cache.has(path)) {
|
|
65
|
-
|
|
78
|
+
// Remove query and hash params from path before loading
|
|
79
|
+
const cleanPath = path
|
|
80
|
+
.split("?")[0]
|
|
81
|
+
.split("#")[0];
|
|
82
|
+
const { source, data } = await this.options.loader.load(cleanPath);
|
|
66
83
|
const template = this.compile(source, path, data);
|
|
67
84
|
this.cache.set(path, template);
|
|
68
85
|
}
|
package/esm/src/tokenizer.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
export default function tokenize(source) {
|
|
2
2
|
const tokens = [];
|
|
3
3
|
let type = "string";
|
|
4
|
-
let trimNext = false;
|
|
5
4
|
let position = 0;
|
|
6
5
|
try {
|
|
7
6
|
while (source.length > 0) {
|
|
8
7
|
if (type === "string") {
|
|
9
8
|
const index = source.indexOf("{{");
|
|
10
9
|
const code = index === -1 ? source : source.slice(0, index);
|
|
11
|
-
|
|
12
|
-
tokens.push([type, code.trimStart(), position]);
|
|
13
|
-
trimNext = false;
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
tokens.push([type, code, position]);
|
|
17
|
-
}
|
|
10
|
+
tokens.push([type, code, position]);
|
|
18
11
|
if (index === -1) {
|
|
19
12
|
break;
|
|
20
13
|
}
|
|
@@ -41,39 +34,23 @@ export default function tokenize(source) {
|
|
|
41
34
|
const lastIndex = indexes.length - 1;
|
|
42
35
|
let tag;
|
|
43
36
|
indexes.reduce((prev, curr, index) => {
|
|
44
|
-
|
|
37
|
+
const code = source.slice(prev, curr - 2);
|
|
45
38
|
// Tag
|
|
46
39
|
if (index === 1) {
|
|
47
|
-
|
|
48
|
-
if (code.startsWith("-")) {
|
|
49
|
-
code = code.slice(1);
|
|
50
|
-
const lastToken = tokens[tokens.length - 1];
|
|
51
|
-
lastToken[1] = lastToken[1].trimEnd();
|
|
52
|
-
}
|
|
53
|
-
// Right trim
|
|
54
|
-
if (code.endsWith("-") && index === lastIndex) {
|
|
55
|
-
code = code.slice(0, -1);
|
|
56
|
-
trimNext = true;
|
|
57
|
-
}
|
|
58
|
-
tag = [type, code.trim(), position];
|
|
40
|
+
tag = [type, code, position];
|
|
59
41
|
tokens.push(tag);
|
|
60
42
|
return curr;
|
|
61
43
|
}
|
|
62
|
-
// Right trim
|
|
63
|
-
if (index === lastIndex && code.endsWith("-")) {
|
|
64
|
-
code = code.slice(0, -1);
|
|
65
|
-
trimNext = true;
|
|
66
|
-
}
|
|
67
44
|
// Filters
|
|
68
|
-
tokens.push(["filter", code
|
|
45
|
+
tokens.push(["filter", code]);
|
|
69
46
|
return curr;
|
|
70
47
|
});
|
|
71
48
|
position += indexes[lastIndex];
|
|
72
49
|
source = source.slice(indexes[lastIndex]);
|
|
73
50
|
type = "string";
|
|
74
51
|
// Search the closing echo tag {{ /echo }}
|
|
75
|
-
if (tag?.[1]
|
|
76
|
-
const end = source.match(/{{
|
|
52
|
+
if (tag?.[1].match(/^\-?\s*echo\s*\-?$/)) {
|
|
53
|
+
const end = source.match(/{{\-?\s*\/echo\s*\-?}}/);
|
|
77
54
|
if (!end) {
|
|
78
55
|
throw new Error("Unclosed echo tag");
|
|
79
56
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"module": "./esm/mod.js",
|
|
3
3
|
"main": "./script/mod.js",
|
|
4
4
|
"name": "ventojs",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.11.0",
|
|
6
6
|
"description": "🌬 A minimal but powerful template engine",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": "github:oscarotero/vento",
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
".": {
|
|
13
13
|
"import": "./esm/mod.js",
|
|
14
14
|
"require": "./script/mod.js"
|
|
15
|
+
},
|
|
16
|
+
"./plugins/auto_trim.js": {
|
|
17
|
+
"import": "./esm/plugins/auto_trim.js",
|
|
18
|
+
"require": "./script/plugins/auto_trim.js"
|
|
15
19
|
}
|
|
16
20
|
},
|
|
17
21
|
"scripts": {
|
package/script/mod.js
CHANGED
|
@@ -41,6 +41,7 @@ const export_js_1 = __importDefault(require("./plugins/export.js"));
|
|
|
41
41
|
const echo_js_1 = __importDefault(require("./plugins/echo.js"));
|
|
42
42
|
const escape_js_1 = __importDefault(require("./plugins/escape.js"));
|
|
43
43
|
const unescape_js_1 = __importDefault(require("./plugins/unescape.js"));
|
|
44
|
+
const trim_js_1 = __importDefault(require("./plugins/trim.js"));
|
|
44
45
|
function default_1(options = {}) {
|
|
45
46
|
const loader = typeof options.includes === "object"
|
|
46
47
|
? options.includes
|
|
@@ -64,6 +65,7 @@ function default_1(options = {}) {
|
|
|
64
65
|
env.use((0, echo_js_1.default)());
|
|
65
66
|
env.use((0, escape_js_1.default)());
|
|
66
67
|
env.use((0, unescape_js_1.default)());
|
|
68
|
+
env.use((0, trim_js_1.default)());
|
|
67
69
|
return env;
|
|
68
70
|
}
|
|
69
71
|
exports.default = default_1;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Token } from "../src/tokenizer.js";
|
|
2
|
+
import type { Environment } from "../src/environment.js";
|
|
3
|
+
export declare const defaultTags: string[];
|
|
4
|
+
export type AutoTrimOptions = {
|
|
5
|
+
tags: string[];
|
|
6
|
+
};
|
|
7
|
+
export default function (options?: AutoTrimOptions): (env: Environment) => void;
|
|
8
|
+
export declare function autoTrim(tokens: Token[], options: AutoTrimOptions): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.autoTrim = exports.defaultTags = void 0;
|
|
4
|
+
exports.defaultTags = [
|
|
5
|
+
">",
|
|
6
|
+
"#",
|
|
7
|
+
"set",
|
|
8
|
+
"/set",
|
|
9
|
+
"if",
|
|
10
|
+
"/if",
|
|
11
|
+
"else",
|
|
12
|
+
"for",
|
|
13
|
+
"/for",
|
|
14
|
+
"function",
|
|
15
|
+
"async",
|
|
16
|
+
"/function",
|
|
17
|
+
"export",
|
|
18
|
+
"/export",
|
|
19
|
+
"import",
|
|
20
|
+
];
|
|
21
|
+
function default_1(options = { tags: exports.defaultTags }) {
|
|
22
|
+
return (env) => {
|
|
23
|
+
env.tokenPreprocessors.push((_, tokens) => autoTrim(tokens, options));
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
exports.default = default_1;
|
|
27
|
+
function autoTrim(tokens, options) {
|
|
28
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
29
|
+
const previous = tokens[i - 1];
|
|
30
|
+
const token = tokens[i];
|
|
31
|
+
const next = tokens[i + 1];
|
|
32
|
+
const [type, code] = token;
|
|
33
|
+
if (type === "tag" && options.tags.find((tag) => code.startsWith(tag))) {
|
|
34
|
+
// Remove leading horizontal space
|
|
35
|
+
previous[1] = previous[1].replace(/[ \t]*$/, "");
|
|
36
|
+
// Remove trailing horizontal space + newline
|
|
37
|
+
next[1] = next[1].replace(/^[ \t]*(?:\r\n|\n)/, "");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.autoTrim = autoTrim;
|
package/script/plugins/escape.js
CHANGED
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const deps_js_1 = require("../deps.js");
|
|
4
4
|
function default_1() {
|
|
5
5
|
return (env) => {
|
|
6
|
-
|
|
6
|
+
// deno-lint-ignore no-explicit-any
|
|
7
|
+
env.filters.escape = (value) => value ? deps_js_1.html.escape(value.toString()) : "";
|
|
7
8
|
};
|
|
8
9
|
}
|
|
9
10
|
exports.default = default_1;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.trim = void 0;
|
|
4
|
+
function default_1() {
|
|
5
|
+
return (env) => {
|
|
6
|
+
env.tokenPreprocessors.push(trim);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
exports.default = default_1;
|
|
10
|
+
function trim(_, tokens) {
|
|
11
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
12
|
+
const previous = tokens[i - 1];
|
|
13
|
+
const token = tokens[i];
|
|
14
|
+
const next = tokens[i + 1];
|
|
15
|
+
let [type, code] = token;
|
|
16
|
+
if (type === "tag" && code.startsWith("-")) {
|
|
17
|
+
previous[1] = previous[1].trimEnd();
|
|
18
|
+
code = code.slice(1);
|
|
19
|
+
}
|
|
20
|
+
if (type === "tag" && code.endsWith("-")) {
|
|
21
|
+
next[1] = next[1].trimStart();
|
|
22
|
+
code = code.slice(0, -1);
|
|
23
|
+
}
|
|
24
|
+
// Trim tag and filter code
|
|
25
|
+
switch (type) {
|
|
26
|
+
case "tag":
|
|
27
|
+
case "filter":
|
|
28
|
+
token[1] = code.trim();
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.trim = trim;
|
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const deps_js_1 = require("../deps.js");
|
|
4
4
|
function default_1() {
|
|
5
5
|
return (env) => {
|
|
6
|
-
|
|
6
|
+
// deno-lint-ignore no-explicit-any
|
|
7
|
+
env.filters.unescape = (value) => value ? deps_js_1.html.unescape(value.toString()) : "";
|
|
7
8
|
};
|
|
8
9
|
}
|
|
9
10
|
exports.default = default_1;
|
|
@@ -16,6 +16,7 @@ export interface TemplateSync {
|
|
|
16
16
|
code: string;
|
|
17
17
|
file?: string;
|
|
18
18
|
}
|
|
19
|
+
export type TokenPreprocessor = (env: Environment, tokens: Token[], path?: string) => Token[] | void;
|
|
19
20
|
export type Tag = (env: Environment, code: string, output: string, tokens: Token[]) => string | undefined;
|
|
20
21
|
export type FilterThis = {
|
|
21
22
|
data: Record<string, unknown>;
|
|
@@ -33,6 +34,7 @@ export declare class Environment {
|
|
|
33
34
|
cache: Map<string, Template>;
|
|
34
35
|
options: Options;
|
|
35
36
|
tags: Tag[];
|
|
37
|
+
tokenPreprocessors: TokenPreprocessor[];
|
|
36
38
|
filters: Record<string, Filter>;
|
|
37
39
|
utils: Record<string, unknown>;
|
|
38
40
|
constructor(options: Options);
|
|
@@ -42,6 +44,7 @@ export declare class Environment {
|
|
|
42
44
|
runStringSync(source: string, data?: Record<string, unknown>): TemplateResult;
|
|
43
45
|
compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: false): Template;
|
|
44
46
|
compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: true): TemplateSync;
|
|
47
|
+
tokenize(source: string, path?: string): Token[];
|
|
45
48
|
load(file: string, from?: string): Promise<Template>;
|
|
46
49
|
compileTokens(tokens: Token[], outputVar?: string, stopAt?: string[]): string[];
|
|
47
50
|
compileFilters(tokens: Token[], output: string, autoescape?: boolean): string;
|
|
@@ -33,6 +33,7 @@ class Environment {
|
|
|
33
33
|
cache = new Map();
|
|
34
34
|
options;
|
|
35
35
|
tags = [];
|
|
36
|
+
tokenPreprocessors = [];
|
|
36
37
|
filters = {};
|
|
37
38
|
utils = {};
|
|
38
39
|
constructor(options) {
|
|
@@ -63,10 +64,7 @@ class Environment {
|
|
|
63
64
|
return template(data);
|
|
64
65
|
}
|
|
65
66
|
compile(source, path, defaults, sync = false) {
|
|
66
|
-
const
|
|
67
|
-
if (error) {
|
|
68
|
-
throw this.createError(path || "unknown", source, position, error);
|
|
69
|
-
}
|
|
67
|
+
const tokens = this.tokenize(source, path);
|
|
70
68
|
const code = this.compileTokens(tokens).join("\n");
|
|
71
69
|
const { dataVarname, useWith } = this.options;
|
|
72
70
|
const constructor = new Function("__file", "__env", "__defaults", `return${sync ? "" : " async"} function (${dataVarname}) {
|
|
@@ -88,10 +86,29 @@ class Environment {
|
|
|
88
86
|
template.source = source;
|
|
89
87
|
return template;
|
|
90
88
|
}
|
|
89
|
+
tokenize(source, path) {
|
|
90
|
+
const result = (0, tokenizer_js_1.default)(source);
|
|
91
|
+
let { tokens } = result;
|
|
92
|
+
const { position, error } = result;
|
|
93
|
+
if (error) {
|
|
94
|
+
throw this.createError(path || "unknown", source, position, error);
|
|
95
|
+
}
|
|
96
|
+
for (const tokenPreprocessor of this.tokenPreprocessors) {
|
|
97
|
+
const result = tokenPreprocessor(this, tokens, path);
|
|
98
|
+
if (result !== undefined) {
|
|
99
|
+
tokens = result;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return tokens;
|
|
103
|
+
}
|
|
91
104
|
async load(file, from) {
|
|
92
105
|
const path = from ? this.options.loader.resolve(from, file) : file;
|
|
93
106
|
if (!this.cache.has(path)) {
|
|
94
|
-
|
|
107
|
+
// Remove query and hash params from path before loading
|
|
108
|
+
const cleanPath = path
|
|
109
|
+
.split("?")[0]
|
|
110
|
+
.split("#")[0];
|
|
111
|
+
const { source, data } = await this.options.loader.load(cleanPath);
|
|
95
112
|
const template = this.compile(source, path, data);
|
|
96
113
|
this.cache.set(path, template);
|
|
97
114
|
}
|
package/script/src/tokenizer.js
CHANGED
|
@@ -4,20 +4,13 @@ exports.parseTag = void 0;
|
|
|
4
4
|
function tokenize(source) {
|
|
5
5
|
const tokens = [];
|
|
6
6
|
let type = "string";
|
|
7
|
-
let trimNext = false;
|
|
8
7
|
let position = 0;
|
|
9
8
|
try {
|
|
10
9
|
while (source.length > 0) {
|
|
11
10
|
if (type === "string") {
|
|
12
11
|
const index = source.indexOf("{{");
|
|
13
12
|
const code = index === -1 ? source : source.slice(0, index);
|
|
14
|
-
|
|
15
|
-
tokens.push([type, code.trimStart(), position]);
|
|
16
|
-
trimNext = false;
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
tokens.push([type, code, position]);
|
|
20
|
-
}
|
|
13
|
+
tokens.push([type, code, position]);
|
|
21
14
|
if (index === -1) {
|
|
22
15
|
break;
|
|
23
16
|
}
|
|
@@ -44,39 +37,23 @@ function tokenize(source) {
|
|
|
44
37
|
const lastIndex = indexes.length - 1;
|
|
45
38
|
let tag;
|
|
46
39
|
indexes.reduce((prev, curr, index) => {
|
|
47
|
-
|
|
40
|
+
const code = source.slice(prev, curr - 2);
|
|
48
41
|
// Tag
|
|
49
42
|
if (index === 1) {
|
|
50
|
-
|
|
51
|
-
if (code.startsWith("-")) {
|
|
52
|
-
code = code.slice(1);
|
|
53
|
-
const lastToken = tokens[tokens.length - 1];
|
|
54
|
-
lastToken[1] = lastToken[1].trimEnd();
|
|
55
|
-
}
|
|
56
|
-
// Right trim
|
|
57
|
-
if (code.endsWith("-") && index === lastIndex) {
|
|
58
|
-
code = code.slice(0, -1);
|
|
59
|
-
trimNext = true;
|
|
60
|
-
}
|
|
61
|
-
tag = [type, code.trim(), position];
|
|
43
|
+
tag = [type, code, position];
|
|
62
44
|
tokens.push(tag);
|
|
63
45
|
return curr;
|
|
64
46
|
}
|
|
65
|
-
// Right trim
|
|
66
|
-
if (index === lastIndex && code.endsWith("-")) {
|
|
67
|
-
code = code.slice(0, -1);
|
|
68
|
-
trimNext = true;
|
|
69
|
-
}
|
|
70
47
|
// Filters
|
|
71
|
-
tokens.push(["filter", code
|
|
48
|
+
tokens.push(["filter", code]);
|
|
72
49
|
return curr;
|
|
73
50
|
});
|
|
74
51
|
position += indexes[lastIndex];
|
|
75
52
|
source = source.slice(indexes[lastIndex]);
|
|
76
53
|
type = "string";
|
|
77
54
|
// Search the closing echo tag {{ /echo }}
|
|
78
|
-
if (tag?.[1]
|
|
79
|
-
const end = source.match(/{{
|
|
55
|
+
if (tag?.[1].match(/^\-?\s*echo\s*\-?$/)) {
|
|
56
|
+
const end = source.match(/{{\-?\s*\/echo\s*\-?}}/);
|
|
80
57
|
if (!end) {
|
|
81
58
|
throw new Error("Unclosed echo tag");
|
|
82
59
|
}
|