pawa-ssr 1.3.22 → 1.3.24
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/index.js +4 -7
- package/package.json +2 -2
- package/pawaElement.js +3 -0
- package/power.js +389 -277
- package/test/App.js +9 -0
- package/test/check.js +13 -0
- package/test/index.js +15 -64
package/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { propsValidator, evaluateExpr,extractAtExpressions, reArrangeAttri,resum
|
|
|
5
5
|
import {AsyncLocalStorage} from'node:async_hooks'
|
|
6
6
|
import { If,For,State,Switch, Key } from'./power.js';
|
|
7
7
|
import PawaElement from'./pawaElement.js'
|
|
8
|
-
import { pluginsMap, lazyComponents } from "pawajs/index.js";
|
|
8
|
+
import { pluginsMap, lazyComponents,components } from "pawajs/index.js";
|
|
9
9
|
|
|
10
10
|
const PAWA_STORE_SYMBOL = Symbol.for('pawa.ssr.store');
|
|
11
11
|
|
|
@@ -216,10 +216,7 @@ setServer({
|
|
|
216
216
|
forwardProps
|
|
217
217
|
})
|
|
218
218
|
export const pawaForServer=setServer
|
|
219
|
-
|
|
220
|
-
export const getPawaComponentsMap =()=>{
|
|
221
|
-
return components ;
|
|
222
|
-
}
|
|
219
|
+
|
|
223
220
|
const getStore=()=>{
|
|
224
221
|
return store.getStore()
|
|
225
222
|
}
|
|
@@ -1124,10 +1121,10 @@ export const render =async (el, contexts = {},stream) => {
|
|
|
1124
1121
|
const compo=await component()
|
|
1125
1122
|
if(compo[name]){
|
|
1126
1123
|
|
|
1127
|
-
components.set(name,compo[name])
|
|
1124
|
+
components.set(name.toUpperCase(),compo[name])
|
|
1128
1125
|
el._component.component=compo[name]
|
|
1129
1126
|
el._component.validPropRule=compo[name]?.validateProps || {};
|
|
1130
|
-
lazyComponents.delete(
|
|
1127
|
+
lazyComponents.delete(name.toUpperCase())
|
|
1131
1128
|
}
|
|
1132
1129
|
} catch (error) {
|
|
1133
1130
|
throw new Error(`lazy component ${el.tagName} : Error from the component - ${error.message}`,error.message)
|
package/package.json
CHANGED
package/pawaElement.js
CHANGED
|
@@ -90,6 +90,7 @@ class PawaElement {
|
|
|
90
90
|
this._el.setAttribute('p:C',this._resumeAttr)
|
|
91
91
|
}
|
|
92
92
|
pawaAttribute(){
|
|
93
|
+
const componentAllowedAttribute=['if','else-if','for-each','switch','case','key','s-default','else']
|
|
93
94
|
const pawaAttr=getPawaAttributes()
|
|
94
95
|
const setTextResume=()=>{
|
|
95
96
|
if( this._componentName === ''&& this._el.firstElementChild === null && this._el.childNodes.some(node=>node.nodeType === 3 && node.nodeValue.includes('@{')) && !this._avoidPawaRender){
|
|
@@ -103,12 +104,14 @@ class PawaElement {
|
|
|
103
104
|
if (this._el.hasAttribute('p:c')) {
|
|
104
105
|
this._resumeAttr=this._el.getAttribute('p:c')
|
|
105
106
|
this._el.attributes.forEach((value, index, array) => {
|
|
107
|
+
// if (this._componentName && !componentAllowedAttribute.includes(value.name)) return
|
|
106
108
|
if(this._resumeAttr.includes(value.name) || value.name === 'p:c')return
|
|
107
109
|
if (value.name.startsWith(':')) return
|
|
108
110
|
this._resumeAttr+=`${value.name};`
|
|
109
111
|
})
|
|
110
112
|
}else{
|
|
111
113
|
this._el.attributes.forEach((value, index, array) => {
|
|
114
|
+
if (this._componentName && !componentAllowedAttribute.includes(value.name)) return
|
|
112
115
|
if(this._resumeAttr.includes(value.name) || value.name === 'p:c' )return
|
|
113
116
|
if (value.name.startsWith(':')) return
|
|
114
117
|
this._resumeAttr+=`${value.name};`
|
package/power.js
CHANGED
|
@@ -1,298 +1,410 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {render} from "./index.js";
|
|
2
|
+
import { convertToNumber,evaluateExpr, processNode, reArrangeAttri, pawaGenerateId } from "./utils.js";
|
|
3
|
+
import {parseHTML} from "linkedom"
|
|
4
|
+
export const If = async(el, attr,stream) => {
|
|
5
|
+
if (el._running) return;
|
|
6
|
+
el._running = true;
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this._error=[]
|
|
35
|
-
this._lazy=false
|
|
36
|
-
this._running=false
|
|
37
|
-
this._hasForOrIf=this.hasForOrIf
|
|
38
|
-
this._createError=this.createError
|
|
39
|
-
this._setError=this.setError
|
|
40
|
-
this._hydrateProps={}
|
|
41
|
-
this._resumeAttr=''
|
|
42
|
-
this._evaluateExpr=this.evaluateExpr
|
|
43
|
-
this._reArrangeAttri=this.reArrange
|
|
44
|
-
this._replaceResumeAttr=this.replaceResumeAttr
|
|
45
|
-
this._setResumeAttr=this.setResumeAttr
|
|
46
|
-
if(this._avoidPawaRender){
|
|
47
|
-
element.removeAttribute('s-pawa-avoid')
|
|
48
|
-
Array.from(element.children).forEach((child) => {
|
|
49
|
-
if (child.nodeType === 1) {
|
|
50
|
-
child.setAttribute('s-pawa-avoid','')
|
|
8
|
+
const nextSiblings = el.nextElementSibling || null;
|
|
9
|
+
const chained=[{
|
|
10
|
+
exp:el.getAttribute('if'),
|
|
11
|
+
condition:'if',
|
|
12
|
+
element:el
|
|
13
|
+
}]
|
|
14
|
+
const chainMap=new Map()
|
|
15
|
+
chainMap.set(el.getAttribute('if'),{condition:'if',element:el})
|
|
16
|
+
const getChained = (sibling) => {
|
|
17
|
+
while (sibling) {
|
|
18
|
+
const next = sibling.nextElementSibling;
|
|
19
|
+
const isElseIf = sibling.hasAttribute('else-if');
|
|
20
|
+
const isElse = sibling.hasAttribute('else');
|
|
21
|
+
|
|
22
|
+
if (isElseIf) {
|
|
23
|
+
const exp = sibling.getAttribute('else-if');
|
|
24
|
+
chained.push({ exp, condition: 'else-if', element: sibling });
|
|
25
|
+
chainMap.set(exp, { condition: 'else-if', element: sibling });
|
|
26
|
+
sibling.remove();
|
|
27
|
+
} else if (isElse) {
|
|
28
|
+
chained.push({ exp: 'false', condition: 'else', element: sibling });
|
|
29
|
+
chainMap.set('else', { condition: 'else', element: sibling });
|
|
30
|
+
sibling.remove();
|
|
31
|
+
} else {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
sibling = next;
|
|
51
35
|
}
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* @typedef{object}
|
|
56
|
-
* @property{any}
|
|
57
|
-
* Object of Html Attributes for Rest Attributes
|
|
58
|
-
*/
|
|
59
|
-
this._restProps={}
|
|
60
|
-
this._componentChildren=null
|
|
61
|
-
this.getComponent()
|
|
62
|
-
this.setProps()
|
|
63
|
-
this.pawaAttribute()
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
*
|
|
67
|
-
* @param {HTMLElement} el
|
|
68
|
-
* @param {object} context
|
|
69
|
-
* @returns {PawaElement}
|
|
70
|
-
*/
|
|
71
|
-
static Element(el,context){
|
|
72
|
-
const pawa=new PawaElement(el,context)
|
|
73
|
-
Object.assign(el,pawa)
|
|
74
|
-
return el
|
|
75
36
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
37
|
+
getChained(nextSiblings)
|
|
38
|
+
let func=new Map()
|
|
39
|
+
let current
|
|
40
|
+
let latestChain
|
|
41
|
+
chained.forEach((item)=>{
|
|
42
|
+
if(item.condition === 'else' || current)return
|
|
43
|
+
const result = el._evaluateExpr(item.exp, el._context,`at condition directives check - ${item.exp} at ${item.element.toString()}`);
|
|
44
|
+
current=result
|
|
45
|
+
if (current) {
|
|
46
|
+
latestChain={
|
|
47
|
+
id:item.exp,
|
|
48
|
+
condition:item.condition
|
|
49
|
+
}
|
|
50
|
+
}else{
|
|
51
|
+
latestChain={
|
|
52
|
+
id:'else',
|
|
53
|
+
condition:'else'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
})
|
|
58
|
+
const document=el.ownerDocument
|
|
59
|
+
const id=pawaGenerateId(10)
|
|
60
|
+
const comment=document.createComment(`condition(${latestChain.id})@-$@-$@${id}`)
|
|
61
|
+
const endComment=document.createComment(`end condition(${latestChain.id})@-$@-$@${id}`)
|
|
62
|
+
el.replaceWith(endComment);
|
|
63
|
+
let stringHtml=''
|
|
64
|
+
const template=document.createElement('template')
|
|
65
|
+
const store=document.createElement('template')
|
|
66
|
+
chained.forEach((item) => {
|
|
67
|
+
const clone = item.element.cloneNode(true);
|
|
68
|
+
clone._avoidPawaRender = true;
|
|
69
|
+
Array.from(clone.attributes).forEach(at => {
|
|
70
|
+
if (at.name.startsWith('c-') || at.name === 'p:c') {
|
|
71
|
+
clone.removeAttribute(at.name);
|
|
84
72
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return this._resumeAttr+='c-t'
|
|
96
|
-
}
|
|
73
|
+
});
|
|
74
|
+
store.appendChild(clone);
|
|
75
|
+
template.appendChild(item.element);
|
|
76
|
+
});
|
|
77
|
+
const getRightElement=chainMap.get(latestChain.id)
|
|
78
|
+
if (getRightElement) {
|
|
79
|
+
const copyElement=getRightElement.element.cloneNode(true)
|
|
80
|
+
copyElement.attributes.forEach((att)=>{
|
|
81
|
+
if(att.name.startsWith('c-')){
|
|
82
|
+
copyElement.removeAttribute(att.name)
|
|
97
83
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
84
|
+
})
|
|
85
|
+
const dataComment=document.createComment(stringHtml)
|
|
86
|
+
let newElement = el.cloneNode(true);
|
|
87
|
+
if (attr.value === latestChain.id) {
|
|
88
|
+
el._replaceResumeAttr('if',`c-if-${id}`,latestChain.id)
|
|
89
|
+
newElement = el.cloneNode(true);
|
|
90
|
+
}else{
|
|
91
|
+
el._replaceResumeAttr('if',`c-if-${id}`,latestChain.id,copyElement)
|
|
92
|
+
newElement = copyElement;
|
|
93
|
+
}
|
|
94
|
+
newElement.removeAttribute(latestChain.condition)
|
|
95
|
+
newElement.setAttribute('pawa-same',true)
|
|
96
|
+
endComment.parentElement.insertBefore(comment,endComment)
|
|
97
|
+
stream(`<!--${comment.data}-->`)
|
|
98
|
+
store.setAttribute('p:store-if',id)
|
|
99
|
+
store.setAttribute('p:store','')
|
|
100
|
+
comment.parentElement.insertBefore(store,endComment)
|
|
101
|
+
stream(store.outerHTML)
|
|
102
|
+
comment.parentElement.insertBefore(newElement,endComment)
|
|
103
|
+
await render(newElement, el._context,stream);
|
|
104
|
+
stream(`<!--${endComment.data}-->`)
|
|
105
|
+
} else {
|
|
106
|
+
|
|
107
|
+
template.setAttribute('pawa-render',true)
|
|
108
|
+
endComment.replaceWith(template);
|
|
109
|
+
stream(`${template.outerHTML}`)
|
|
110
|
+
// await render(template, el._context,stream);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
export const Switch = async(el, attr,stream) => {
|
|
114
|
+
if (el._running) return;
|
|
115
|
+
el._running = true;
|
|
116
|
+
|
|
117
|
+
const nextSiblings = el.nextElementSibling || null;
|
|
118
|
+
const cases =el.getAttribute('case')
|
|
119
|
+
const chained=[{
|
|
120
|
+
exp:el.getAttribute('case'),
|
|
121
|
+
condition:'case',
|
|
122
|
+
element:el
|
|
123
|
+
}]
|
|
124
|
+
const chainMap=new Map()
|
|
125
|
+
chainMap.set(el.getAttribute('case'),{condition:'case',element:el})
|
|
126
|
+
const getChained = (sibling) => {
|
|
127
|
+
while (sibling) {
|
|
128
|
+
const next = sibling.nextElementSibling;
|
|
129
|
+
const isCase = sibling.hasAttribute('case') && !sibling.hasAttribute('switch');
|
|
130
|
+
const isDefault = sibling.hasAttribute('s-default');
|
|
131
|
+
|
|
132
|
+
if (isCase) {
|
|
133
|
+
const exp = sibling.getAttribute('case');
|
|
134
|
+
chained.push({ exp, condition: 'case', element: sibling });
|
|
135
|
+
chainMap.set(exp, { condition: 'case', element: sibling });
|
|
136
|
+
sibling.remove();
|
|
137
|
+
} else if (isDefault) {
|
|
138
|
+
chained.push({ exp: 'false', condition: 'default', element: sibling });
|
|
139
|
+
chainMap.set('default', { condition: 'default', element: sibling });
|
|
140
|
+
sibling.remove();
|
|
141
|
+
} else {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
sibling = next;
|
|
115
145
|
}
|
|
116
|
-
setTextResume()
|
|
117
|
-
}
|
|
118
|
-
this._el.setAttribute('p:c',this._resumeAttr)
|
|
119
146
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
147
|
+
getChained(nextSiblings)
|
|
148
|
+
let func=new Map()
|
|
149
|
+
let current
|
|
150
|
+
let latestChain
|
|
151
|
+
const switchFunc=el._evaluateExpr(attr.value,el._context,`at switch directive ${attr.value}`)
|
|
152
|
+
chained.forEach((item)=>{
|
|
153
|
+
if(item.condition === 'default' || current)return
|
|
154
|
+
const result = switchFunc === el._evaluateExpr(item.exp, el._context,`at condition directives check case - ${item.exp} ${item.element.toString()}`);
|
|
155
|
+
current=result
|
|
156
|
+
if (current || item.condition === 'default') {
|
|
157
|
+
latestChain={
|
|
158
|
+
id:item.exp,
|
|
159
|
+
condition:item.condition
|
|
160
|
+
}
|
|
161
|
+
}else{
|
|
162
|
+
latestChain={
|
|
163
|
+
id:'default',
|
|
164
|
+
condition:'default'
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
})
|
|
169
|
+
const document=el.ownerDocument
|
|
170
|
+
const id=pawaGenerateId(10)
|
|
171
|
+
const comment=document.createComment(`switch case (${latestChain.id})@-$@-$@${id}`)
|
|
172
|
+
const endComment=document.createComment(`end switch(${latestChain.id})@-$@-$@${id}`)
|
|
173
|
+
el.replaceWith(endComment);
|
|
174
|
+
let stringHtml=''
|
|
175
|
+
const template=document.createElement('template')
|
|
176
|
+
const store=document.createElement('template')
|
|
177
|
+
// let index=0
|
|
178
|
+
chained.forEach((item) => {
|
|
179
|
+
const clone = item.element.cloneNode(true);
|
|
180
|
+
clone._avoidPawaRender = true;
|
|
181
|
+
Array.from(clone.attributes).forEach(at => {
|
|
182
|
+
if (at.name.startsWith('c-') || at.name === 'p:c') {
|
|
183
|
+
clone.removeAttribute(at.name);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
store.appendChild(clone);
|
|
187
|
+
template.appendChild(item.element);
|
|
188
|
+
});
|
|
189
|
+
el.removeAttribute('switch')
|
|
190
|
+
const getRightElement=chainMap.get(latestChain.id)
|
|
191
|
+
if (getRightElement && (current || latestChain.condition === 'default')) {
|
|
192
|
+
const copyElement=getRightElement.element.cloneNode(true)
|
|
193
|
+
|
|
194
|
+
copyElement.attributes.forEach((att)=>{
|
|
195
|
+
if(att.name.startsWith('c-')){
|
|
196
|
+
copyElement.removeAttribute(att.name)
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
let newElement = el.cloneNode(true);
|
|
200
|
+
if (cases === latestChain.id) {
|
|
201
|
+
el._replaceResumeAttr(latestChain.condition,`c-sw-${id}`,latestChain.id)
|
|
202
|
+
newElement = el.cloneNode(true);
|
|
129
203
|
}else{
|
|
130
|
-
|
|
131
|
-
|
|
204
|
+
el._replaceResumeAttr(latestChain.condition === 's-default'?'s-default':latestChain.condition ,`c-sw-${id}`,latestChain.id,copyElement)
|
|
205
|
+
newElement = copyElement;
|
|
132
206
|
}
|
|
207
|
+
newElement.removeAttribute(latestChain.condition)
|
|
208
|
+
newElement.setAttribute('pawa-same',true)
|
|
209
|
+
store.setAttribute('p:store-switch',id)
|
|
210
|
+
store.setAttribute('p:store','')
|
|
211
|
+
endComment.parentElement.insertBefore(comment,endComment)
|
|
212
|
+
stream(`<!--${comment.data}-->`)
|
|
213
|
+
comment.parentElement.insertBefore(store,endComment)
|
|
214
|
+
stream(store.outerHTML)
|
|
215
|
+
comment.parentElement.insertBefore(newElement,endComment)
|
|
216
|
+
await render(newElement, el._context,stream);
|
|
217
|
+
stream(`<!--${endComment.data}-->`)
|
|
218
|
+
} else {
|
|
219
|
+
// If no case matches and no default, we just leave the comments
|
|
220
|
+
template.setAttribute('pawa-render',true)
|
|
221
|
+
endComment.parentElement.insertBefore(comment, endComment);
|
|
222
|
+
stream(`${template.outerHTML}`)
|
|
223
|
+
// stream(`<!--${comment.data}-->`)
|
|
224
|
+
comment.parentElement.insertBefore(template, endComment);
|
|
225
|
+
// await render(template, el._context,stream);
|
|
226
|
+
// No element rendered
|
|
227
|
+
// stream(`<!--${endComment.data}-->`)
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export const For=async(el,attr,stream)=>{
|
|
232
|
+
if(el._running){
|
|
233
|
+
return
|
|
133
234
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
235
|
+
el._running=true
|
|
236
|
+
try {
|
|
237
|
+
const value=attr.value
|
|
238
|
+
const document=el.ownerDocument
|
|
239
|
+
const dirId=pawaGenerateId(10)
|
|
240
|
+
const comment=document.createComment(`for(${value})@-$@-$@${dirId}`)
|
|
241
|
+
const endComment=document.createComment(`endfor(${value})@-$@-$@${dirId}`)
|
|
242
|
+
el.replaceWith(endComment)
|
|
243
|
+
const hasKey=el.getAttribute('for-key')
|
|
244
|
+
endComment.parentElement.insertBefore(comment,endComment)
|
|
245
|
+
// More robust split using regex to find the last occurrence of ' in ' or handle simple cases
|
|
246
|
+
const match = value.match(/^(.*?) in (.*)$/);
|
|
247
|
+
if (!match) throw new Error(`Invalid for loop syntax: ${value}`);
|
|
248
|
+
const [_, itemPart, arrayName] = match;
|
|
249
|
+
const arrayItems=itemPart.split(',')
|
|
250
|
+
const arrayItem=arrayItems[0].trim()
|
|
251
|
+
const indexes=arrayItems[1]
|
|
252
|
+
const array=el._evaluateExpr(arrayName,el._context,`at for directives check - ${attr.value}`)
|
|
253
|
+
const copyElement=el.cloneNode(true)
|
|
254
|
+
const store=[]
|
|
255
|
+
Array.from(copyElement.attributes).forEach(at=>{
|
|
256
|
+
if (at.name.startsWith('c-')) {
|
|
257
|
+
store.push(at)
|
|
258
|
+
copyElement.removeAttribute(at.name)
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const template=document.createElement('template')
|
|
263
|
+
const storeClone = copyElement.cloneNode(true)
|
|
264
|
+
storeClone._avoidPawaRender = true
|
|
265
|
+
template.appendChild(storeClone)
|
|
266
|
+
store.forEach(at=>{
|
|
267
|
+
copyElement.setAttribute(at.name,at.value)
|
|
268
|
+
})
|
|
269
|
+
const componentAttr={}
|
|
270
|
+
if(Array.isArray(array)){
|
|
271
|
+
if (array.length > 0) {
|
|
272
|
+
stream(`<!--${comment.data}-->`)
|
|
273
|
+
template.setAttribute('p:store-for',dirId)
|
|
274
|
+
template.setAttribute('p:store','')
|
|
275
|
+
endComment.parentElement.insertBefore(template,endComment)
|
|
276
|
+
stream(template.outerHTML)
|
|
277
|
+
el.attributes.forEach(attri =>{
|
|
278
|
+
if(attri.name.startsWith('c-')){
|
|
279
|
+
componentAttr[attri.name]=attri.value
|
|
145
280
|
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
hasForOrIf(){
|
|
150
|
-
if (this._el.getAttribute('if') || this._el.getAttribute('for-each') || this._el.getAttribute('else') || this._el.getAttribute('else-if')) {
|
|
151
|
-
return true
|
|
152
|
-
}else{
|
|
153
|
-
return false
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
getComponent(){
|
|
158
|
-
if (components.has(splitAndAdd(this._el.tagName.toUpperCase())) && !this._client || this._lazy) {
|
|
159
|
-
this._componentName=splitAndAdd(this._el.tagName.toUpperCase())
|
|
160
|
-
const fakeCompo=()=>true
|
|
161
|
-
this._component=new PawaComponent(components.get(splitAndAdd(this._el.tagName.toUpperCase())),fakeCompo)
|
|
162
|
-
Array.from(this._el.children).forEach(slot =>{
|
|
281
|
+
})
|
|
282
|
+
el._replaceResumeAttr('for-each',`c-for`,dirId)
|
|
283
|
+
el.removeAttribute('for-each')
|
|
163
284
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if(this._el.hasAttribute('only-client')){
|
|
172
|
-
this._el.removeAttribute('only-client')
|
|
285
|
+
// Fix: Use for...of to ensure await works correctly in SSR
|
|
286
|
+
for (const [index, item] of array.entries()) {
|
|
287
|
+
const context=el._context
|
|
288
|
+
const itemContext = {
|
|
289
|
+
[arrayItem]: item,
|
|
290
|
+
[indexes]: index,
|
|
291
|
+
...context
|
|
173
292
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
createError({message,stack}){
|
|
182
|
-
this._error.push({message,stack})
|
|
183
|
-
}
|
|
184
|
-
//set Component props
|
|
185
|
-
setProps(){
|
|
186
|
-
if (this._componentName) {
|
|
187
|
-
const allServerAttr=getPawaAttributes()
|
|
188
|
-
this._el.attributes.forEach(attr=>{
|
|
189
|
-
if(!allServerAttr.has(attr.name)){
|
|
190
|
-
if ( !attr.name.startsWith(':')) {
|
|
191
|
-
if( attr.name.startsWith('c-') || attr.name.startsWith('p:c') || attr.name.startsWith('state-')) return
|
|
192
|
-
let name=''
|
|
193
|
-
if (attr.name.startsWith('-')) {
|
|
194
|
-
name=attr.name.slice(1)
|
|
195
|
-
}else{
|
|
196
|
-
name=attr.name
|
|
197
|
-
}
|
|
198
|
-
let hydatename
|
|
199
|
-
if(name === 'class'){
|
|
200
|
-
hydatename=':'+'className'
|
|
201
|
-
}else if(name === 'default'){
|
|
202
|
-
hydatename=':'+'defaultValue'
|
|
203
|
-
}else{
|
|
204
|
-
hydatename=':'+name
|
|
205
|
-
}
|
|
206
|
-
this._hydrateProps[hydatename]=attr.value
|
|
207
|
-
const setProps=()=>{
|
|
208
|
-
let value=attr.value
|
|
209
|
-
if (value.includes('@{')) {
|
|
210
|
-
const regex = /@{([^}]*)}/g;
|
|
211
|
-
value = value.replace(regex, (match, expression) => {
|
|
212
|
-
|
|
213
|
-
const res = this.evaluateExpr(expression,this._context,`evaluating props with template operators at ${attr.name} - ${attr.value} : ${this._template}`)
|
|
214
|
-
return res
|
|
215
|
-
|
|
216
|
-
});
|
|
217
|
-
return value
|
|
218
|
-
}else if( attr.name.startsWith('on-') || attr.name.startsWith('out-') || attr.name === 'ref'){
|
|
219
|
-
const res=this.evaluateExpr(`(e)=>{
|
|
220
|
-
${attr.value}
|
|
221
|
-
}`, this._context,`evaluating props with template operators at ${attr.name} - ${attr.value} : ${this._template}`)
|
|
222
|
-
return res
|
|
223
|
-
}
|
|
224
|
-
return attr.value
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
this._restProps[name]={name:name,value:attr.value}
|
|
228
|
-
name=name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
229
|
-
if (this._props[name] || name ==='class' && this._props?.className || name ==='default' && this._props.defaultValue) return
|
|
230
|
-
if (name === 'class') {
|
|
231
|
-
this._props['className']=setProps
|
|
232
|
-
}else if(name === 'default'){
|
|
233
|
-
this._props['defaultValue']=setProps
|
|
234
|
-
}else{
|
|
235
|
-
this._props[name]=setProps
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
} else if(attr.name.startsWith(':')) {
|
|
239
|
-
this._hydrateProps[attr.name.slice(1)]=attr.value
|
|
240
|
-
if(attr.value === '') attr.value=true;
|
|
241
|
-
try {
|
|
242
|
-
const func = this.evaluateExpr(`()=>{
|
|
243
|
-
const prop= ${replaceTemplateOperators(attr.value)};
|
|
244
|
-
if(prop === '')return prop
|
|
245
|
-
return prop
|
|
246
|
-
}
|
|
247
|
-
`,this._context,`setting props at ${attr.name} - ${attr.value} : ${this._template}`)
|
|
248
|
-
let name=attr.name.slice(1)
|
|
249
|
-
if(name.includes('-')){
|
|
250
|
-
name=name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
251
|
-
}
|
|
252
|
-
this._props[name]=func
|
|
253
|
-
} catch (error) {
|
|
254
|
-
console.log(error.message,error.stack)
|
|
255
|
-
__pawaDev.setError({
|
|
256
|
-
el:this._el,
|
|
257
|
-
msg:`errors while setting props at runtime ${error.message}`,
|
|
258
|
-
directives:'setting props',
|
|
259
|
-
stack:error.stack,
|
|
260
|
-
template:el?._template,
|
|
261
|
-
})
|
|
262
|
-
}
|
|
293
|
+
itemContext[arrayItem]=item
|
|
294
|
+
const newElement=el.cloneNode(true)
|
|
295
|
+
if(index !== 0){
|
|
296
|
+
newElement.removeAttribute('c-for')
|
|
297
|
+
for (const [key,value] of Object.entries(componentAttr)) {
|
|
298
|
+
if(newElement.hasAttribute(key)){ //removing element with resuming attri from second -- for element
|
|
299
|
+
newElement.removeAttribute(key)
|
|
263
300
|
}
|
|
264
301
|
}
|
|
265
|
-
}
|
|
302
|
+
}
|
|
303
|
+
newElement.setAttribute('pawa-same',true)
|
|
304
|
+
processNode(newElement,itemContext)
|
|
305
|
+
|
|
306
|
+
const key=newElement.getAttribute('for-key')
|
|
307
|
+
const keyComment=document.createComment(`forKey@-$@-$@${dirId}@-$@-$@${key || index}`)
|
|
308
|
+
const endKeyComment=document.createComment(`endForKey@-$@-$@${dirId}@-$@-$@${key || index}`)
|
|
309
|
+
endComment.parentElement.insertBefore(endKeyComment,endComment)
|
|
310
|
+
endKeyComment.parentElement.insertBefore(keyComment,endKeyComment)
|
|
311
|
+
stream(`<!--${keyComment.data}-->`)
|
|
312
|
+
endKeyComment.parentElement.insertBefore(newElement,endKeyComment)
|
|
313
|
+
await render(newElement,itemContext,stream)
|
|
314
|
+
stream(`<!--${endKeyComment.data}-->`)
|
|
266
315
|
}
|
|
316
|
+
stream(`<!--${endComment.data}-->`)
|
|
317
|
+
}else{
|
|
318
|
+
template.setAttribute('pawa-render',true)
|
|
319
|
+
stream(`<template pawa-render="true">${el.outerHTML}</template>`)
|
|
320
|
+
template.appendChild(el)
|
|
321
|
+
comment.replaceWith(template)
|
|
322
|
+
endComment.remove()
|
|
267
323
|
}
|
|
268
|
-
evaluateExpr(expr, context = {},error){
|
|
269
|
-
try {
|
|
270
|
-
const keys = Object.keys(context);
|
|
271
|
-
// Create a cache key based on the expression and the available context keys
|
|
272
|
-
// We sort keys to ensure consistent cache hits regardless of key order
|
|
273
|
-
const cacheKey = expr + '|||' + keys.sort().join(',');
|
|
274
|
-
|
|
275
|
-
let func = expressionCache.get(cacheKey);
|
|
276
|
-
if (!func) {
|
|
277
|
-
func = new Function(...keys, `
|
|
278
|
-
const require=null
|
|
279
|
-
return ${expr}`);
|
|
280
|
-
expressionCache.set(cacheKey, func);
|
|
281
324
|
}
|
|
325
|
+
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error(error.message,error.stack)
|
|
328
|
+
__pawaDev.setError({
|
|
329
|
+
el:el,
|
|
330
|
+
msg:`error from for-each ${attr.value}`+ error.message + error.stack,
|
|
331
|
+
directives:'for-each',
|
|
332
|
+
stack:error.stack,
|
|
333
|
+
template:el?._template,
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
282
338
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
console.error(`Evaluation failed for: ${expr}`,error,err.message,err.stack);
|
|
287
|
-
__pawaDev.setError({
|
|
288
|
-
el:this._el,
|
|
289
|
-
msg:`${error} ${err.message}`,
|
|
290
|
-
directives:'expression',
|
|
291
|
-
stack:err.stack,
|
|
292
|
-
template:this._el?._template,
|
|
293
|
-
})
|
|
294
|
-
return null;
|
|
339
|
+
export const State=async(el,attr)=>{
|
|
340
|
+
if (el._running) {
|
|
341
|
+
return
|
|
295
342
|
}
|
|
296
|
-
|
|
343
|
+
try {
|
|
344
|
+
|
|
345
|
+
const name=attr.name.split('-')[1]
|
|
346
|
+
el._replaceResumeAttr(attr.name,`c-$-${name}`,attr.value)
|
|
347
|
+
el.removeAttribute(attr.name)
|
|
348
|
+
const result=el._evaluateExpr(attr.value,el._context,`at state directive ${attr.name}=${attr.value}`)
|
|
349
|
+
// el.setAttribute(`resume-state-${name}`,attr.value)
|
|
350
|
+
el._context[name]={value:result}
|
|
351
|
+
} catch (error) {
|
|
352
|
+
console.log(error.message,error.stack)
|
|
353
|
+
__pawaDev.setError({
|
|
354
|
+
el:el,
|
|
355
|
+
msg:`error from state ${attr.name} : ${attr.value}`+ error.message + error.stack,
|
|
356
|
+
directives:el.tagName,
|
|
357
|
+
stack:error.stack,
|
|
358
|
+
template:el?._template,
|
|
359
|
+
})
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export const Key=async(el,attr,stream)=>{
|
|
365
|
+
if(el._running){
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
el._running=true
|
|
369
|
+
try {
|
|
370
|
+
const value=attr.value
|
|
371
|
+
const document=el.ownerDocument
|
|
372
|
+
const dirId=pawaGenerateId(10)
|
|
373
|
+
const comment=document.createComment(`key`)
|
|
374
|
+
const endComment=document.createComment(`key`)
|
|
375
|
+
const clone=el.cloneNode(true)
|
|
376
|
+
clone._avoidPawaRender = true
|
|
377
|
+
const template=document.createElement('template')
|
|
378
|
+
template.setAttribute('p:store-key',dirId)
|
|
379
|
+
template.setAttribute('p:store','')
|
|
380
|
+
Array.from(clone.attributes).forEach(at => {
|
|
381
|
+
if (at.name.startsWith('c-') || at.name === 'p:c') {
|
|
382
|
+
clone.removeAttribute(at.name);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
template.appendChild(clone)
|
|
386
|
+
el.replaceWith(endComment)
|
|
387
|
+
endComment.parentElement.insertBefore(comment,endComment)
|
|
388
|
+
const func=el._evaluateExpr(attr.value,el._context,`error at Key - ${attr.name} = ${attr.value}`)
|
|
389
|
+
endComment.parentElement.insertBefore(template,endComment)
|
|
390
|
+
comment.data=`key(${func})@-$@-$@${dirId}`
|
|
391
|
+
endComment.data=`key(${func})@-$@-$@${dirId}`
|
|
392
|
+
stream(`<!--${comment.data}-->`)
|
|
393
|
+
stream(template.outerHTML)
|
|
394
|
+
el._replaceResumeAttr('key',`c-key-${dirId}`,typeof func === 'string'?`'${func}'`:func)
|
|
395
|
+
el.removeAttribute(attr.name)
|
|
396
|
+
const newElement=el.cloneNode(true)
|
|
397
|
+
endComment.parentElement.insertBefore(newElement,endComment)
|
|
398
|
+
await render(newElement,el._context,stream)
|
|
399
|
+
stream(`<!--${endComment.data}-->`)
|
|
400
|
+
}catch(error){
|
|
401
|
+
console.error(error.message,error.stack)
|
|
402
|
+
__pawaDev.setError({
|
|
403
|
+
el:el,
|
|
404
|
+
msg:`error from Key at ${attr.value}`+ error.message + error.stack,
|
|
405
|
+
directives:el.tagName,
|
|
406
|
+
stack:error.stack,
|
|
407
|
+
template:el?._template,
|
|
408
|
+
})
|
|
297
409
|
}
|
|
298
|
-
|
|
410
|
+
}
|
package/test/App.js
ADDED
package/test/check.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const Check=()=>{
|
|
2
|
+
return `<div class="p-4">
|
|
3
|
+
<h1 class="text-2xl font-bold mb-4">Hello, World!</h1>
|
|
4
|
+
<p class="text-gray-700">This is a test Checker</p>
|
|
5
|
+
</div>`
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Check2=()=>{
|
|
9
|
+
return `<div class="p-4">
|
|
10
|
+
<h1 class="text-2xl font-bold mb-4">Hello, World!</h1>
|
|
11
|
+
<p class="text-gray-700">This is a test Checker 2</p>
|
|
12
|
+
</div>`
|
|
13
|
+
}
|
package/test/index.js
CHANGED
|
@@ -1,67 +1,18 @@
|
|
|
1
1
|
import {useValidateComponent, RegisterComponent,useContext,useInsert,setContext} from 'pawajs'
|
|
2
2
|
import {startApp} from '../index.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
useInsert({user,array,name})
|
|
9
|
-
return `
|
|
10
|
-
<div>
|
|
11
|
-
<h1 title="@{user ? name():'Guest'}" -->
|
|
12
|
-
<span>@{name()}</span>
|
|
13
|
-
<span if="user">Allwell</span>
|
|
14
|
-
<span else>false</span>
|
|
15
|
-
</h1>
|
|
16
|
-
<div s-for="items in array" s-pawa-avoid>
|
|
17
|
-
<span>@{items}</span>
|
|
18
|
-
</div>
|
|
19
|
-
<small-app></small-app>
|
|
20
|
-
</div>
|
|
21
|
-
`
|
|
22
|
-
}
|
|
23
|
-
const SmallApp=()=>{
|
|
24
|
-
const {name}=useContext(auth)
|
|
25
|
-
useInsert({name})
|
|
26
|
-
return /*html*/`
|
|
27
|
-
<div state-count="0">
|
|
28
|
-
<h1>Small App Component</h1>
|
|
29
|
-
<span>@{name}</span>
|
|
30
|
-
<h1>@{count.value}</h1>
|
|
31
|
-
<new-app></new-app>
|
|
32
|
-
</div>`
|
|
33
|
-
}
|
|
34
|
-
useValidateComponent(component,{
|
|
35
|
-
name:{
|
|
36
|
-
strict:true,
|
|
37
|
-
type:String
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
const NewApp=()=>{
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
RegisterComponent(component,SmallApp,NewApp)
|
|
44
|
-
const html=()=>{
|
|
45
|
-
return `
|
|
46
|
-
<!doctype html>
|
|
47
|
-
<html lang="en">
|
|
48
|
-
<head>
|
|
49
|
-
<meta charset="UTF-8" />
|
|
50
|
-
<link rel="icon" type="image/svg+xml" href="/pawajs.svg" />
|
|
51
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
52
|
-
<title>pawajs-template</title>
|
|
53
|
-
</head>
|
|
54
|
-
<body>
|
|
55
|
-
<div id="app">
|
|
56
|
-
<div>
|
|
57
|
-
<component :name="'Allwell'"></component>
|
|
58
|
-
</div>
|
|
3
|
+
RegisterComponent.lazy(
|
|
4
|
+
'App',()=>import('./App.js'),
|
|
5
|
+
'Check',()=>import('./check.js'),
|
|
6
|
+
'Check2',()=>import('./check.js'),
|
|
7
|
+
)
|
|
59
8
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
9
|
+
const app=`
|
|
10
|
+
<div>
|
|
11
|
+
<app>
|
|
12
|
+
<check></check>
|
|
13
|
+
<check-2></check-2>
|
|
14
|
+
</app>
|
|
15
|
+
</div>
|
|
16
|
+
`
|
|
17
|
+
const {toString}=await startApp(app)
|
|
18
|
+
console.log(await toString());
|