@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.
Files changed (3) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +1 -9
  3. 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",
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(html, organization_id, url) {
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(html, lastKey) {
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
- // 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
- // }
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 result = await render(html, "root");
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 result.toString();
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