@cocreate/server-side-render 1.12.3 → 1.12.5
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/CHANGELOG.md +21 -0
- package/package.json +1 -9
- package/src/index.js +145 -62
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [1.12.5](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.12.4...v1.12.5) (2025-10-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add TODO comments for fetching src and activating language links in HTML rendering ([0429a37](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/0429a37715c648c46989f351befb8c5f19f5e181))
|
|
7
|
+
* enhance src handling and language link tag creation in HTML rendering ([1dc2bb6](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/1dc2bb6d9c4839d605b3ddd1cd4374f9b10bf3ba))
|
|
8
|
+
* ensure proper context binding and improve translation logic in CoCreateServerSideRender ([74e5ab2](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/74e5ab22a68d1eec14dd6a2600f2a8363eefff16))
|
|
9
|
+
* improve src handling and add getRelativePath utility in HTML rendering ([c8b17f8](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/c8b17f8b603808325393503c897bed94ff93e36f))
|
|
10
|
+
* refactor language link tag creation and improve DOM manipulation in HTML rendering ([b860e08](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/b860e08be2c89ea60aac758d56e2599ed1aeec3a))
|
|
11
|
+
* update HTML method to accept organization and language parameters for improved rendering ([515336e](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/515336ea7e7cc975a5e65f07d0f5166d7bc2d9e1))
|
|
12
|
+
|
|
13
|
+
## [1.12.4](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.12.3...v1.12.4) (2025-09-01)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* optimize language link tag insertion in HTML rendering ([ac0580c](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/ac0580cd90da8a555fb1fb47412c1484ccc20a93))
|
|
19
|
+
* refactor HTML method to accept file object and enhance language link generation ([b0d7a97](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/b0d7a97de5f3e3c46dc3bd42c62e79e3843ebb22))
|
|
20
|
+
* streamline render attribute checks in CoCreateServerSideRender ([4407afb](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/4407afbcc218e7fa9af488e5a2bc5b2abf74d7bc))
|
|
21
|
+
|
|
1
22
|
## [1.12.3](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.12.2...v1.12.3) (2025-05-01)
|
|
2
23
|
|
|
3
24
|
|
package/package.json
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/server-side-render",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.5",
|
|
4
4
|
"description": "A simple server-side-render component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"server-side-render",
|
|
7
|
-
"cocreate",
|
|
8
|
-
"low-code-framework",
|
|
9
|
-
"no-code-framework",
|
|
10
|
-
"cocreatejs",
|
|
11
|
-
"cocreatejs-component",
|
|
12
|
-
"cocreate-framework",
|
|
13
|
-
"no-code",
|
|
14
7
|
"low-code",
|
|
15
|
-
"collaborative-framework",
|
|
16
8
|
"realtime",
|
|
17
9
|
"realtime-framework",
|
|
18
10
|
"collaboration",
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
const { parse } = require("node-html-parser");
|
|
2
|
-
const { checkValue } = require("@cocreate/utils");
|
|
2
|
+
const { checkValue, getRelativePath, ObjectId } = require("@cocreate/utils");
|
|
3
|
+
const path = require("path");
|
|
3
4
|
|
|
4
5
|
class CoCreateServerSideRender {
|
|
5
6
|
constructor(crud) {
|
|
6
7
|
this.crud = crud;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
async HTML(
|
|
10
|
+
async HTML(file, organization, urlObject, langRegion, lang) {
|
|
10
11
|
const self = this;
|
|
11
12
|
let ignoreElement = {
|
|
12
13
|
INPUT: true,
|
|
@@ -19,10 +20,10 @@ class CoCreateServerSideRender {
|
|
|
19
20
|
|
|
20
21
|
let dep = [];
|
|
21
22
|
let dbCache = new Map();
|
|
23
|
+
let organization_id = file.organization_id;
|
|
24
|
+
const host = urlObject.hostname;
|
|
22
25
|
|
|
23
|
-
async function render(
|
|
24
|
-
const dom = parse(html);
|
|
25
|
-
|
|
26
|
+
async function render(dom, lastKey) {
|
|
26
27
|
// Handle elements with [array][key][object]
|
|
27
28
|
for (let el of dom.querySelectorAll("[array][key][object]")) {
|
|
28
29
|
let meta = el.attributes;
|
|
@@ -32,14 +33,7 @@ class CoCreateServerSideRender {
|
|
|
32
33
|
if (el.closest(".template, [template], template, [render]"))
|
|
33
34
|
continue;
|
|
34
35
|
|
|
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;
|
|
36
|
+
if (el.hasAttribute("render-query")) continue;
|
|
43
37
|
|
|
44
38
|
if (el.hasAttribute("component") || el.hasAttribute("plugin"))
|
|
45
39
|
continue;
|
|
@@ -67,6 +61,7 @@ class CoCreateServerSideRender {
|
|
|
67
61
|
} else {
|
|
68
62
|
data = await self.crud.send({
|
|
69
63
|
method: "object.read",
|
|
64
|
+
host,
|
|
70
65
|
array,
|
|
71
66
|
object: { _id },
|
|
72
67
|
organization_id
|
|
@@ -97,62 +92,150 @@ class CoCreateServerSideRender {
|
|
|
97
92
|
dep.pop();
|
|
98
93
|
}
|
|
99
94
|
|
|
95
|
+
// ToDo: Fetch and render src, update relativePath. must have similar functionality to @cocreate/elements/fetch-src
|
|
100
96
|
// Handle elements with [src]
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
97
|
+
for (let el of dom.querySelectorAll(
|
|
98
|
+
"[src]:not(script, img, iframe, audio, video, source, track, input, embed, frame)"
|
|
99
|
+
)) {
|
|
100
|
+
let src = el.getAttribute("src");
|
|
101
|
+
if (!src) continue;
|
|
102
|
+
|
|
103
|
+
let path =
|
|
104
|
+
el.getAttribute("path") || getRelativePath(file.path);
|
|
105
|
+
|
|
106
|
+
if (path) {
|
|
107
|
+
src = src.replaceAll(/\$relativePath\/?/g, path);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Construct actual pathname using src and the original URL
|
|
111
|
+
let pathname = new URL(src, `http://localhost${file.path}`)
|
|
112
|
+
.pathname;
|
|
113
|
+
|
|
114
|
+
if (pathname.endsWith("/")) {
|
|
115
|
+
pathname += "index.html";
|
|
116
|
+
}
|
|
117
|
+
let $filter = {
|
|
118
|
+
query: {
|
|
119
|
+
pathname: pathname
|
|
120
|
+
}
|
|
121
|
+
}; // Use filter to structure query
|
|
122
|
+
|
|
123
|
+
let data = await self.crud.send({
|
|
124
|
+
method: "object.read",
|
|
125
|
+
host,
|
|
126
|
+
array: "files",
|
|
127
|
+
object: "",
|
|
128
|
+
$filter,
|
|
129
|
+
organization_id
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
data &&
|
|
134
|
+
data.object &&
|
|
135
|
+
data.object[0] &&
|
|
136
|
+
data.object[0].src
|
|
137
|
+
) {
|
|
138
|
+
let chunk = data.object[0].src;
|
|
139
|
+
|
|
140
|
+
// Replace $relativePath in the fetched chunk
|
|
141
|
+
let path =
|
|
142
|
+
el.getAttribute("path") || getRelativePath(file.path);
|
|
143
|
+
if (path) {
|
|
144
|
+
chunk = chunk.replaceAll(/\$relativePath\/?/g, path);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Replace ObjectId() with a new ObjectId
|
|
148
|
+
chunk = chunk.replaceAll("ObjectId()", () => {
|
|
149
|
+
// Generate a NEW ObjectId inside the function
|
|
150
|
+
return ObjectId().toString(); // Return its string representation
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Parse chunk into DOM before recursive rendering
|
|
154
|
+
let chunkDom = parse(chunk);
|
|
155
|
+
chunkDom = await render(chunkDom);
|
|
156
|
+
|
|
157
|
+
el.setAttribute("rendered", "");
|
|
158
|
+
el.innerHTML = "";
|
|
159
|
+
// Append all child nodes of chunkDom to el
|
|
160
|
+
for (const child of chunkDom.childNodes) {
|
|
161
|
+
el.appendChild(child);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
148
165
|
|
|
149
166
|
return dom;
|
|
150
167
|
}
|
|
151
168
|
|
|
152
|
-
let
|
|
169
|
+
let dom = parse(file.src);
|
|
170
|
+
dom = await render(dom, "root");
|
|
171
|
+
if (langRegion || lang) {
|
|
172
|
+
dom = await this.translate(dom, file, langRegion, lang);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (organization.languages && organization.languages.length > 0) {
|
|
176
|
+
let langLinkTags = this.createLanguageLinkTags(
|
|
177
|
+
file,
|
|
178
|
+
organization,
|
|
179
|
+
urlObject
|
|
180
|
+
);
|
|
181
|
+
const head = dom.querySelector("head");
|
|
182
|
+
if (head && langLinkTags) {
|
|
183
|
+
const linksFragment = parse(langLinkTags);
|
|
184
|
+
for (const link of linksFragment.querySelectorAll("link")) {
|
|
185
|
+
head.appendChild(link);
|
|
186
|
+
}
|
|
187
|
+
// Remove the fragment node from the DOM if it exists
|
|
188
|
+
if (linksFragment.parentNode) {
|
|
189
|
+
linksFragment.remove();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
153
194
|
dep = [];
|
|
154
195
|
dbCache.clear();
|
|
155
|
-
return
|
|
196
|
+
return dom.toString();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
createLanguageLinkTags(file, organization, urlObject) {
|
|
200
|
+
let generatedLinksString = `<link rel="alternate" hreflang="x-default" href="https://${urlObject.hostname}${file.pathname}">\n`;
|
|
201
|
+
|
|
202
|
+
for (const language of organization.languages) {
|
|
203
|
+
let langPath = `/${language}${file.pathname}`;
|
|
204
|
+
const hrefUrl = `https://${urlObject.hostname}${langPath}`;
|
|
205
|
+
generatedLinksString += `<link rel="alternate" hreflang="${language}" href="${hrefUrl}">\n`;
|
|
206
|
+
}
|
|
207
|
+
return generatedLinksString;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async translate(dom, file, langRegion, lang) {
|
|
211
|
+
if (file.translations && (langRegion || lang)) {
|
|
212
|
+
for (let translation of file.translations) {
|
|
213
|
+
let elements = dom.querySelectorAll(translation.selector) || [];
|
|
214
|
+
for (let el of elements) {
|
|
215
|
+
if (!el) continue;
|
|
216
|
+
if (translation.innerHTML) {
|
|
217
|
+
let content =
|
|
218
|
+
translation.innerHTML[langRegion] ||
|
|
219
|
+
translation.innerHTML[lang];
|
|
220
|
+
if (content) {
|
|
221
|
+
el.innerHTML = content;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (translation.attributes) {
|
|
225
|
+
for (let [key, languageObj] of Object.entries(
|
|
226
|
+
translation.attributes
|
|
227
|
+
)) {
|
|
228
|
+
let value =
|
|
229
|
+
languageObj[langRegion] || languageObj[lang];
|
|
230
|
+
if (value) {
|
|
231
|
+
el.setAttribute(key, value);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return dom;
|
|
156
239
|
}
|
|
157
240
|
}
|
|
158
241
|
|