-
Notifications
You must be signed in to change notification settings - Fork 32
/
jae.ts
125 lines (111 loc) · 3.84 KB
/
jae.ts
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
const decoder = new TextDecoder("utf-8");
export class Jae {
/**
* A property to hold the base path to the template(s).
*/
public views_path = "";
//////////////////////////////////////////////////////////////////////////////
// FILE MARKER - CONSTRUCTOR /////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/**
* Construct an object of this class.
*
* @param viewsPath - The base path to the template(s).
*/
constructor(viewsPath: string) {
this.views_path = viewsPath;
}
//////////////////////////////////////////////////////////////////////////////
// FILE MARKER - METHODS - PUBLIC ////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/**
* Render a template file and replace all template variables with the
* specified data.
*
* @param template - The template to render.
* @param data - The data that should be rendered with the template.
*
* @remarks
* For example, the data could be...
* ```json
* {
* name: "John"
* }
* ```
* and the template would render "John" in <% name %>.
* This data can be anything and everything. It contains the data that the
* template engine will use for template variable replacement.
* @returns The html to be rendered
*/
public render(template: string, data: unknown): string {
let code = "with(obj) { var r=[];\n";
let cursor = 0;
let match;
let filepath = this.views_path;
if (this.views_path.endsWith("/") && template.startsWith("/")) {
filepath += template.slice(1);
} else if (!this.views_path.endsWith("/") && !template.startsWith("/")) {
filepath += `/${template}`;
} else {
filepath += template;
}
let html: string = decoder.decode(
Deno.readFileSync(filepath),
);
// Check if the template extends another template
const extended = html.match(/<% extends.* %>/g);
if (extended) {
extended.forEach((m: string) => {
html = html.replace(m, "");
let template = m.replace('<% extends("', "").replace('") %>', "");
template = decoder.decode(
Deno.readFileSync(this.views_path + template),
);
html = template.replace("<% yield %>", html);
});
}
// Check for partials
let partials;
while ((partials = html.match(/<% include_partial.* %>/g))) {
partials.forEach((m: string) => {
let template = m
.replace('<% include_partial("', "")
.replace('") %>', "");
template = decoder.decode(
Deno.readFileSync(this.views_path + template),
);
html = html.replace(m, template);
});
}
// The following code was taken from (and modified):
// https://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line
// Thanks, Krasimir!
const re = /<%(.+?)\%>/g;
const reExp = /(^( )?(var|if|for|else|switch|case|break|{|}|;))(.*)?/g;
let result;
function add(line: string, js: unknown | null = null) {
js
? (code += line.match(reExp) ? line + "\n" : "r.push(" + line + ");\n")
: (code += line != ""
? 'r.push("' + line.replace(/"/g, '\\"') + '");\n'
: "");
return add;
}
while ((match = re.exec(html))) {
add(html.slice(cursor, match.index));
add(match[1], true);
cursor = match.index + match[0].length;
}
add(html.substr(cursor, html.length - cursor));
code = (code + 'return r.join(""); }').replace(/[\r\t\n]/g, " ");
try {
if (!data) {
data = {};
}
result = new Function("obj", code).apply(data, [data]);
} catch (err) {
console.error("'" + err.message + "'", " in \n\nCode:\n", code, "\n");
}
return result;
}
}