@cocreate/server-side-render 1.12.4 → 1.12.6

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 +19 -0
  2. package/package.json +1 -1
  3. package/src/index.js +124 -109
package/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## [1.12.6](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.12.5...v1.12.6) (2025-10-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * ensure pathname ends with a trailing slash for correct URL construction in HTML rendering ([5122e24](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/5122e2413a986ebe51cb4a48ab92387aad4b5727))
7
+
8
+ ## [1.12.5](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.12.4...v1.12.5) (2025-10-08)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * 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))
14
+ * enhance src handling and language link tag creation in HTML rendering ([1dc2bb6](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/1dc2bb6d9c4839d605b3ddd1cd4374f9b10bf3ba))
15
+ * ensure proper context binding and improve translation logic in CoCreateServerSideRender ([74e5ab2](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/74e5ab22a68d1eec14dd6a2600f2a8363eefff16))
16
+ * improve src handling and add getRelativePath utility in HTML rendering ([c8b17f8](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/c8b17f8b603808325393503c897bed94ff93e36f))
17
+ * refactor language link tag creation and improve DOM manipulation in HTML rendering ([b860e08](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/b860e08be2c89ea60aac758d56e2599ed1aeec3a))
18
+ * update HTML method to accept organization and language parameters for improved rendering ([515336e](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/515336ea7e7cc975a5e65f07d0f5166d7bc2d9e1))
19
+
1
20
  ## [1.12.4](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.12.3...v1.12.4) (2025-09-01)
2
21
 
3
22
 
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.6",
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,154 @@ 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
+ let pathname = file.path
111
+ if (!pathname.endsWith("/")) {
112
+ pathname += "/";
113
+ }
114
+ // Construct actual pathname using src and the original URL
115
+ pathname = new URL(src, `http://localhost${pathname}`)
116
+ .pathname;
117
+
118
+ if (pathname.endsWith("/")) {
119
+ pathname += "index.html";
120
+ }
121
+ let $filter = {
122
+ query: {
123
+ pathname: pathname
124
+ }
125
+ }; // Use filter to structure query
126
+
127
+ let data = await self.crud.send({
128
+ method: "object.read",
129
+ host,
130
+ array: "files",
131
+ object: "",
132
+ $filter,
133
+ organization_id
134
+ });
135
+
136
+ if (
137
+ data &&
138
+ data.object &&
139
+ data.object[0] &&
140
+ data.object[0].src
141
+ ) {
142
+ let chunk = data.object[0].src;
143
+
144
+ // Replace $relativePath in the fetched chunk
145
+ let path =
146
+ el.getAttribute("path") || getRelativePath(file.path);
147
+ if (path) {
148
+ chunk = chunk.replaceAll(/\$relativePath\/?/g, path);
149
+ }
150
+
151
+ // Replace ObjectId() with a new ObjectId
152
+ chunk = chunk.replaceAll("ObjectId()", () => {
153
+ // Generate a NEW ObjectId inside the function
154
+ return ObjectId().toString(); // Return its string representation
155
+ });
156
+
157
+ // Parse chunk into DOM before recursive rendering
158
+ let chunkDom = parse(chunk);
159
+ chunkDom = await render(chunkDom);
160
+
161
+ el.setAttribute("rendered", "");
162
+ el.innerHTML = "";
163
+ // Append all child nodes of chunkDom to el
164
+ for (const child of chunkDom.childNodes) {
165
+ el.appendChild(child);
166
+ }
167
+ }
168
+ }
139
169
 
140
170
  return dom;
141
171
  }
142
172
 
143
173
  let dom = parse(file.src);
144
174
  dom = await render(dom, "root");
145
- if (file.langRegion || file.lang) {
146
- dom = translate(dom, file);
147
- let langLinkTags = createLanguageLinkTags(file);
175
+ if (langRegion || lang) {
176
+ dom = await this.translate(dom, file, langRegion, lang);
177
+ }
178
+
179
+ if (organization.languages && organization.languages.length > 0) {
180
+ let langLinkTags = this.createLanguageLinkTags(
181
+ file,
182
+ organization,
183
+ urlObject
184
+ );
148
185
  const head = dom.querySelector("head");
149
186
  if (head && langLinkTags) {
150
- const linksFragment = parse(
151
- `<fragment>${langLinkTags}</fragment>`
152
- );
153
- for (const link of linksFragment.childNodes) {
187
+ const linksFragment = parse(langLinkTags);
188
+ for (const link of linksFragment.querySelectorAll("link")) {
154
189
  head.appendChild(link);
155
190
  }
191
+ // Remove the fragment node from the DOM if it exists
192
+ if (linksFragment.parentNode) {
193
+ linksFragment.remove();
194
+ }
156
195
  }
157
196
  }
197
+
158
198
  dep = [];
159
199
  dbCache.clear();
160
200
  return dom.toString();
161
201
  }
162
202
 
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}`;
203
+ createLanguageLinkTags(file, organization, urlObject) {
204
+ let generatedLinksString = `<link rel="alternate" hreflang="x-default" href="https://${urlObject.hostname}${file.pathname}">\n`;
194
205
 
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
- }
206
+ for (const language of organization.languages) {
207
+ let langPath = `/${language}${file.pathname}`;
208
+ const hrefUrl = `https://${urlObject.hostname}${langPath}`;
209
+ generatedLinksString += `<link rel="alternate" hreflang="${language}" href="${hrefUrl}">\n`;
198
210
  }
199
211
  return generatedLinksString;
200
212
  }
201
213
 
202
- async translate(dom, file) {
203
- let langRegion = file.langRegion;
204
- let lang = file.lang;
205
- if (file.translations & (langRegion || lang)) {
214
+ async translate(dom, file, langRegion, lang) {
215
+ if (file.translations && (langRegion || lang)) {
206
216
  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;
217
+ let elements = dom.querySelectorAll(translation.selector) || [];
218
+ for (let el of elements) {
219
+ if (!el) continue;
220
+ if (translation.innerHTML) {
221
+ let content =
222
+ translation.innerHTML[langRegion] ||
223
+ translation.innerHTML[lang];
224
+ if (content) {
225
+ el.innerHTML = content;
226
+ }
214
227
  }
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);
228
+ if (translation.attributes) {
229
+ for (let [key, languageObj] of Object.entries(
230
+ translation.attributes
231
+ )) {
232
+ let value =
233
+ languageObj[langRegion] || languageObj[lang];
234
+ if (value) {
235
+ el.setAttribute(key, value);
236
+ }
223
237
  }
224
238
  }
225
239
  }
226
240
  }
227
241
  }
242
+ return dom;
228
243
  }
229
244
  }
230
245