@@ -2,98 +2,158 @@ const { parse } = require("node-html-parser");
22const { checkValue } = require ( "@cocreate/utils" ) ;
33
44class 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+ }
97157}
98158
99159module . exports = CoCreateServerSideRender ;
0 commit comments