pawa-ssr 1.3.21 → 1.3.23

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 (2) hide show
  1. package/package.json +1 -1
  2. package/power.js +389 -277
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pawa-ssr",
3
- "version": "1.3.21",
3
+ "version": "1.3.23",
4
4
  "type":"module",
5
5
  "description": "pawajs ssr libary",
6
6
  "main": "index.js",
package/power.js CHANGED
@@ -1,298 +1,410 @@
1
- import { HTMLElement, parseHTML } from "linkedom"
2
- import {getPawaAttributes, getDevelopment } from "./index.js"
3
- import {components,lazyComponents} from '/pawajs/index.js'
4
- import PawaComponent from "./pawaComponent.js"
5
- import { evaluateExpr, splitAndAdd,replaceTemplateOperators } from "./utils.js"
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 expressionCache = new Map();
8
- // changed the hydrateProps
9
-
10
- class PawaElement {
11
- /**
12
- *
13
- * @param {HTMLElement} element
14
- * @param {object} context
15
- */
16
- constructor(element,context) {
17
- const {document}=parseHTML()
18
- /**
19
- * @type{PawaElement|HTMLElement}
20
- */
21
- this._el=element
22
- this._slots=document.createDocumentFragment()
23
- this._context=context
24
- this._avoidPawaRender=element.hasAttribute('s-pawa-avoid')
25
- this._client=element.hasAttribute('client')
26
- this._props={}
27
- this._template=element.outerHTML
28
- this._component=null
29
- this._componentName=''
30
- this._thisSame=element.hasAttribute('pawa-same')
31
- this._arrangeAttribute={}
32
- this._pawaAlready=element.hasAttribute('p:c')
33
- /**@type {Array<{message:string,stack:string}>} */
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
- attributes(){
77
- this._el.attributes.forEach((value, index,) => {
78
- this._arrangeAttribute[value.name]=value.value
79
- })
80
- }
81
- checkLazy(){
82
- if (lazyComponents.has(splitAndAdd(this._el.tagName))) {
83
- this._lazy=true
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
- setResumeAttr(name){
87
- if(name.startsWith(':')) return
88
- this._resumeAttr+=`${name};`
89
- this._el.setAttribute('p:C',this._resumeAttr)
90
- }
91
- pawaAttribute(){
92
- const pawaAttr=getPawaAttributes()
93
- const setTextResume=()=>{
94
- if( this._componentName === ''&& this._el.firstElementChild === null && this._el.childNodes.some(node=>node.nodeType === 3 && node.nodeValue.includes('@{')) && !this._avoidPawaRender){
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
- if (this._thisSame) {
99
- this._resumeAttr=this._el.getAttribute('p:c')
100
- this._el.removeAttribute('pawa-same')
101
- }else {
102
- if (this._el.hasAttribute('p:c')) {
103
- this._resumeAttr=this._el.getAttribute('p:c')
104
- this._el.attributes.forEach((value, index, array) => {
105
- if(this._resumeAttr.includes(value.name) || value.name === 'p:c')return
106
- if (value.name.startsWith(':')) return
107
- this._resumeAttr+=`${value.name};`
108
- })
109
- }else{
110
- this._el.attributes.forEach((value, index, array) => {
111
- if(this._resumeAttr.includes(value.name) || value.name === 'p:c' )return
112
- if (value.name.startsWith(':')) return
113
- this._resumeAttr+=`${value.name};`
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
- replaceResumeAttr(name,newName,value,ele){
121
- if (ele === undefined) {
122
- this._el.setAttribute(newName,value)
123
- const array=this._resumeAttr.split(';')
124
- const index=array.indexOf(name,0)
125
- array[index]=newName
126
- const toString=array.join(';')
127
- this._el.setAttribute('p:c',toString)
128
- this._resumeAttr=toString
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
- ele.setAttribute(newName,value)
131
- ele.setAttribute('p:c',`${newName};`)
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
- reArrange(name,value,replace){
135
- const newAttribute={}
136
- if(this._arrangeAttribute){
137
- this._el.attributes.forEach((value, index) => {
138
- this._el.removeAttribute(value.name)
139
- })
140
- for (const [key,value] of Object.entries(this._arrangeAttribute)) {
141
- if (key === replace) {
142
- this._el.setAttribute(name,value)
143
- }else{
144
- this._el.setAttribute(key,value)
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
- if (slot.tagName === 'TEMPLATE' && slot.getAttribute('prop')) {
165
-
166
- this._slots.appendChild(slot)
167
- }
168
- })
169
- this._componentChildren=this._el.innerHTML
170
- }else{
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
- setError(){
177
- if (getDevelopment() && this._error.length > 0) {
178
- this._el.setAttribute('ssr-error',JSON.stringify(this._error))
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
- const values = keys.map((key) => context[key]);
284
- return func(...values);
285
- } catch (err) {
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
- export default PawaElement
410
+ }