jcc-express-mvc 1.0.5 → 1.0.7
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/package.json +1 -1
- package/src/index.js +2 -1
- package/src/templating-engine/index.js +143 -0
- package/src/templating-engine/utils.js +64 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const App = require("./app");
|
|
2
2
|
const { authenticated, protectedApi } = require("./Auth/protectedRoute");
|
|
3
3
|
const Auth = require("./Auth");
|
|
4
|
-
|
|
4
|
+
const template = require("./templating-engine");
|
|
5
5
|
const {
|
|
6
6
|
bcrypt,
|
|
7
7
|
getApiController,
|
|
@@ -39,4 +39,5 @@ module.exports = {
|
|
|
39
39
|
apiAuthenticated: protectedApi,
|
|
40
40
|
Auth,
|
|
41
41
|
getDbCli,
|
|
42
|
+
template: template,
|
|
42
43
|
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const Util = require("./utils");
|
|
3
|
+
const rootPath = require("app-root-path").path;
|
|
4
|
+
|
|
5
|
+
const regex = new RegExp(
|
|
6
|
+
/{{\s*(?<param>[a-zA-Z0-9_.-]+)\S*}}|(?<loop>@foreach\((?<loopvariable>[a-zA-Z0-9_-]+) in (?<loopData>[a-zA-Z0-9._-]+)\))(?<loopBody>([\s\S\t\n\r]+?))(?<loopEnd>@endforeach)|(?<ifcon>@if\((?<ifData>[a-zA-Z0-9._-]+)\))(?<ifBody>([\s\S]+?))(?<else>(?:@else(?<elseBody>([\s\S]+?))))(?<endIf>@endif)|(?<include>@include\((?<includePath>['"]([^'"]+)['"])\))/
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
// console.log(rootPath);
|
|
10
|
+
|
|
11
|
+
class Template {
|
|
12
|
+
#extendsRegex = /(?<extend>@extends\((?<extendPath>['"]([^'"]+)['"])\))/;
|
|
13
|
+
#loopRegex = /@foreach\(/;
|
|
14
|
+
constructor() {
|
|
15
|
+
this.option = {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#readFile(path) {
|
|
19
|
+
const resolvePath = path.replace(/\./g, "/").replace(/\"/g, "");
|
|
20
|
+
return fs
|
|
21
|
+
.readFileSync(`${rootPath}/views/${resolvePath}.blade.html`)
|
|
22
|
+
.toString();
|
|
23
|
+
}
|
|
24
|
+
#accessNestedProperty(obj, propertyString) {
|
|
25
|
+
if (!propertyString) return "";
|
|
26
|
+
const properties = propertyString.split(".");
|
|
27
|
+
let currentObj = obj;
|
|
28
|
+
for (const property of properties) {
|
|
29
|
+
if (currentObj && currentObj.hasOwnProperty(property)) {
|
|
30
|
+
currentObj = currentObj[property];
|
|
31
|
+
} else {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return currentObj;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#handleVariable(content, match) {
|
|
40
|
+
const variableName = match.groups.param;
|
|
41
|
+
const variableValue = this.#accessNestedProperty(this.option, variableName);
|
|
42
|
+
const newContent = content.replace(/{{\s*([^}]+)\s*}}/, variableValue);
|
|
43
|
+
return this.#parser(newContent);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#handleLoop(content, match) {
|
|
47
|
+
let result = "";
|
|
48
|
+
const res = this.#accessNestedProperty(this.option, match?.groups.loopData);
|
|
49
|
+
|
|
50
|
+
if (Array.isArray(res)) {
|
|
51
|
+
result = res
|
|
52
|
+
.map((item) => {
|
|
53
|
+
this.option[match.groups.loopvariable] = { ...item };
|
|
54
|
+
const itemTemplate = match.groups.loopBody;
|
|
55
|
+
if (this.#loopRegex.test(itemTemplate)) {
|
|
56
|
+
return this.#parser(itemTemplate + "@endforeach");
|
|
57
|
+
}
|
|
58
|
+
return this.#parser(itemTemplate);
|
|
59
|
+
})
|
|
60
|
+
.join("");
|
|
61
|
+
}
|
|
62
|
+
const { afterMatch, beforMatch } = Util.content(content, match);
|
|
63
|
+
const afterListResult = this.#parser(afterMatch);
|
|
64
|
+
const finalResult = beforMatch + result + afterListResult;
|
|
65
|
+
return finalResult.replace(/@endforeach/gm, "");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#handleCondition(content, match) {
|
|
69
|
+
let result = "";
|
|
70
|
+
const value = this.#accessNestedProperty(
|
|
71
|
+
this.option,
|
|
72
|
+
match?.groups?.ifData
|
|
73
|
+
);
|
|
74
|
+
if (value) {
|
|
75
|
+
result = this.#parser(match.groups?.ifBody);
|
|
76
|
+
} else {
|
|
77
|
+
if (match?.groups?.elseBody) {
|
|
78
|
+
result = this.#parser(match.groups?.elseBody);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const { beforMatch, afterMatch } = Util.content(content, match);
|
|
82
|
+
return beforMatch + result + this.#parser(afterMatch);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#extends(content) {
|
|
86
|
+
const match = content.match(this.#extendsRegex);
|
|
87
|
+
if (!match) return content;
|
|
88
|
+
const layoutPath = match.groups?.extendPath;
|
|
89
|
+
const { beforMatch, afterMatch } = Util.content(content, match);
|
|
90
|
+
if (layoutPath) {
|
|
91
|
+
const newContent = beforMatch + afterMatch;
|
|
92
|
+
const layout = Util.layout(newContent, this.#readFile(layoutPath));
|
|
93
|
+
return layout;
|
|
94
|
+
}
|
|
95
|
+
return content;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#includes(content, match) {
|
|
99
|
+
const includePath = match?.groups?.includePath;
|
|
100
|
+
if (includePath) {
|
|
101
|
+
const includeContent = this.#readFile(`${includePath}`);
|
|
102
|
+
const { afterMatch, beforMatch } = Util.content(content, match);
|
|
103
|
+
const remainStr = this.#parser(afterMatch);
|
|
104
|
+
return beforMatch + includeContent + remainStr;
|
|
105
|
+
}
|
|
106
|
+
return content;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#parser(content) {
|
|
110
|
+
// console.log({ content });
|
|
111
|
+
const match = content.match(regex);
|
|
112
|
+
if (!match) return content;
|
|
113
|
+
|
|
114
|
+
if (match[0].startsWith("@include")) {
|
|
115
|
+
return this.#includes(content, match);
|
|
116
|
+
}
|
|
117
|
+
if (match[0].startsWith("@foreach")) {
|
|
118
|
+
return this.#handleLoop(content, match);
|
|
119
|
+
}
|
|
120
|
+
if (match[0].startsWith("@if")) {
|
|
121
|
+
return this.#handleCondition(content, match);
|
|
122
|
+
}
|
|
123
|
+
return this.#handleVariable(content, match);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#compiler(content) {
|
|
127
|
+
const newContent = this.#extends(content);
|
|
128
|
+
const finalContent = this.#parser(newContent);
|
|
129
|
+
return finalContent
|
|
130
|
+
.replace(/(\{\{\-\-)/g, "<!--")
|
|
131
|
+
.replace(/(\-\-\}\})/g, "-->");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
render(filePath, locals, callback) {
|
|
135
|
+
fs.readFile(filePath, "utf-8", (err, content) => {
|
|
136
|
+
if (err) return callback(err);
|
|
137
|
+
this.option = locals;
|
|
138
|
+
return callback(null, this.#compiler(content));
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = new Template();
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const sectionRegex =
|
|
2
|
+
/@section\((?<sectionValue>['"]([^'"]+)['"])\)(?<sectionBody>([\s\S\t\n\r]+?))(?<sectionEnd>@endsection)|@section\((?<placeholder>[\s\S]+?)\)/;
|
|
3
|
+
class Util {
|
|
4
|
+
static content(content, match) {
|
|
5
|
+
const beforMatch = content.substring(0, match.index);
|
|
6
|
+
const afterMatch = content.substring(match.index + match[0].length);
|
|
7
|
+
return { afterMatch, beforMatch };
|
|
8
|
+
}
|
|
9
|
+
static htmlspecialchars(str) {
|
|
10
|
+
let map = {
|
|
11
|
+
"&": "&",
|
|
12
|
+
"<": "<",
|
|
13
|
+
">": ">",
|
|
14
|
+
'"': """,
|
|
15
|
+
"'": "'",
|
|
16
|
+
"/": "/",
|
|
17
|
+
};
|
|
18
|
+
return str.replace(/[&<>"'/]/g, function (m) {
|
|
19
|
+
return map[m];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static layout(content, layoutContent) {
|
|
24
|
+
const match = content.match(sectionRegex);
|
|
25
|
+
if (!match) return layoutContent;
|
|
26
|
+
const sectionContent = match?.groups?.sectionValue;
|
|
27
|
+
const placeholder = match?.groups?.placeholder;
|
|
28
|
+
if (placeholder) {
|
|
29
|
+
const item = placeholder.split(",");
|
|
30
|
+
if (item.length === 2) {
|
|
31
|
+
const newLayout = layoutContent.replace(
|
|
32
|
+
`@yield(${item[0]})`,
|
|
33
|
+
item[1].replace(/\"/g, "")
|
|
34
|
+
);
|
|
35
|
+
const { beforMatch, afterMatch } = Util.content(content, match);
|
|
36
|
+
return Util.layout(beforMatch + afterMatch, newLayout);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (sectionContent) {
|
|
40
|
+
const newLayout = layoutContent.replace(
|
|
41
|
+
`@yield(${sectionContent})`,
|
|
42
|
+
match?.groups?.sectionBody
|
|
43
|
+
);
|
|
44
|
+
const { beforMatch, afterMatch } = Util.content(content, match);
|
|
45
|
+
return Util.layout(beforMatch + afterMatch, newLayout);
|
|
46
|
+
}
|
|
47
|
+
return layoutContent;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static strToObject(str) {
|
|
51
|
+
let result = [];
|
|
52
|
+
if (!str) return [];
|
|
53
|
+
|
|
54
|
+
const items = str.split(".");
|
|
55
|
+
if (items.length == 1) return items;
|
|
56
|
+
if (items.length == 2) {
|
|
57
|
+
return items;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = Util;
|