pawa-ssr 1.2.5 → 1.2.6
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 +550 -46
- package/package.json +1 -1
- package/pawaElement.js +4 -1
- package/power.js +36 -12
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {getServerInstance, setServer} from 'pawajs/server.js'
|
|
1
|
+
import {getServerInstance, setServer} from '../src/pawajs/server.js'
|
|
2
2
|
import { DOMParser,parseHTML, HTMLElement} from 'linkedom'
|
|
3
3
|
import PawaComponent from './pawaComponent.js'
|
|
4
4
|
import { propsValidator, evaluateExpr,extractAtExpressions, reArrangeAttri,resumeAttribute, pawaGenerateId } from './utils.js'
|
|
@@ -22,15 +22,39 @@ const useInsert=(obj={})=>{
|
|
|
22
22
|
Object.assign(appContext.stateContext.insert, obj);
|
|
23
23
|
}
|
|
24
24
|
}catch(error){
|
|
25
|
-
if(
|
|
25
|
+
if(__pawaDev){
|
|
26
26
|
console.log(error.message,error.stack)
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
const useAsync=()=>{
|
|
31
|
+
try {
|
|
32
|
+
const appContext=store.getStore();
|
|
33
|
+
if (appContext?.stateContext) {
|
|
34
|
+
const keep=appContext?.stateContext
|
|
35
|
+
return {
|
|
36
|
+
$async:(callback)=>{
|
|
37
|
+
if (typeof callback === 'function') {
|
|
38
|
+
store.getStore().stateContext=keep
|
|
39
|
+
const res=callback()
|
|
40
|
+
return res
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
onSuspense:(html)=>{
|
|
44
|
+
if (typeof html === 'string') {
|
|
45
|
+
keep.suspense=html
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
}
|
|
31
54
|
const setContext=()=>{
|
|
32
55
|
const id=crypto.randomUUID()
|
|
33
56
|
const setValue=(context={})=>{
|
|
57
|
+
|
|
34
58
|
try{
|
|
35
59
|
const appContext = store.getStore();
|
|
36
60
|
if(appContext?.stateContext?.transportContext){
|
|
@@ -60,18 +84,20 @@ const accessChild=()=>{
|
|
|
60
84
|
}
|
|
61
85
|
}
|
|
62
86
|
const useServer=()=>{
|
|
87
|
+
const appContext = store.getStore().stateContext;
|
|
63
88
|
try{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
appContext.
|
|
89
|
+
if (appContext) {
|
|
90
|
+
appContext.useServer=true
|
|
91
|
+
appContext.serializeData = true;
|
|
67
92
|
const setServerData=(data={})=>{
|
|
93
|
+
|
|
68
94
|
for (const [key,value] of Object.entries(data)) {
|
|
69
95
|
if (typeof value !== 'function') {
|
|
70
|
-
appContext.
|
|
96
|
+
appContext.fromSerialized[key]=value
|
|
71
97
|
}
|
|
72
98
|
}
|
|
73
99
|
}
|
|
74
|
-
const getServerData=()=>appContext.
|
|
100
|
+
const getServerData=()=>appContext.fromSerialized
|
|
75
101
|
return {setServerData,getServerData}
|
|
76
102
|
}
|
|
77
103
|
}catch(error){
|
|
@@ -85,6 +111,7 @@ const useContext=(context)=>{
|
|
|
85
111
|
const appContext = store.getStore();
|
|
86
112
|
const id=context?.id
|
|
87
113
|
if(appContext?.stateContext?.transportContext && id){
|
|
114
|
+
|
|
88
115
|
return appContext.stateContext.transportContext[id];
|
|
89
116
|
}
|
|
90
117
|
return {}
|
|
@@ -129,9 +156,10 @@ setServer({
|
|
|
129
156
|
setContext,
|
|
130
157
|
$state,
|
|
131
158
|
accessChild,
|
|
132
|
-
useServer
|
|
159
|
+
useServer,
|
|
160
|
+
useAsync
|
|
133
161
|
})
|
|
134
|
-
|
|
162
|
+
export const pawaForServer=setServer
|
|
135
163
|
const components = new Map();
|
|
136
164
|
export const getPawaComponentsMap =()=>{
|
|
137
165
|
return components ;
|
|
@@ -267,7 +295,7 @@ export const useValidateComponent=(component,object)=>{
|
|
|
267
295
|
* @param {import('./pawaElement.js').default} el
|
|
268
296
|
* @returns
|
|
269
297
|
*/
|
|
270
|
-
const component=async (el)=>{
|
|
298
|
+
const component=async (el,stream)=>{
|
|
271
299
|
if(el._running){
|
|
272
300
|
return
|
|
273
301
|
}
|
|
@@ -276,7 +304,8 @@ const component=async (el)=>{
|
|
|
276
304
|
const document = el.ownerDocument
|
|
277
305
|
const slots={}
|
|
278
306
|
/**@type {AppStateContextType} */
|
|
279
|
-
const oldAppContext=getStore().stateContext
|
|
307
|
+
const oldAppContext=store.getStore().stateContext
|
|
308
|
+
// console.log(oldAppContext)
|
|
280
309
|
let stateContext={}
|
|
281
310
|
let appContext={
|
|
282
311
|
transportContext: {},
|
|
@@ -289,7 +318,12 @@ const component=async (el)=>{
|
|
|
289
318
|
accessChild:false,
|
|
290
319
|
serializeData:false,
|
|
291
320
|
serialize:{},
|
|
292
|
-
fromSerialized:{}
|
|
321
|
+
fromSerialized:{},
|
|
322
|
+
useServer:false,
|
|
323
|
+
suspense:'',
|
|
324
|
+
carrier:'',
|
|
325
|
+
pc:el.getAttribute('p:c')
|
|
326
|
+
|
|
293
327
|
}
|
|
294
328
|
Object.assign(appContext.transportContext, oldAppContext?.transportContext || {})
|
|
295
329
|
const slotHydrates={}
|
|
@@ -341,22 +375,22 @@ appContext.component._prop={children,...el._props,...slots}
|
|
|
341
375
|
...el._props
|
|
342
376
|
}
|
|
343
377
|
for (const fn of compoBeforeCall) {
|
|
344
|
-
|
|
378
|
+
try {
|
|
345
379
|
await fn(stateContext,app)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
380
|
+
} catch (error) {
|
|
381
|
+
console.error(`Error in beforeCall for ${el._componentName}:`, error.message,error.stack)
|
|
382
|
+
__pawaDev.setError({
|
|
349
383
|
el:el,
|
|
350
384
|
msg:`from compoBeforeCall${el._componentName}:`+ error.message + error.stack,
|
|
351
385
|
directives:'plugin',
|
|
352
386
|
stack:error.stack,
|
|
353
387
|
template:el?._template,
|
|
354
388
|
})
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const div=document.createElement('div')
|
|
393
|
+
el._setResumeAttr(`c-compo-${el._componentName}-${id}`)
|
|
360
394
|
let compo=""
|
|
361
395
|
try{
|
|
362
396
|
if(done){
|
|
@@ -375,11 +409,11 @@ appContext.component._prop={children,...el._props,...slots}
|
|
|
375
409
|
template:el?._template,
|
|
376
410
|
})
|
|
377
411
|
}
|
|
378
|
-
|
|
412
|
+
if (appContext?.insert){
|
|
379
413
|
Object.assign(el._context,appContext.insert)
|
|
380
414
|
}
|
|
381
|
-
|
|
382
|
-
if(typeof compo !== 'boolean' && compo){
|
|
415
|
+
|
|
416
|
+
if(typeof compo !== 'boolean' && typeof compo === 'string'){
|
|
383
417
|
div.innerHTML=compo
|
|
384
418
|
}
|
|
385
419
|
const findElement=div.querySelector('[--]') || div.querySelector('[r-]')
|
|
@@ -403,7 +437,7 @@ appContext.component._prop={children,...el._props,...slots}
|
|
|
403
437
|
}
|
|
404
438
|
}
|
|
405
439
|
|
|
406
|
-
|
|
440
|
+
|
|
407
441
|
hydrate.data=null
|
|
408
442
|
if (appContext.serializeData) { // get serialized data
|
|
409
443
|
for (const [key,value] of Object.entries(appContext.fromSerialized)) {
|
|
@@ -417,12 +451,232 @@ appContext.component._prop={children,...el._props,...slots}
|
|
|
417
451
|
for (const [key] of Object.entries(appContext.insert)) {
|
|
418
452
|
hydrate.context.push(key)
|
|
419
453
|
}
|
|
454
|
+
|
|
420
455
|
comment.data=`component+${id}+${el._componentName}+${encodeJSON(hydrate)}`
|
|
421
456
|
|
|
422
457
|
for (const newElement of newElements) {
|
|
423
458
|
comment.parentElement.insertBefore(newElement, endComment)
|
|
424
|
-
|
|
459
|
+
|
|
460
|
+
newElement.setAttribute('p:c', el.getAttribute('p:c'))
|
|
461
|
+
Array.from(el.attributes).forEach((value) => {
|
|
462
|
+
if (value.name.startsWith('c-')) {
|
|
463
|
+
newElement.setAttribute(value.name, value.value)
|
|
464
|
+
}
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
newElement.setAttribute(`c-c-${el._componentName}-${id}`, id)
|
|
468
|
+
await render(newElement, el._context,stream)
|
|
469
|
+
|
|
470
|
+
}
|
|
471
|
+
store.getStore().stateContext=appContext.formerContext
|
|
472
|
+
} catch (error) {
|
|
473
|
+
console.log(error.message,error.stack);
|
|
474
|
+
__pawaDev.setError({
|
|
475
|
+
el:el,
|
|
476
|
+
msg:`from ${el.tagName} component`,
|
|
477
|
+
directives:'component',
|
|
478
|
+
stack:error.stack,
|
|
479
|
+
template:el?._template,
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
*
|
|
486
|
+
* @param {import('./pawaElement.js').default} el
|
|
487
|
+
* @returns
|
|
488
|
+
*/
|
|
489
|
+
const streamingComponent=async (el,stream)=>{
|
|
490
|
+
if(el._running){
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
const slot=el._slots
|
|
495
|
+
const document = el.ownerDocument
|
|
496
|
+
const slots={}
|
|
497
|
+
/**@type {AppStateContextType} */
|
|
498
|
+
const oldAppContext=store.getStore().stateContext
|
|
499
|
+
// console.log(oldAppContext)
|
|
500
|
+
let stateContext={}
|
|
501
|
+
let appContext={
|
|
502
|
+
transportContext: {},
|
|
503
|
+
innerContext:el._context,
|
|
504
|
+
mount:[],
|
|
505
|
+
formerContext:oldAppContext,
|
|
506
|
+
name:el._componentName,
|
|
507
|
+
insert:{},
|
|
508
|
+
component:el._component,
|
|
509
|
+
accessChild:false,
|
|
510
|
+
serializeData:false,
|
|
511
|
+
serialize:{},
|
|
512
|
+
fromSerialized:{},
|
|
513
|
+
useServer:false,
|
|
514
|
+
suspense:'',
|
|
515
|
+
carrier:'',
|
|
516
|
+
pc:el.getAttribute('p:c')
|
|
517
|
+
|
|
518
|
+
}
|
|
519
|
+
Object.assign(appContext.transportContext, oldAppContext?.transportContext || {})
|
|
520
|
+
const slotHydrates={}
|
|
521
|
+
Array.from(slot.children).forEach(prop =>{
|
|
522
|
+
if (prop.getAttribute('prop')) {
|
|
523
|
+
const html=prop.innerHTML
|
|
524
|
+
slots[prop.getAttribute('prop')]=()=>html
|
|
525
|
+
slotHydrates[prop.getAttribute('prop')]=html
|
|
526
|
+
}else{
|
|
527
|
+
console.warn('sloting props must have prop attribute')
|
|
528
|
+
}
|
|
529
|
+
})
|
|
530
|
+
const children=el._componentChildren
|
|
531
|
+
const hydrate={
|
|
532
|
+
children:children,
|
|
533
|
+
props:{
|
|
534
|
+
...el._hydrateProps,
|
|
535
|
+
},
|
|
536
|
+
slots:{...slotHydrates},
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const id=pawaGenerateId(10)
|
|
540
|
+
const encodeJSON = (obj) => Buffer.from(JSON.stringify(obj)).toString('base64').replace(/\+/g, '-');
|
|
541
|
+
const comment = document.createComment(`component+${id}`);
|
|
542
|
+
const endComment=document.createComment(`end component+${id}+${el._componentName}`)
|
|
543
|
+
el.replaceWith(endComment)
|
|
544
|
+
endComment.parentElement.insertBefore(comment,endComment)
|
|
545
|
+
/**
|
|
546
|
+
* @type {import('./pawaComponent.js').default}
|
|
547
|
+
*/
|
|
548
|
+
const component =el._component
|
|
549
|
+
stateContext=component
|
|
550
|
+
/**
|
|
551
|
+
*
|
|
552
|
+
* @param {object} props
|
|
553
|
+
* @returns {object}
|
|
554
|
+
*/
|
|
555
|
+
stateContext._name=el._componentName
|
|
556
|
+
|
|
557
|
+
let done=true
|
|
558
|
+
if(Object.entries(el._component.validPropRule).length > 0){
|
|
559
|
+
done=propsValidator(el._component.validPropRule,{...el._props,...slots},appContext.component._name,el.toString(),el)
|
|
560
|
+
}
|
|
561
|
+
appContext.component._prop={children,...el._props,...slots}
|
|
562
|
+
|
|
563
|
+
const app = {
|
|
564
|
+
children,
|
|
565
|
+
...slots,
|
|
566
|
+
...el._props
|
|
567
|
+
}
|
|
568
|
+
for (const fn of compoBeforeCall) {
|
|
569
|
+
try {
|
|
570
|
+
await fn(stateContext,app)
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error(`Error in beforeCall for ${el._componentName}:`, error.message,error.stack)
|
|
573
|
+
__pawaDev.setError({
|
|
574
|
+
el:el,
|
|
575
|
+
msg:`from compoBeforeCall${el._componentName}:`+ error.message + error.stack,
|
|
576
|
+
directives:'plugin',
|
|
577
|
+
stack:error.stack,
|
|
578
|
+
template:el?._template,
|
|
579
|
+
})
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const div=document.createElement('div')
|
|
584
|
+
el._setResumeAttr(`c-compo-${el._componentName}-${id}`)
|
|
585
|
+
let compo=""
|
|
586
|
+
let isBoundary=false
|
|
587
|
+
try{
|
|
588
|
+
if(done){
|
|
589
|
+
|
|
590
|
+
store.getStore().stateContext=appContext
|
|
591
|
+
compo=component.component(app)
|
|
592
|
+
isBoundary=appContext.suspense && appContext.useServer
|
|
593
|
+
if (compo instanceof Promise && !isBoundary) {
|
|
594
|
+
compo=await compo.then((res)=>res)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
}catch(error){
|
|
599
|
+
console.error(`error from PawaComponent.${appContext.component._name}`,error.message,error.stack)
|
|
600
|
+
__pawaDev.setError({
|
|
601
|
+
el:el,
|
|
602
|
+
msg:`error from PawaComponent.${appContext.component._name}`+ error.message + error.stack,
|
|
603
|
+
directives:el.tagName,
|
|
604
|
+
stack:error.stack,
|
|
605
|
+
template:el?._template,
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
if (appContext?.insert){
|
|
609
|
+
Object.assign(el._context,appContext.insert)
|
|
610
|
+
}
|
|
611
|
+
if (isBoundary) {
|
|
612
|
+
store.getStore().batch.push({
|
|
613
|
+
component:compo,
|
|
614
|
+
id:id,
|
|
615
|
+
comment:{
|
|
616
|
+
hydrate:hydrate,
|
|
617
|
+
encodeJSON:encodeJSON,
|
|
618
|
+
name:el._componentName
|
|
619
|
+
},
|
|
620
|
+
appContext:appContext,
|
|
621
|
+
context:{...el._context},
|
|
622
|
+
restProps:el._restProps,
|
|
623
|
+
hydrate:hydrate
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
}
|
|
627
|
+
if(isBoundary){
|
|
628
|
+
compo=`<div id="p${id}">${appContext.suspense}</div>`
|
|
629
|
+
}
|
|
630
|
+
if(typeof compo !== 'boolean' && typeof compo === 'string'){
|
|
631
|
+
div.innerHTML=compo
|
|
632
|
+
}
|
|
633
|
+
const findElement=div.querySelector('[--]') || div.querySelector('[r-]')
|
|
634
|
+
if (findElement) {
|
|
635
|
+
for (const [key,value] of Object.entries(el?._restProps)) {
|
|
636
|
+
findElement.setAttribute(value.name,value.value)
|
|
637
|
+
}
|
|
638
|
+
findElement.removeAttribute('--')
|
|
639
|
+
findElement.removeAttribute('r-')
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Handle multiple root nodes (Fragments)
|
|
643
|
+
const newElements = Array.from(div.children)
|
|
644
|
+
for (const fn of compoAfterCall) {
|
|
645
|
+
try {
|
|
646
|
+
// Note: passing the first child might be limiting if there are multiple,
|
|
647
|
+
// but keeping API consistent for now.
|
|
648
|
+
await fn(appContext, newElements[0], el)
|
|
649
|
+
} catch (error) {
|
|
650
|
+
console.error(error.message,error.stack)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
hydrate.data=null
|
|
656
|
+
if (appContext.serializeData) { // get serialized data
|
|
657
|
+
for (const [key,value] of Object.entries(appContext.fromSerialized)) {
|
|
658
|
+
if (typeof value !== 'function') {
|
|
659
|
+
appContext.serialize[key]=value
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
hydrate.data=appContext.serialize
|
|
663
|
+
}
|
|
664
|
+
hydrate.context=[]
|
|
665
|
+
for (const [key] of Object.entries(appContext.insert)) {
|
|
666
|
+
hydrate.context.push(key)
|
|
667
|
+
}
|
|
425
668
|
|
|
669
|
+
comment.data=`component+${id}+${el._componentName}+${encodeJSON(hydrate)}`
|
|
670
|
+
stream(`<!--${comment.data}-->`)
|
|
671
|
+
|
|
672
|
+
for (const newElement of newElements) {
|
|
673
|
+
comment.parentElement.insertBefore(newElement, endComment)
|
|
674
|
+
if (!isBoundary) {
|
|
675
|
+
newElement.setAttribute('p:c', el.getAttribute('p:c'))
|
|
676
|
+
}else{
|
|
677
|
+
newElement.setAttribute('p:c',el.getAttribute('p:c'))
|
|
678
|
+
newElement.setAttribute('p-async', el.getAttribute('p:c'))
|
|
679
|
+
}
|
|
426
680
|
Array.from(el.attributes).forEach((value) => {
|
|
427
681
|
if (value.name.startsWith('c-')) {
|
|
428
682
|
newElement.setAttribute(value.name, value.value)
|
|
@@ -430,25 +684,33 @@ appContext.component._prop={children,...el._props,...slots}
|
|
|
430
684
|
})
|
|
431
685
|
|
|
432
686
|
newElement.setAttribute(`c-c-${el._componentName}-${id}`, id)
|
|
433
|
-
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
await render(newElement, el._context,stream)
|
|
690
|
+
|
|
434
691
|
}
|
|
692
|
+
stream(`<!--${endComment.data}-->`)
|
|
435
693
|
|
|
436
694
|
appContext.mount.forEach(async(call)=>{
|
|
437
695
|
await call()
|
|
438
696
|
})
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
439
700
|
store.getStore().stateContext=appContext.formerContext
|
|
440
701
|
} catch (error) {
|
|
441
|
-
console.log(error.message,error.stack
|
|
442
|
-
|
|
702
|
+
console.log(error.message,error.stack);
|
|
703
|
+
__pawaDev.setError({
|
|
443
704
|
el:el,
|
|
444
705
|
msg:`from ${el.tagName} component`,
|
|
445
706
|
directives:'component',
|
|
446
707
|
stack:error.stack,
|
|
447
708
|
template:el?._template,
|
|
448
709
|
})
|
|
710
|
+
|
|
449
711
|
}
|
|
450
712
|
}
|
|
451
|
-
const templates=async(el,
|
|
713
|
+
const templates=async(el,stream)=>{
|
|
452
714
|
if(el._running)return
|
|
453
715
|
const document = el.ownerDocument
|
|
454
716
|
const comment=document.createComment(`<template>`)
|
|
@@ -456,6 +718,7 @@ const templates=async(el,context)=>{
|
|
|
456
718
|
el.replaceWith(endComment)
|
|
457
719
|
|
|
458
720
|
endComment.parentElement.insertBefore(comment,endComment)
|
|
721
|
+
stream(`<!--${comment.data}-->`)
|
|
459
722
|
let element=[]
|
|
460
723
|
for (const child of Array.from(el.content.children)) {
|
|
461
724
|
endComment.parentElement.insertBefore(child,endComment)
|
|
@@ -471,8 +734,9 @@ const templates=async(el,context)=>{
|
|
|
471
734
|
}
|
|
472
735
|
}
|
|
473
736
|
}
|
|
474
|
-
await render(child,el._context)
|
|
737
|
+
await render(child,el._context,stream)
|
|
475
738
|
}
|
|
739
|
+
stream(`<!--${endComment.data}-->`)
|
|
476
740
|
}
|
|
477
741
|
const textContentHandler = async(el) => {
|
|
478
742
|
if (el._running || el._componentName) return;
|
|
@@ -546,7 +810,7 @@ const attributeHandler =async (el, attr) => {
|
|
|
546
810
|
const expressions = extractAtExpressions(value);
|
|
547
811
|
|
|
548
812
|
expressions.forEach(({ fullMatch, expression }) => {
|
|
549
|
-
const func =
|
|
813
|
+
const func =el._evaluateExpr(
|
|
550
814
|
expression,
|
|
551
815
|
el._context,
|
|
552
816
|
`from text interpolation @{} - ${expression} at ${currentHtmlString} attribute ${attr.name}`
|
|
@@ -564,7 +828,7 @@ const attributeHandler =async (el, attr) => {
|
|
|
564
828
|
}
|
|
565
829
|
} catch (error) {
|
|
566
830
|
console.log(error.message, error.stack);
|
|
567
|
-
|
|
831
|
+
__pawaDev.setError({
|
|
568
832
|
el:el,
|
|
569
833
|
msg:`error from Attribute Handler`+ error.message + error.stack,
|
|
570
834
|
directives:'Attribute Handler',
|
|
@@ -577,13 +841,18 @@ const attributeHandler =async (el, attr) => {
|
|
|
577
841
|
evaluate();
|
|
578
842
|
|
|
579
843
|
};
|
|
580
|
-
|
|
844
|
+
const singleElement=new Set()
|
|
845
|
+
const setSingle=(...string)=>{
|
|
846
|
+
string.forEach(v => singleElement.add(v))
|
|
847
|
+
}
|
|
848
|
+
setSingle('img','br')
|
|
581
849
|
/**
|
|
582
850
|
*
|
|
583
851
|
* @param {PawaElement | HTMLElement} el
|
|
584
852
|
* @param {object} contexts
|
|
585
853
|
*/
|
|
586
|
-
export const render =async (el, contexts = {}) => {
|
|
854
|
+
export const render =async (el, contexts = {},stream) => {
|
|
855
|
+
const isStream=store.getStore().stream
|
|
587
856
|
if(el.hasAttribute('only-client')){
|
|
588
857
|
el.removeAttribute('only-client')
|
|
589
858
|
const template=el.ownerDocument.createElement('template')
|
|
@@ -616,7 +885,6 @@ export const render =async (el, contexts = {}) => {
|
|
|
616
885
|
}
|
|
617
886
|
|
|
618
887
|
PawaElement.Element(el,context)
|
|
619
|
-
|
|
620
888
|
if(el.childNodes.some(node=>node.nodeType === 3 && node.nodeValue.includes('@{')) && !el._avoidPawaRender){
|
|
621
889
|
await textContentHandler(el)
|
|
622
890
|
}
|
|
@@ -646,11 +914,11 @@ export const render =async (el, contexts = {}) => {
|
|
|
646
914
|
const attributes = Array.from(el.attributes);
|
|
647
915
|
for(const attr of attributes){
|
|
648
916
|
if (directives[attr.name]) {
|
|
649
|
-
await directives[attr.name](el,attr)
|
|
917
|
+
await directives[attr.name](el,attr,stream)
|
|
650
918
|
}else if(attr.value.includes('@{')){
|
|
651
919
|
await attributeHandler(el,attr)
|
|
652
920
|
}else if (attr.name.startsWith('state-')) {
|
|
653
|
-
|
|
921
|
+
directives['state-'](el,attr)
|
|
654
922
|
}
|
|
655
923
|
else if(fullNamePlugin.has(attr.name)) {
|
|
656
924
|
if(externalPlugin[attr.name]){
|
|
@@ -683,12 +951,18 @@ export const render =async (el, contexts = {}) => {
|
|
|
683
951
|
|
|
684
952
|
}
|
|
685
953
|
if(el.tagName === 'TEMPLATE'){
|
|
686
|
-
await templates(el)
|
|
954
|
+
await templates(el,stream)
|
|
687
955
|
return
|
|
688
956
|
}
|
|
689
957
|
if (el._componentName) {
|
|
690
|
-
|
|
958
|
+
if(isStream){
|
|
959
|
+
await streamingComponent(el,stream)
|
|
960
|
+
return
|
|
961
|
+
}else{
|
|
962
|
+
await component(el,stream)
|
|
691
963
|
return
|
|
964
|
+
}
|
|
965
|
+
return
|
|
692
966
|
}
|
|
693
967
|
}
|
|
694
968
|
for (const fn of renderBeforeChild) {
|
|
@@ -699,10 +973,26 @@ export const render =async (el, contexts = {}) => {
|
|
|
699
973
|
}
|
|
700
974
|
}
|
|
701
975
|
if(!el._running){
|
|
702
|
-
|
|
976
|
+
const attr=Array.from(el.attributes).map(att=>`${att.name}="${att.value}"`).join(' ')
|
|
977
|
+
const isSingle=singleElement.has(el.tagName.toLowerCase())
|
|
978
|
+
if (isSingle) {
|
|
979
|
+
stream(`<${el.tagName.toLowerCase()} ${attr} />`)
|
|
980
|
+
}else{
|
|
981
|
+
stream(`<${el.tagName.toLowerCase()} ${attr} >`)
|
|
982
|
+
}
|
|
983
|
+
const children = el.childNodes;
|
|
703
984
|
for(const child of children){
|
|
704
|
-
|
|
985
|
+
if (child.nodeType === 3) {
|
|
986
|
+
stream(child.nodeValue)
|
|
987
|
+
}else if (child.nodeType === 8) {
|
|
988
|
+
stream(`<!--${child.nodeValue}-->`)
|
|
989
|
+
}else if (child.nodeType === 1){
|
|
990
|
+
await render(child, el._context,stream);
|
|
991
|
+
}
|
|
705
992
|
};
|
|
993
|
+
if (!isSingle) {
|
|
994
|
+
stream(`</${el.tagName.toLowerCase()}>`)
|
|
995
|
+
}
|
|
706
996
|
|
|
707
997
|
}
|
|
708
998
|
|
|
@@ -717,11 +1007,11 @@ const directives={
|
|
|
717
1007
|
'switch':Switch,
|
|
718
1008
|
'key':Key
|
|
719
1009
|
}
|
|
720
|
-
|
|
721
1010
|
export const startApp = async (html, context = {}, devlopment = false) => {
|
|
722
1011
|
const appContext = {
|
|
723
1012
|
context: context,
|
|
724
1013
|
stateContext: null,
|
|
1014
|
+
stream:false
|
|
725
1015
|
};
|
|
726
1016
|
isDevelopment = devlopment;
|
|
727
1017
|
const app = new DOMParser();
|
|
@@ -731,9 +1021,9 @@ export const startApp = async (html, context = {}, devlopment = false) => {
|
|
|
731
1021
|
const div = body.firstElementChild; // Original app div
|
|
732
1022
|
const root = body.createElement('div'); // New root to track transformations
|
|
733
1023
|
root.appendChild(div?.cloneNode(true)); // Clone to preserve original structure
|
|
734
|
-
|
|
1024
|
+
const fakeStream=(html)=>{}
|
|
735
1025
|
await store.run(appContext, async () => {
|
|
736
|
-
await render(root.firstElementChild, context); // Render into cloned div
|
|
1026
|
+
await render(root.firstElementChild, context,fakeStream); // Render into cloned div
|
|
737
1027
|
});
|
|
738
1028
|
return {
|
|
739
1029
|
element: root?.firstElementChild, // Return the transformed element
|
|
@@ -741,3 +1031,217 @@ export const startApp = async (html, context = {}, devlopment = false) => {
|
|
|
741
1031
|
head:body.head.innerHTML
|
|
742
1032
|
};
|
|
743
1033
|
};
|
|
1034
|
+
|
|
1035
|
+
export const startStreamApp = async (html, context = {},stream,{templateStart,templateEnd}) => {
|
|
1036
|
+
const appContext = {
|
|
1037
|
+
context: context,
|
|
1038
|
+
stateContext: null,
|
|
1039
|
+
useServer:false,
|
|
1040
|
+
batch:[],
|
|
1041
|
+
stream:true
|
|
1042
|
+
};
|
|
1043
|
+
const app = new DOMParser();
|
|
1044
|
+
|
|
1045
|
+
const body = app.parseFromString(html, 'text/html');
|
|
1046
|
+
|
|
1047
|
+
const div = body.firstElementChild; // Original app div
|
|
1048
|
+
|
|
1049
|
+
const root = body.createElement('div'); // New root to track transformations
|
|
1050
|
+
root.appendChild(div?.cloneNode(true)); // Clone to preserve original structure
|
|
1051
|
+
await store.run(appContext, async () => {
|
|
1052
|
+
stream(templateStart)
|
|
1053
|
+
stream('<div id="app">')
|
|
1054
|
+
await render(root.firstElementChild, context,stream); // Render into cloned div
|
|
1055
|
+
stream('</div>')
|
|
1056
|
+
const batchs=store.getStore().batch
|
|
1057
|
+
|
|
1058
|
+
// stream(`<script>console.log('first run') </script>`)
|
|
1059
|
+
stream(`<script>
|
|
1060
|
+
if (window?.__pawaDev && !window?.__pawaHasStarted) {
|
|
1061
|
+
window?.__startClient()
|
|
1062
|
+
window.__pawaHasStarted=true
|
|
1063
|
+
__startClient=null
|
|
1064
|
+
} </script>`)
|
|
1065
|
+
// ===== RESOLVE BATCHED ASYNC COMPONENTS =====
|
|
1066
|
+
let batch = store.getStore().batch;
|
|
1067
|
+
let maxDepth = 10; // Prevent infinite loops
|
|
1068
|
+
let depth = 0;
|
|
1069
|
+
|
|
1070
|
+
while (batch.length > 0 && depth < maxDepth) {
|
|
1071
|
+
// console.log(`Resolving batch depth ${depth}: ${batch.length} components`);
|
|
1072
|
+
|
|
1073
|
+
// Clear the batch for this iteration
|
|
1074
|
+
const currentBatch = [...batch];
|
|
1075
|
+
store.getStore().batch = [];
|
|
1076
|
+
|
|
1077
|
+
// Resolve all in parallel
|
|
1078
|
+
await Promise.allSettled(
|
|
1079
|
+
currentBatch.map(item => resolvesAsync({...item}, body, stream))
|
|
1080
|
+
);
|
|
1081
|
+
|
|
1082
|
+
// Check if new async components were discovered
|
|
1083
|
+
batch = store.getStore().batch;
|
|
1084
|
+
depth++;
|
|
1085
|
+
}
|
|
1086
|
+
stream(`<script>
|
|
1087
|
+
if (window?.__pawaDev && !window?.__pawaHasStarted && window?.__startClient !== null) {
|
|
1088
|
+
window?.__startClient();
|
|
1089
|
+
window.__pawaHasStarted = true;
|
|
1090
|
+
__startClient = null;
|
|
1091
|
+
} else {
|
|
1092
|
+
window.__shouldStart = true;
|
|
1093
|
+
}
|
|
1094
|
+
</script>`);
|
|
1095
|
+
});
|
|
1096
|
+
const errors=__pawaDev.errors
|
|
1097
|
+
const errorHtml = errors?.length ? `
|
|
1098
|
+
<div pawa-avoid style="position:fixed; top:0; left:0; width:100vw; height:100vh; background-color:#1a1a1a; z-index:99999; overflow-y:auto; font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; padding:2rem; box-sizing:border-box;">
|
|
1099
|
+
<h1 style="color:#ff6b6b; margin-top:0; border-bottom:1px solid #333; padding-bottom:1rem;">PawaJS Error Overlay</h1>
|
|
1100
|
+
${errors.map((err, i) => `
|
|
1101
|
+
<div style="background-color:#242424; border:1px solid #333; border-radius:0.5rem; padding:1.5rem; margin-bottom:1.5rem; box-shadow:0 4px 6px -1px rgba(0, 0, 0, 0.5);">
|
|
1102
|
+
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;">
|
|
1103
|
+
<h2 style="color:#e0e0e0; margin:0; font-size:1.25rem;">${err.directives || 'Runtime Error'}</h2>
|
|
1104
|
+
<span style="background-color:#333; color:#888; padding:0.25rem 0.5rem; border-radius:0.25rem; font-size:0.75rem;">Error #${i + 1}</span>
|
|
1105
|
+
</div>
|
|
1106
|
+
|
|
1107
|
+
<div style="margin-bottom:1rem;">
|
|
1108
|
+
<p style="color:#ff8787; margin:0; font-weight:bold;">${err.msg}</p>
|
|
1109
|
+
</div>
|
|
1110
|
+
|
|
1111
|
+
${err.template ? `
|
|
1112
|
+
<div style="margin-bottom:1rem;">
|
|
1113
|
+
<h3 style="color:#888; font-size:0.875rem; text-transform:uppercase; margin:0 0 0.5rem 0;">Template Context</h3>
|
|
1114
|
+
<pre style="background-color:#111; color:#a8a8a8; padding:1rem; border-radius:0.25rem; overflow-x:auto; margin:0; border:1px solid #333;"><code>${err.template.replace(/</g, '<').replace(/>/g, '>')}</code></pre>
|
|
1115
|
+
</div>
|
|
1116
|
+
` : ''}
|
|
1117
|
+
|
|
1118
|
+
<div>
|
|
1119
|
+
<h3 style="color:#888; font-size:0.875rem; text-transform:uppercase; margin:0 0 0.5rem 0;">Stack Trace</h3>
|
|
1120
|
+
<pre style="background-color:#111; color:#888; padding:1rem; border-radius:0.25rem; overflow-x:auto; margin:0; border:1px solid #333; font-size:0.875rem; line-height:1.5;"><code>${err.stack.split('\n').map(line => {
|
|
1121
|
+
const escaped = line.replace(/</g, '<').replace(/>/g, '>');
|
|
1122
|
+
return escaped.replace(/((?:[a-zA-Z]:\\|\/)[^:)]+):(\d+):(\d+)/g, (match, path, l, c) => {
|
|
1123
|
+
return `<a href="/__open-in-editor?file=${encodeURIComponent(path + ':' + l + ':' + c)}" onclick="event.preventDefault(); fetch(this.href);" style="color:#64b5f6; text-decoration:underline; cursor:pointer;">${match}</a>`
|
|
1124
|
+
})
|
|
1125
|
+
}).join('\n')}</code></pre>
|
|
1126
|
+
</div>
|
|
1127
|
+
</div>
|
|
1128
|
+
`).join('')}
|
|
1129
|
+
</div>
|
|
1130
|
+
` : ''
|
|
1131
|
+
__pawaDev.errors=[]
|
|
1132
|
+
stream(errorHtml)
|
|
1133
|
+
stream(templateEnd)
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
const resolvesAsync=async({component,
|
|
1137
|
+
id:id,
|
|
1138
|
+
comment:{
|
|
1139
|
+
encodeJSON,
|
|
1140
|
+
name
|
|
1141
|
+
},
|
|
1142
|
+
appContext,context,restProps,hydrate},root,stream,index)=>{
|
|
1143
|
+
let chunk=''
|
|
1144
|
+
const bufferStream=(string)=>{
|
|
1145
|
+
chunk+=string
|
|
1146
|
+
}
|
|
1147
|
+
bufferStream(`<div id="p${id}" hidden>`)
|
|
1148
|
+
store.getStore().stateContext=appContext
|
|
1149
|
+
const compo=await component.then((res)=>res)
|
|
1150
|
+
const div=root.createElement('div')
|
|
1151
|
+
let commentData=''
|
|
1152
|
+
if(typeof compo !== 'boolean' && compo){
|
|
1153
|
+
div.innerHTML=compo
|
|
1154
|
+
}
|
|
1155
|
+
const findElement=div.querySelector('[--]') || div.querySelector('[r-]')
|
|
1156
|
+
if (findElement) {
|
|
1157
|
+
for (const [key,value] of Object.entries(restProps)) {
|
|
1158
|
+
findElement.setAttribute(value.name,value.value)
|
|
1159
|
+
}
|
|
1160
|
+
findElement.removeAttribute('--')
|
|
1161
|
+
findElement.removeAttribute('r-')
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
const newElement=div.firstElementChild
|
|
1165
|
+
hydrate.data=null
|
|
1166
|
+
if (appContext.serializeData) { // get serialized data
|
|
1167
|
+
for (const [key,value] of Object.entries(appContext.fromSerialized)) {
|
|
1168
|
+
if (typeof value !== 'function') {
|
|
1169
|
+
appContext.serialize[key]=value
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
hydrate.data=appContext.serialize
|
|
1173
|
+
}
|
|
1174
|
+
hydrate.context=[]
|
|
1175
|
+
for (const [key] of Object.entries(appContext.insert)) {
|
|
1176
|
+
hydrate.context.push(key)
|
|
1177
|
+
}
|
|
1178
|
+
commentData=`component+${id}+${name}+${encodeJSON(hydrate)}`
|
|
1179
|
+
if (newElement) {
|
|
1180
|
+
await render(newElement,{context,...appContext.insert},bufferStream)
|
|
1181
|
+
}
|
|
1182
|
+
bufferStream(`</div>`)
|
|
1183
|
+
bufferStream(`
|
|
1184
|
+
<script class="p${id}">
|
|
1185
|
+
const s${id}=()=>{
|
|
1186
|
+
let p=document.querySelectorAll("#p${id}")
|
|
1187
|
+
if(p.length < 2){
|
|
1188
|
+
if(p[0]) p[0].remove()
|
|
1189
|
+
document.querySelector('.p${id}').remove()
|
|
1190
|
+
return
|
|
1191
|
+
}
|
|
1192
|
+
let c=p[0].previousSibling
|
|
1193
|
+
c.data='${commentData}'
|
|
1194
|
+
p[0].remove()
|
|
1195
|
+
const sc=p[0]?._stateContext
|
|
1196
|
+
let ec=c.nextSibling
|
|
1197
|
+
let fc=p[1].firstElementChild
|
|
1198
|
+
p[0].removeAttribute('pawa-avoid')
|
|
1199
|
+
Array.from(p[0].attributes).forEach(a => {
|
|
1200
|
+
if(a.name === 'p-async') return
|
|
1201
|
+
if (a.name === 'p:c') {
|
|
1202
|
+
let o=a.value + (fc.getAttribute('p:c') || '')
|
|
1203
|
+
fc.setAttribute('p:c',o)
|
|
1204
|
+
}else{
|
|
1205
|
+
fc.setAttribute(a.name,a.value)
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
p[1].childNodes.forEach(e => {
|
|
1209
|
+
c.parentElement.insertBefore(e,ec)
|
|
1210
|
+
});
|
|
1211
|
+
if (window?.__pawaDev) {
|
|
1212
|
+
window.__pawaStream(fc,p[0]._context,sc)
|
|
1213
|
+
}
|
|
1214
|
+
p[1].remove()
|
|
1215
|
+
}
|
|
1216
|
+
s${id}()
|
|
1217
|
+
document.querySelector('.p${id}').remove()
|
|
1218
|
+
</script>
|
|
1219
|
+
`)
|
|
1220
|
+
stream(chunk)
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function streamErrorFallback(id, stream) {
|
|
1224
|
+
stream(`
|
|
1225
|
+
<div hidden id="R${id}">
|
|
1226
|
+
<div class="async-error">
|
|
1227
|
+
<p>⚠️ Failed to load content</p>
|
|
1228
|
+
<button onclick="location.reload()">Retry</button>
|
|
1229
|
+
</div>
|
|
1230
|
+
</div>
|
|
1231
|
+
<script>
|
|
1232
|
+
(function() {
|
|
1233
|
+
try {
|
|
1234
|
+
var p = document.getElementById("p${id}");
|
|
1235
|
+
var r = document.getElementById("R${id}");
|
|
1236
|
+
if (p && r) {
|
|
1237
|
+
p.innerHTML = r.innerHTML;
|
|
1238
|
+
p.classList.add('error-state');
|
|
1239
|
+
r.remove();
|
|
1240
|
+
}
|
|
1241
|
+
} catch (e) {
|
|
1242
|
+
console.error('Error handling fallback:', e);
|
|
1243
|
+
}
|
|
1244
|
+
})();
|
|
1245
|
+
</script>
|
|
1246
|
+
`);
|
|
1247
|
+
}
|
package/package.json
CHANGED
package/pawaElement.js
CHANGED
|
@@ -200,7 +200,10 @@ class PawaElement {
|
|
|
200
200
|
return prop
|
|
201
201
|
}
|
|
202
202
|
`,this._context,`setting props at ${attr.name} - ${attr.value} : ${this._template}`)
|
|
203
|
-
|
|
203
|
+
let name=attr.name.slice(1)
|
|
204
|
+
if(name.includes('-')){
|
|
205
|
+
name=name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
206
|
+
}
|
|
204
207
|
this._props[name]=func
|
|
205
208
|
} catch (error) {
|
|
206
209
|
console.log(error.message,error.stack)
|
package/power.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {render} from "./index.js";
|
|
2
2
|
import { convertToNumber,evaluateExpr, processNode, reArrangeAttri, pawaGenerateId } from "./utils.js";
|
|
3
3
|
import {parseHTML} from "linkedom"
|
|
4
|
-
export const If = async(el, attr) => {
|
|
4
|
+
export const If = async(el, attr,stream) => {
|
|
5
5
|
if (el._running) return;
|
|
6
6
|
el._running = true;
|
|
7
7
|
|
|
@@ -68,6 +68,7 @@ let latestChain
|
|
|
68
68
|
const store=document.createElement('template')
|
|
69
69
|
chained.forEach((item,index) =>{
|
|
70
70
|
const clone=item.element.cloneNode(true)
|
|
71
|
+
clone._avoidPawaRender = true
|
|
71
72
|
if (index === 0) {
|
|
72
73
|
Array.from(clone.attributes).forEach(at => {
|
|
73
74
|
if (at.name.startsWith('c-')) {
|
|
@@ -103,18 +104,23 @@ let latestChain
|
|
|
103
104
|
newElement.removeAttribute(latestChain.condition)
|
|
104
105
|
newElement.setAttribute('pawa-same',true)
|
|
105
106
|
endComment.parentElement.insertBefore(comment,endComment)
|
|
107
|
+
stream(`<!--${comment.data}-->`)
|
|
106
108
|
store.setAttribute('p:store-if',id)
|
|
107
109
|
store.setAttribute('p:store','')
|
|
108
110
|
comment.parentElement.insertBefore(store,endComment)
|
|
111
|
+
stream(store.outerHTML)
|
|
109
112
|
comment.parentElement.insertBefore(newElement,endComment)
|
|
110
|
-
await render(newElement, el._context);
|
|
113
|
+
await render(newElement, el._context,stream);
|
|
114
|
+
stream(`<!--${endComment.data}-->`)
|
|
111
115
|
} else {
|
|
112
116
|
|
|
113
117
|
template.setAttribute('pawa-render',true)
|
|
114
118
|
endComment.replaceWith(template);
|
|
119
|
+
stream(`${template.outerHTML}`)
|
|
120
|
+
// await render(template, el._context,stream);
|
|
115
121
|
}
|
|
116
122
|
};
|
|
117
|
-
export const Switch = async(el, attr) => {
|
|
123
|
+
export const Switch = async(el, attr,stream) => {
|
|
118
124
|
if (el._running) return;
|
|
119
125
|
el._running = true;
|
|
120
126
|
|
|
@@ -184,6 +190,7 @@ const switchFunc=el._evaluateExpr(attr.value,el._context,`at switch directive ${
|
|
|
184
190
|
let index=0
|
|
185
191
|
chained.forEach(item =>{
|
|
186
192
|
const clone=item.element.cloneNode(true)
|
|
193
|
+
clone._avoidPawaRender = true
|
|
187
194
|
if (index === 0) {
|
|
188
195
|
Array.from(clone.attributes).forEach(at => {
|
|
189
196
|
if (at.name.startsWith('c-')) {
|
|
@@ -220,19 +227,26 @@ const switchFunc=el._evaluateExpr(attr.value,el._context,`at switch directive ${
|
|
|
220
227
|
store.setAttribute('p:store-switch',id)
|
|
221
228
|
store.setAttribute('p:store','')
|
|
222
229
|
endComment.parentElement.insertBefore(comment,endComment)
|
|
223
|
-
comment.
|
|
230
|
+
stream(`<!--${comment.data}-->`)
|
|
231
|
+
comment.parentElement.insertBefore(store,endComment)
|
|
232
|
+
stream(store.outerHTML)
|
|
224
233
|
comment.parentElement.insertBefore(newElement,endComment)
|
|
225
|
-
await render(newElement, el._context);
|
|
234
|
+
await render(newElement, el._context,stream);
|
|
235
|
+
stream(`<!--${endComment.data}-->`)
|
|
226
236
|
} else {
|
|
227
237
|
// If no case matches and no default, we just leave the comments
|
|
228
238
|
template.setAttribute('pawa-render',true)
|
|
229
239
|
endComment.parentElement.insertBefore(comment, endComment);
|
|
240
|
+
stream(`${template.outerHTML}`)
|
|
241
|
+
// stream(`<!--${comment.data}-->`)
|
|
230
242
|
comment.parentElement.insertBefore(template, endComment);
|
|
243
|
+
// await render(template, el._context,stream);
|
|
231
244
|
// No element rendered
|
|
245
|
+
// stream(`<!--${endComment.data}-->`)
|
|
232
246
|
}
|
|
233
247
|
};
|
|
234
248
|
|
|
235
|
-
export const For=async(el,attr)=>{
|
|
249
|
+
export const For=async(el,attr,stream)=>{
|
|
236
250
|
if(el._running){
|
|
237
251
|
return
|
|
238
252
|
}
|
|
@@ -246,7 +260,6 @@ export const For=async(el,attr)=>{
|
|
|
246
260
|
el.replaceWith(endComment)
|
|
247
261
|
const hasKey=el.getAttribute('for-key')
|
|
248
262
|
endComment.parentElement.insertBefore(comment,endComment)
|
|
249
|
-
|
|
250
263
|
// More robust split using regex to find the last occurrence of ' in ' or handle simple cases
|
|
251
264
|
const match = value.match(/^(.*?) in (.*)$/);
|
|
252
265
|
if (!match) throw new Error(`Invalid for loop syntax: ${value}`);
|
|
@@ -265,16 +278,20 @@ export const For=async(el,attr)=>{
|
|
|
265
278
|
})
|
|
266
279
|
|
|
267
280
|
const template=document.createElement('template')
|
|
268
|
-
|
|
281
|
+
const storeClone = copyElement.cloneNode(true)
|
|
282
|
+
storeClone._avoidPawaRender = true
|
|
283
|
+
template.appendChild(storeClone)
|
|
269
284
|
store.forEach(at=>{
|
|
270
285
|
copyElement.setAttribute(at.name,at.value)
|
|
271
286
|
})
|
|
272
287
|
const componentAttr={}
|
|
273
288
|
if(Array.isArray(array)){
|
|
274
289
|
if (array.length > 0) {
|
|
290
|
+
stream(`<!--${comment.data}-->`)
|
|
275
291
|
template.setAttribute('p:store-for',dirId)
|
|
276
292
|
template.setAttribute('p:store','')
|
|
277
293
|
endComment.parentElement.insertBefore(template,endComment)
|
|
294
|
+
stream(template.outerHTML)
|
|
278
295
|
el.attributes.forEach(attri =>{
|
|
279
296
|
if(attri.name.startsWith('c-')){
|
|
280
297
|
componentAttr[attri.name]=attri.value
|
|
@@ -309,12 +326,15 @@ export const For=async(el,attr)=>{
|
|
|
309
326
|
const endKeyComment=document.createComment(`endForKey@-$@-$@${dirId}@-$@-$@${key || index}`)
|
|
310
327
|
endComment.parentElement.insertBefore(endKeyComment,endComment)
|
|
311
328
|
endKeyComment.parentElement.insertBefore(keyComment,endKeyComment)
|
|
329
|
+
stream(`<!--${keyComment.data}-->`)
|
|
312
330
|
endKeyComment.parentElement.insertBefore(newElement,endKeyComment)
|
|
313
|
-
await render(newElement,itemContext)
|
|
331
|
+
await render(newElement,itemContext,stream)
|
|
332
|
+
stream(`<!--${endKeyComment.data}-->`)
|
|
314
333
|
}
|
|
315
|
-
|
|
334
|
+
stream(`<!--${endComment.data}-->`)
|
|
316
335
|
}else{
|
|
317
336
|
template.setAttribute('pawa-render',true)
|
|
337
|
+
stream(`<template pawa-render="true">${el.outerHTML}</template>`)
|
|
318
338
|
template.appendChild(el)
|
|
319
339
|
comment.replaceWith(template)
|
|
320
340
|
endComment.remove()
|
|
@@ -358,7 +378,7 @@ export const State=async(el,attr)=>{
|
|
|
358
378
|
|
|
359
379
|
}
|
|
360
380
|
|
|
361
|
-
export const Key=async(el,attr)=>{
|
|
381
|
+
export const Key=async(el,attr,stream)=>{
|
|
362
382
|
if(el._running){
|
|
363
383
|
return
|
|
364
384
|
}
|
|
@@ -370,6 +390,7 @@ export const Key=async(el,attr)=>{
|
|
|
370
390
|
const comment=document.createComment(`key`)
|
|
371
391
|
const endComment=document.createComment(`key`)
|
|
372
392
|
const clone=el.cloneNode(true)
|
|
393
|
+
clone._avoidPawaRender = true
|
|
373
394
|
const template=document.createElement('template')
|
|
374
395
|
template.setAttribute('p:store-key',dirId)
|
|
375
396
|
template.setAttribute('p:store','')
|
|
@@ -385,11 +406,14 @@ export const Key=async(el,attr)=>{
|
|
|
385
406
|
endComment.parentElement.insertBefore(template,endComment)
|
|
386
407
|
comment.data=`key(${func})@-$@-$@${dirId}`
|
|
387
408
|
endComment.data=`key(${func})@-$@-$@${dirId}`
|
|
409
|
+
stream(`<!--${comment.data}-->`)
|
|
410
|
+
stream(template.outerHTML)
|
|
388
411
|
el._replaceResumeAttr('key',`c-key-${dirId}`,typeof func === 'string'?`'${func}'`:func)
|
|
389
412
|
el.removeAttribute(attr.name)
|
|
390
413
|
const newElement=el.cloneNode(true)
|
|
391
414
|
endComment.parentElement.insertBefore(newElement,endComment)
|
|
392
|
-
await render(newElement,el._context)
|
|
415
|
+
await render(newElement,el._context,stream)
|
|
416
|
+
stream(`<!--${endComment.data}-->`)
|
|
393
417
|
}catch(error){
|
|
394
418
|
console.error(error.message,error.stack)
|
|
395
419
|
__pawaDev.setError({
|