@@ -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 ;
0 commit comments