-
-
Notifications
You must be signed in to change notification settings - Fork 87
Expand file tree
/
Copy pathcompile-string.ts
More file actions
135 lines (111 loc) · 4.12 KB
/
Copy pathcompile-string.ts
File metadata and controls
135 lines (111 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import type { Options } from "./config.ts";
import type { Eta } from "./internal.ts";
import type { AstObject } from "./parse.ts";
/**
* Compiles a template string to a function string. Most often users just use `compile()`, which calls `compileToString` and creates a new function using the result
*/
export function compileToString(
this: Eta,
str: string,
options?: Partial<Options>,
): string {
const config = this.config;
const isAsync = options?.async;
const compileBody = this.compileBody;
const buffer: Array<AstObject> = this.parse.call(this, str);
// note: when the include function passes through options, the only parameter that matters is the filepath parameter
let res = `${config.functionHeader}
let include = (__eta_t, __eta_d) => this.render(__eta_t, {...${config.varName}, ...(__eta_d ?? {})}, options);
let includeAsync = (__eta_t, __eta_d) => this.renderAsync(__eta_t, {...${config.varName}, ...(__eta_d ?? {})}, options);
let __eta = {res: "", e: this.config.escapeFunction, f: this.config.filterFunction, blocks: {}${
config.debug
? ', line: 1, templateStr: "' +
str.replace(/\\|"/g, "\\$&").replace(/\r\n|\n|\r/g, "\\n") +
'"'
: ""
}};
function layout(path, data) {
__eta.layout = path;
__eta.layoutData = data;
}${config.debug ? "try {" : ""}${
config.useWith ? "with(" + config.varName + "||{}){" : ""
}
function ${config.outputFunctionName}(s){__eta.res+=s;}
function capture(fn){const s=__eta.res;__eta.res='';try{fn();return __eta.res}finally{__eta.res=s;}}
async function captureAsync(fn){const s=__eta.res;__eta.res='';try{await fn();return __eta.res}finally{__eta.res=s;}}
function block(name,fn){if(__eta.layout){if(fn){__eta.blocks[name]=capture(fn);}return '';}const b=${config.varName}.__blocks||{};if(name in b){return b[name];}return fn?capture(fn):'';}
async function blockAsync(name,fn){if(__eta.layout){if(fn){__eta.blocks[name]=await captureAsync(fn);}return '';}const b=${config.varName}.__blocks||{};if(name in b){return b[name];}return fn?await captureAsync(fn):'';}
${compileBody.call(this, buffer)}
if (__eta.layout) {
__eta.res = ${
isAsync ? "await includeAsync" : "include"
} (__eta.layout, {...${
config.varName
}, body: __eta.res, ...__eta.layoutData, __blocks: __eta.blocks});
}
${config.useWith ? "}" : ""}${
config.debug
? "} catch (e) { this.RuntimeErr(e, __eta.templateStr, __eta.line, options.filepath) }"
: ""
}
return __eta.res;
`;
if (config.plugins) {
for (let i = 0; i < config.plugins.length; i++) {
const plugin = config.plugins[i];
if (plugin.processFnString) {
res = plugin.processFnString(res, config);
}
}
}
return res;
}
/**
* Loops through the AST generated by `parse` and transform each item into JS calls
*
* **Example**
*
* ```js
* let templateAST = ['Hi ', { val: 'it.name', t: 'i' }]
* compileBody.call(Eta, templateAST)
* // => "__eta.res+='Hi '\n__eta.res+=__eta.e(it.name)\n"
* ```
*/
export function compileBody(this: Eta, buff: Array<AstObject>): string {
const config = this.config;
let i = 0;
const buffLength = buff.length;
let returnStr = "";
for (i; i < buffLength; i++) {
const currentBlock = buff[i];
if (typeof currentBlock === "string") {
const str = currentBlock;
// we know string exists
returnStr += "__eta.res+='" + str + "';\n";
} else {
const type = currentBlock.t; // "r", "e", or "i"
let content = currentBlock.val || "";
if (config.debug) returnStr += "__eta.line=" + currentBlock.lineNo + "\n";
if (type === "r") {
// raw
if (config.autoFilter) {
content = "__eta.f(" + content + ")";
}
returnStr += "__eta.res+=" + content + ";\n";
} else if (type === "i") {
// interpolate
if (config.autoFilter) {
content = "__eta.f(" + content + ")";
}
if (config.autoEscape) {
content = "__eta.e(" + content + ")";
}
returnStr += "__eta.res+=" + content + ";\n";
} else if (type === "e") {
// execute
returnStr += content + "\n";
}
}
}
return returnStr;
}