@cocreate/server-side-render 1.11.0 → 1.12.0

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.12.0](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.11.0...v1.12.0) (2024-12-22)
2
+
3
+
4
+ ### Features
5
+
6
+ * render elements with attribute src ([eba6b44](https://github.com/CoCreate-app/CoCreate-server-side-render/commit/eba6b44a764590a3d2ca6b224638d7be334fb322))
7
+
1
8
  # [1.11.0](https://github.com/CoCreate-app/CoCreate-server-side-render/compare/v1.10.7...v1.11.0) (2024-11-04)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/server-side-render",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
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
@@ -2,98 +2,158 @@ const { parse } = require("node-html-parser");
2
2
  const { checkValue } = require("@cocreate/utils");
3
3
 
4
4
  class CoCreateServerSideRender {
5
- constructor(crud) {
6
- this.crud = crud;
7
- }
8
-
9
- async HTML(html, organization_id) {
10
- const self = this
11
- let ignoreElement = { INPUT: true, TEXTAREA: true, SELECT: true, LINK: true, IFRAME: true, "COCREATE-SELECT": true }
12
-
13
- let dep = [];
14
- let dbCache = new Map();
15
-
16
- // Does not support instanceof HTMLCollection
17
- async function render(html, lastKey) {
18
- const dom = parse(html);
19
- for (let el of dom.querySelectorAll(
20
- "[array][key][object]"
21
- )) {
22
- let meta = el.attributes;
23
-
24
- if (ignoreElement[el.tagName])
25
- continue;
26
-
27
- if (el.closest('.template, [template], template, [render]'))
28
- continue;
29
-
30
- if (el.hasAttribute('render-selector') || el.hasAttribute('render-closest') || el.hasAttribute('render-parent') || el.hasAttribute('render-next') || el.hasAttribute('render-previous'))
31
- continue;
32
-
33
- if (el.hasAttribute('component') || el.hasAttribute('plugin'))
34
- continue;
35
-
36
- if (el.hasAttribute('actions'))
37
- continue;
38
- let _id = meta["object"],
39
- array = meta['array'],
40
- key = meta['key'];
41
- let crudKey = _id + array + key;
42
- if (!_id || !key || !array) continue;
43
- if (!checkValue(_id) || !checkValue(key) || !checkValue(array)) continue;
44
- if (dep.includes(crudKey))
45
- throw new Error(
46
- `infinite loop: ${lastKey} ${array} ${key} ${_id} has been already rendered`
47
- );
48
- else
49
- dep.push(crudKey)
50
-
51
- let cacheKey = _id + array;
52
- let data;
53
- if (dbCache.has(cacheKey))
54
- data = dbCache.get(cacheKey)
55
- else {
56
- data = await self.crud.send({
57
- method: 'object.read',
58
- array,
59
- object: {
60
- _id
61
- },
62
- organization_id
63
- });
64
- if (data && data.object && data.object[0])
65
- data = data.object[0]
66
-
67
- dbCache.set(cacheKey, data)
68
- }
69
-
70
- if (!data || !data[key]) {
71
- dep.pop();
72
- continue;
73
- }
74
- let chunk = data[key];
75
- if (!chunk) {
76
- dep.pop();
77
- continue;
78
- }
79
- let dom = await render(chunk);
80
-
81
- el.setAttribute('rendered', '')
82
- el.innerHTML = "";
83
- el.appendChild(dom);
84
-
85
-
86
- dep.pop();
87
- }
88
-
89
- return dom;
90
- }
91
-
92
- let result = await render(html, 'root');
93
- dep = [];
94
- dbCache.clear();
95
- return result.toString();
96
- }
5
+ constructor(crud) {
6
+ this.crud = crud;
7
+ }
8
+
9
+ async HTML(html, organization_id, url) {
10
+ const self = this;
11
+ let ignoreElement = {
12
+ INPUT: true,
13
+ TEXTAREA: true,
14
+ SELECT: true,
15
+ LINK: true,
16
+ IFRAME: true,
17
+ "COCREATE-SELECT": true
18
+ };
19
+
20
+ let dep = [];
21
+ let dbCache = new Map();
22
+
23
+ async function render(html, lastKey) {
24
+ const dom = parse(html);
25
+
26
+ // Handle elements with [array][key][object]
27
+ for (let el of dom.querySelectorAll("[array][key][object]")) {
28
+ let meta = el.attributes;
29
+
30
+ if (ignoreElement[el.tagName]) continue;
31
+
32
+ if (el.closest(".template, [template], template, [render]"))
33
+ continue;
34
+
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;
43
+
44
+ if (el.hasAttribute("component") || el.hasAttribute("plugin"))
45
+ continue;
46
+
47
+ if (el.hasAttribute("actions")) continue;
48
+
49
+ let _id = meta["object"],
50
+ array = meta["array"],
51
+ key = meta["key"];
52
+ let crudKey = _id + array + key;
53
+
54
+ if (!_id || !key || !array) continue;
55
+ if (!checkValue(_id) || !checkValue(key) || !checkValue(array))
56
+ continue;
57
+ if (dep.includes(crudKey))
58
+ throw new Error(
59
+ `infinite loop: ${lastKey} ${array} ${key} ${_id} has been already rendered`
60
+ );
61
+ else dep.push(crudKey);
62
+
63
+ let cacheKey = _id + array;
64
+ let data;
65
+ if (dbCache.has(cacheKey)) {
66
+ data = dbCache.get(cacheKey);
67
+ } else {
68
+ data = await self.crud.send({
69
+ method: "object.read",
70
+ array,
71
+ object: { _id },
72
+ organization_id
73
+ });
74
+ if (data && data.object && data.object[0])
75
+ data = data.object[0];
76
+
77
+ dbCache.set(cacheKey, data);
78
+ }
79
+
80
+ if (!data || !data[key]) {
81
+ dep.pop();
82
+ continue;
83
+ }
84
+
85
+ let chunk = data[key];
86
+ if (!chunk) {
87
+ dep.pop();
88
+ continue;
89
+ }
90
+
91
+ chunk = await render(chunk);
92
+
93
+ el.setAttribute("rendered", "");
94
+ el.innerHTML = "";
95
+ el.appendChild(chunk);
96
+
97
+ dep.pop();
98
+ }
99
+
100
+ // 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
+ }
148
+
149
+ return dom;
150
+ }
151
+
152
+ let result = await render(html, "root");
153
+ dep = [];
154
+ dbCache.clear();
155
+ return result.toString();
156
+ }
97
157
  }
98
158
 
99
159
  module.exports = CoCreateServerSideRender;