Skip to content

Commit eba6b44

Browse files
committed
feat: render elements with attribute src
1 parent acceb3e commit eba6b44

File tree

1 file changed

+152
-92
lines changed

1 file changed

+152
-92
lines changed

src/index.js

Lines changed: 152 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,98 +2,158 @@ const { parse } = require("node-html-parser");
22
const { checkValue } = require("@cocreate/utils");
33

44
class CoCreateServerSideRender {
5-
constructor(crud) {
6-
this.crud = crud;
7-
}
8-
9-
async HTML(html, organization_id) {
10-
const self = this
11-
let ignoreElement = { INPUT: true, TEXTAREA: true, SELECT: true, LINK: true, IFRAME: true, "COCREATE-SELECT": true }
12-
13-
let dep = [];
14-
let dbCache = new Map();
15-
16-
// Does not support instanceof HTMLCollection
17-
async function render(html, lastKey) {
18-
const dom = parse(html);
19-
for (let el of dom.querySelectorAll(
20-
"[array][key][object]"
21-
)) {
22-
let meta = el.attributes;
23-
24-
if (ignoreElement[el.tagName])
25-
continue;
26-
27-
if (el.closest('.template, [template], template, [render]'))
28-
continue;
29-
30-
if (el.hasAttribute('render-selector') || el.hasAttribute('render-closest') || el.hasAttribute('render-parent') || el.hasAttribute('render-next') || el.hasAttribute('render-previous'))
31-
continue;
32-
33-
if (el.hasAttribute('component') || el.hasAttribute('plugin'))
34-
continue;
35-
36-
if (el.hasAttribute('actions'))
37-
continue;
38-
let _id = meta["object"],
39-
array = meta['array'],
40-
key = meta['key'];
41-
let crudKey = _id + array + key;
42-
if (!_id || !key || !array) continue;
43-
if (!checkValue(_id) || !checkValue(key) || !checkValue(array)) continue;
44-
if (dep.includes(crudKey))
45-
throw new Error(
46-
`infinite loop: ${lastKey} ${array} ${key} ${_id} has been already rendered`
47-
);
48-
else
49-
dep.push(crudKey)
50-
51-
let cacheKey = _id + array;
52-
let data;
53-
if (dbCache.has(cacheKey))
54-
data = dbCache.get(cacheKey)
55-
else {
56-
data = await self.crud.send({
57-
method: 'object.read',
58-
array,
59-
object: {
60-
_id
61-
},
62-
organization_id
63-
});
64-
if (data && data.object && data.object[0])
65-
data = data.object[0]
66-
67-
dbCache.set(cacheKey, data)
68-
}
69-
70-
if (!data || !data[key]) {
71-
dep.pop();
72-
continue;
73-
}
74-
let chunk = data[key];
75-
if (!chunk) {
76-
dep.pop();
77-
continue;
78-
}
79-
let dom = await render(chunk);
80-
81-
el.setAttribute('rendered', '')
82-
el.innerHTML = "";
83-
el.appendChild(dom);
84-
85-
86-
dep.pop();
87-
}
88-
89-
return dom;
90-
}
91-
92-
let result = await render(html, 'root');
93-
dep = [];
94-
dbCache.clear();
95-
return result.toString();
96-
}
5+
constructor(crud) {
6+
this.crud = crud;
7+
}
8+
9+
async HTML(html, organization_id, url) {
10+
const self = this;
11+
let ignoreElement = {
12+
INPUT: true,
13+
TEXTAREA: true,
14+
SELECT: true,
15+
LINK: true,
16+
IFRAME: true,
17+
"COCREATE-SELECT": true
18+
};
19+
20+
let dep = [];
21+
let dbCache = new Map();
22+
23+
async function render(html, lastKey) {
24+
const dom = parse(html);
25+
26+
// Handle elements with [array][key][object]
27+
for (let el of dom.querySelectorAll("[array][key][object]")) {
28+
let meta = el.attributes;
29+
30+
if (ignoreElement[el.tagName]) continue;
31+
32+
if (el.closest(".template, [template], template, [render]"))
33+
continue;
34+
35+
if (
36+
el.hasAttribute("render-selector") ||
37+
el.hasAttribute("render-closest") ||
38+
el.hasAttribute("render-parent") ||
39+
el.hasAttribute("render-next") ||
40+
el.hasAttribute("render-previous")
41+
)
42+
continue;
43+
44+
if (el.hasAttribute("component") || el.hasAttribute("plugin"))
45+
continue;
46+
47+
if (el.hasAttribute("actions")) continue;
48+
49+
let _id = meta["object"],
50+
array = meta["array"],
51+
key = meta["key"];
52+
let crudKey = _id + array + key;
53+
54+
if (!_id || !key || !array) continue;
55+
if (!checkValue(_id) || !checkValue(key) || !checkValue(array))
56+
continue;
57+
if (dep.includes(crudKey))
58+
throw new Error(
59+
`infinite loop: ${lastKey} ${array} ${key} ${_id} has been already rendered`
60+
);
61+
else dep.push(crudKey);
62+
63+
let cacheKey = _id + array;
64+
let data;
65+
if (dbCache.has(cacheKey)) {
66+
data = dbCache.get(cacheKey);
67+
} else {
68+
data = await self.crud.send({
69+
method: "object.read",
70+
array,
71+
object: { _id },
72+
organization_id
73+
});
74+
if (data && data.object && data.object[0])
75+
data = data.object[0];
76+
77+
dbCache.set(cacheKey, data);
78+
}
79+
80+
if (!data || !data[key]) {
81+
dep.pop();
82+
continue;
83+
}
84+
85+
let chunk = data[key];
86+
if (!chunk) {
87+
dep.pop();
88+
continue;
89+
}
90+
91+
chunk = await render(chunk);
92+
93+
el.setAttribute("rendered", "");
94+
el.innerHTML = "";
95+
el.appendChild(chunk);
96+
97+
dep.pop();
98+
}
99+
100+
// Handle elements with [src]
101+
for (let el of dom.querySelectorAll(
102+
"[src]:not(script, img, iframe, audio, video, source, track, input, embed, frame)"
103+
)) {
104+
let src = el.getAttribute("src");
105+
if (!src) continue;
106+
107+
// Construct actual pathname using src and the original URL
108+
let basePath = new URL(url).pathname;
109+
let resolvedPathname = new URL(
110+
src,
111+
`http://localhost${basePath}`
112+
).pathname;
113+
114+
if (resolvedPathname.endsWith("/")) {
115+
resolvedPathname += "index.html";
116+
}
117+
let $filter = {
118+
query: {
119+
pathname: resolvedPathname
120+
}
121+
}; // Use filter to structure query
122+
123+
let data = await self.crud.send({
124+
method: "object.read",
125+
array: "files",
126+
object: "",
127+
$filter,
128+
organization_id
129+
});
130+
131+
if (
132+
data &&
133+
data.object &&
134+
data.object[0] &&
135+
data.object[0].src
136+
) {
137+
let chunk = data.object[0].src;
138+
let path = el.getAttribute("path");
139+
if (path) chunk = chunk.replaceAll("{{path}}", path);
140+
141+
chunk = await render(chunk);
142+
143+
el.setAttribute("rendered", "");
144+
el.innerHTML = "";
145+
el.appendChild(chunk);
146+
}
147+
}
148+
149+
return dom;
150+
}
151+
152+
let result = await render(html, "root");
153+
dep = [];
154+
dbCache.clear();
155+
return result.toString();
156+
}
97157
}
98158

99159
module.exports = CoCreateServerSideRender;

0 commit comments

Comments
 (0)