@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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -1
  3. 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.4",
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
- // for (let el of dom.querySelectorAll(
93
- // "[src]:not(script, img, iframe, audio, video, source, track, input, embed, frame)"
94
- // )) {
95
- // let src = el.getAttribute("src");
96
- // if (!src) continue;
97
-
98
- // // Construct actual pathname using src and the original URL
99
- // let basePath = new URL(url).pathname;
100
- // let resolvedPathname = new URL(
101
- // src,
102
- // `http://localhost${basePath}`
103
- // ).pathname;
104
-
105
- // if (resolvedPathname.endsWith("/")) {
106
- // resolvedPathname += "index.html";
107
- // }
108
- // let $filter = {
109
- // query: {
110
- // pathname: resolvedPathname
111
- // }
112
- // }; // Use filter to structure query
113
-
114
- // let data = await self.crud.send({
115
- // method: "object.read",
116
- // array: "files",
117
- // object: "",
118
- // $filter,
119
- // organization_id
120
- // });
121
-
122
- // if (
123
- // data &&
124
- // data.object &&
125
- // data.object[0] &&
126
- // data.object[0].src
127
- // ) {
128
- // let chunk = data.object[0].src;
129
- // let path = el.getAttribute("path");
130
- // if (path) chunk = chunk.replaceAll("{{path}}", path);
131
-
132
- // chunk = await render(chunk);
133
-
134
- // el.setAttribute("rendered", "");
135
- // el.innerHTML = "";
136
- // el.appendChild(chunk);
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 (file.langRegion || file.lang) {
146
- dom = translate(dom, file);
147
- let langLinkTags = createLanguageLinkTags(file);
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
- `<fragment>${langLinkTags}</fragment>`
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 xDefault = file.path;
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
- // Append the HTML string. The hreflang and the URL path are now in sync.
196
- generatedLinksString += `<link rel="alternate" hreflang="${language}" href="${hrefUrl}">\n`;
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
- let langRegion = file.langRegion;
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 el = dom.querySelectorAll(translation.selector);
208
- if (translation.innerHTML) {
209
- let content =
210
- translation.innerHTML[langRegion] ||
211
- translation.innerHTML[lang];
212
- if (content) {
213
- el.innerHTML = content;
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
- if (translation.attributes) {
217
- for (let [key, language] of Object.entries(
218
- translation.attributes
219
- )) {
220
- let value = language[langRegion] || language[lang];
221
- if (value) {
222
- el.setAttribute(key, value);
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