@cocreate/server-side-render 1.12.4 → 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 +12 -0
- package/package.json +1 -1
- package/src/index.js +120 -109
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
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
|
+
|
|
1
13
|
## [1.12.4](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.12.3...v1.12.4) (2025-09-01)
|
|
2
14
|
|
|
3
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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",
|
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(file) {
|
|
10
|
+
async HTML(file, organization, urlObject, langRegion, lang) {
|
|
10
11
|
const self = this;
|
|
11
12
|
let ignoreElement = {
|
|
12
13
|
INPUT: true,
|
|
@@ -19,6 +20,8 @@ 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
26
|
async function render(dom, lastKey) {
|
|
24
27
|
// Handle elements with [array][key][object]
|
|
@@ -58,6 +61,7 @@ class CoCreateServerSideRender {
|
|
|
58
61
|
} else {
|
|
59
62
|
data = await self.crud.send({
|
|
60
63
|
method: "object.read",
|
|
64
|
+
host,
|
|
61
65
|
array,
|
|
62
66
|
object: { _id },
|
|
63
67
|
organization_id
|
|
@@ -88,143 +92,150 @@ class CoCreateServerSideRender {
|
|
|
88
92
|
dep.pop();
|
|
89
93
|
}
|
|
90
94
|
|
|
95
|
+
// ToDo: Fetch and render src, update relativePath. must have similar functionality to @cocreate/elements/fetch-src
|
|
91
96
|
// Handle elements with [src]
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
+
}
|
|
139
165
|
|
|
140
166
|
return dom;
|
|
141
167
|
}
|
|
142
168
|
|
|
143
169
|
let dom = parse(file.src);
|
|
144
170
|
dom = await render(dom, "root");
|
|
145
|
-
if (
|
|
146
|
-
dom = translate(dom, file);
|
|
147
|
-
|
|
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
|
+
);
|
|
148
181
|
const head = dom.querySelector("head");
|
|
149
182
|
if (head && langLinkTags) {
|
|
150
|
-
const linksFragment = parse(
|
|
151
|
-
|
|
152
|
-
);
|
|
153
|
-
for (const link of linksFragment.childNodes) {
|
|
183
|
+
const linksFragment = parse(langLinkTags);
|
|
184
|
+
for (const link of linksFragment.querySelectorAll("link")) {
|
|
154
185
|
head.appendChild(link);
|
|
155
186
|
}
|
|
187
|
+
// Remove the fragment node from the DOM if it exists
|
|
188
|
+
if (linksFragment.parentNode) {
|
|
189
|
+
linksFragment.remove();
|
|
190
|
+
}
|
|
156
191
|
}
|
|
157
192
|
}
|
|
193
|
+
|
|
158
194
|
dep = [];
|
|
159
195
|
dbCache.clear();
|
|
160
196
|
return dom.toString();
|
|
161
197
|
}
|
|
162
198
|
|
|
163
|
-
createLanguageLinkTags(file) {
|
|
164
|
-
let
|
|
165
|
-
|
|
166
|
-
if (file.name !== "index.html") {
|
|
167
|
-
if (xDefault.endsWith("/")) {
|
|
168
|
-
xDefault += file.name;
|
|
169
|
-
} else {
|
|
170
|
-
xDefault += "/" + file.name;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
let generatedLinksString = `<link rel="alternate" hreflang="x-default" href="${xDefault}">\n`;
|
|
174
|
-
|
|
175
|
-
// Step 1: Create a lookup object that maps base language to its path.
|
|
176
|
-
// This is done once for efficiency.
|
|
177
|
-
const paths = {};
|
|
178
|
-
for (const p of file.pathname) {
|
|
179
|
-
const secondSlashIndex = p.indexOf("/", 1);
|
|
180
|
-
const langKey = p.substring(1, secondSlashIndex); // e.g., 'en', 'es', 'pt'
|
|
181
|
-
const restOfPath = p.substring(secondSlashIndex);
|
|
182
|
-
paths[langKey] = restOfPath;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Step 2: Iterate through all supported languages and build the HTML string.
|
|
186
|
-
for (const language of file.languages) {
|
|
187
|
-
// Use the base language to find the correct path in our map
|
|
188
|
-
const path = paths[language] || paths[language.split("-")[0]];
|
|
189
|
-
|
|
190
|
-
// If a valid path exists, construct the full link
|
|
191
|
-
if (path) {
|
|
192
|
-
// Construct the full href URL using the full language code from the array
|
|
193
|
-
const hrefUrl = `https://${file.urlObject.hostname}/${language}${path}`;
|
|
199
|
+
createLanguageLinkTags(file, organization, urlObject) {
|
|
200
|
+
let generatedLinksString = `<link rel="alternate" hreflang="x-default" href="https://${urlObject.hostname}${file.pathname}">\n`;
|
|
194
201
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
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`;
|
|
198
206
|
}
|
|
199
207
|
return generatedLinksString;
|
|
200
208
|
}
|
|
201
209
|
|
|
202
|
-
async translate(dom, file) {
|
|
203
|
-
|
|
204
|
-
let lang = file.lang;
|
|
205
|
-
if (file.translations & (langRegion || lang)) {
|
|
210
|
+
async translate(dom, file, langRegion, lang) {
|
|
211
|
+
if (file.translations && (langRegion || lang)) {
|
|
206
212
|
for (let translation of file.translations) {
|
|
207
|
-
let
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
+
}
|
|
214
223
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
+
}
|
|
223
233
|
}
|
|
224
234
|
}
|
|
225
235
|
}
|
|
226
236
|
}
|
|
227
237
|
}
|
|
238
|
+
return dom;
|
|
228
239
|
}
|
|
229
240
|
}
|
|
230
241
|
|