ripple 0.2.103 → 0.2.104
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/package.json +1 -1
- package/src/compiler/phases/2-analyze/index.js +18 -3
- package/src/compiler/phases/3-transform/segments.js +531 -12
- package/src/compiler/phases/3-transform/server/index.js +237 -19
- package/src/runtime/index-client.js +2 -0
- package/src/runtime/internal/client/css.js +68 -0
- package/src/runtime/internal/server/css-registry.js +35 -0
- package/src/runtime/internal/server/index.js +32 -17
- package/src/server/index.js +2 -1
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.104",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -106,7 +106,7 @@ const visitors = {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (
|
|
109
|
-
is_reference(node, /** @type {Node} */
|
|
109
|
+
is_reference(node, /** @type {Node} */(parent)) &&
|
|
110
110
|
node.tracked &&
|
|
111
111
|
binding?.node !== node
|
|
112
112
|
) {
|
|
@@ -117,7 +117,7 @@ const visitors = {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (
|
|
120
|
-
is_reference(node, /** @type {Node} */
|
|
120
|
+
is_reference(node, /** @type {Node} */(parent)) &&
|
|
121
121
|
node.tracked &&
|
|
122
122
|
binding?.node !== node
|
|
123
123
|
) {
|
|
@@ -250,7 +250,10 @@ const visitors = {
|
|
|
250
250
|
}
|
|
251
251
|
const elements = [];
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
// Track metadata for this component
|
|
254
|
+
const metadata = { await: false };
|
|
255
|
+
|
|
256
|
+
context.next({ ...context.state, elements, function_depth: context.state.function_depth + 1, metadata });
|
|
254
257
|
|
|
255
258
|
const css = node.css;
|
|
256
259
|
|
|
@@ -259,6 +262,12 @@ const visitors = {
|
|
|
259
262
|
prune_css(css, node);
|
|
260
263
|
}
|
|
261
264
|
}
|
|
265
|
+
|
|
266
|
+
// Store component metadata in analysis
|
|
267
|
+
context.state.analysis.component_metadata.push({
|
|
268
|
+
id: node.id.name,
|
|
269
|
+
async: metadata.await,
|
|
270
|
+
});
|
|
262
271
|
},
|
|
263
272
|
|
|
264
273
|
ForStatement(node, context) {
|
|
@@ -357,6 +366,11 @@ const visitors = {
|
|
|
357
366
|
}
|
|
358
367
|
|
|
359
368
|
if (node.pending) {
|
|
369
|
+
// Try/pending blocks indicate async operations
|
|
370
|
+
if (context.state.metadata?.await === false) {
|
|
371
|
+
context.state.metadata.await = true;
|
|
372
|
+
}
|
|
373
|
+
|
|
360
374
|
node.metadata = {
|
|
361
375
|
has_template: false,
|
|
362
376
|
};
|
|
@@ -594,6 +608,7 @@ export function analyze(ast, filename) {
|
|
|
594
608
|
ast,
|
|
595
609
|
scope,
|
|
596
610
|
scopes,
|
|
611
|
+
component_metadata: [],
|
|
597
612
|
};
|
|
598
613
|
|
|
599
614
|
walk(
|
|
@@ -99,15 +99,19 @@ export function convert_source_map_to_mappings(ast, source, generated_code) {
|
|
|
99
99
|
/** @type {string[]} */
|
|
100
100
|
const tokens = [];
|
|
101
101
|
|
|
102
|
+
// We have to visit everything in generated order to maintain correct indices
|
|
102
103
|
walk(ast, null, {
|
|
103
|
-
_(node, {
|
|
104
|
+
_(node, { visit }) {
|
|
104
105
|
// Collect key node types: Identifiers, Literals, and JSX Elements
|
|
105
106
|
if (node.type === 'Identifier' && node.name) {
|
|
106
107
|
tokens.push(node.name);
|
|
108
|
+
return; // Leaf node, don't traverse further
|
|
107
109
|
} else if (node.type === 'JSXIdentifier' && node.name) {
|
|
108
110
|
tokens.push(node.name);
|
|
111
|
+
return; // Leaf node, don't traverse further
|
|
109
112
|
} else if (node.type === 'Literal' && node.raw) {
|
|
110
113
|
tokens.push(node.raw);
|
|
114
|
+
return; // Leaf node, don't traverse further
|
|
111
115
|
} else if (node.type === 'ImportDeclaration') {
|
|
112
116
|
// Visit specifiers in source order
|
|
113
117
|
if (node.specifiers) {
|
|
@@ -134,15 +138,15 @@ export function convert_source_map_to_mappings(ast, source, generated_code) {
|
|
|
134
138
|
}
|
|
135
139
|
return;
|
|
136
140
|
} else if (node.type === 'ExportNamedDeclaration') {
|
|
137
|
-
|
|
138
|
-
if (node.declaration) {
|
|
139
|
-
visit(node.declaration);
|
|
140
|
-
}
|
|
141
|
-
if (node.specifiers) {
|
|
141
|
+
if (node.specifiers && node.specifiers.length > 0) {
|
|
142
142
|
for (const specifier of node.specifiers) {
|
|
143
143
|
visit(specifier);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
+
if (node.declaration) {
|
|
147
|
+
// The declaration will be visited with proper ordering
|
|
148
|
+
visit(node.declaration);
|
|
149
|
+
}
|
|
146
150
|
return;
|
|
147
151
|
} else if (node.type === 'ExportDefaultDeclaration') {
|
|
148
152
|
// Visit the declaration
|
|
@@ -153,6 +157,41 @@ export function convert_source_map_to_mappings(ast, source, generated_code) {
|
|
|
153
157
|
} else if (node.type === 'ExportAllDeclaration') {
|
|
154
158
|
// Nothing to visit (just source string)
|
|
155
159
|
return;
|
|
160
|
+
} else if (node.type === 'JSXOpeningElement') {
|
|
161
|
+
// Visit name and attributes in source order
|
|
162
|
+
if (node.name) {
|
|
163
|
+
visit(node.name);
|
|
164
|
+
}
|
|
165
|
+
if (node.attributes) {
|
|
166
|
+
for (const attr of node.attributes) {
|
|
167
|
+
visit(attr);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
} else if (node.type === 'JSXAttribute') {
|
|
172
|
+
// Visit name and value in source order
|
|
173
|
+
if (node.name) {
|
|
174
|
+
visit(node.name);
|
|
175
|
+
}
|
|
176
|
+
if (node.value) {
|
|
177
|
+
visit(node.value);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
} else if (node.type === 'JSXSpreadAttribute') {
|
|
181
|
+
// Visit the spread argument
|
|
182
|
+
if (node.argument) {
|
|
183
|
+
visit(node.argument);
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
} else if (node.type === 'JSXExpressionContainer') {
|
|
187
|
+
// Visit the expression inside {}
|
|
188
|
+
if (node.expression) {
|
|
189
|
+
visit(node.expression);
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
} else if (node.type === 'JSXText') {
|
|
193
|
+
// Text content, no tokens to collect
|
|
194
|
+
return;
|
|
156
195
|
} else if (node.type === 'JSXElement') {
|
|
157
196
|
// Manually visit in source order: opening element, children, closing element
|
|
158
197
|
|
|
@@ -335,11 +374,18 @@ export function convert_source_map_to_mappings(ast, source, generated_code) {
|
|
|
335
374
|
return;
|
|
336
375
|
} else if (node.type === 'Property') {
|
|
337
376
|
// Visit in source order: key, value
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
377
|
+
// For shorthand properties ({ count }), key and value are the same node, only visit once
|
|
378
|
+
if (node.shorthand) {
|
|
379
|
+
if (node.value) {
|
|
380
|
+
visit(node.value);
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
if (node.key) {
|
|
384
|
+
visit(node.key);
|
|
385
|
+
}
|
|
386
|
+
if (node.value) {
|
|
387
|
+
visit(node.value);
|
|
388
|
+
}
|
|
343
389
|
}
|
|
344
390
|
return;
|
|
345
391
|
} else if (node.type === 'ArrayExpression' || node.type === 'ArrayPattern') {
|
|
@@ -459,9 +505,482 @@ export function convert_source_map_to_mappings(ast, source, generated_code) {
|
|
|
459
505
|
visit(node.value);
|
|
460
506
|
}
|
|
461
507
|
return;
|
|
508
|
+
} else if (node.type === 'SequenceExpression') {
|
|
509
|
+
// Visit expressions in order
|
|
510
|
+
if (node.expressions) {
|
|
511
|
+
for (const expr of node.expressions) {
|
|
512
|
+
visit(expr);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return;
|
|
516
|
+
} else if (node.type === 'SpreadElement' || node.type === 'RestElement') {
|
|
517
|
+
// Visit the argument
|
|
518
|
+
if (node.argument) {
|
|
519
|
+
visit(node.argument);
|
|
520
|
+
}
|
|
521
|
+
return;
|
|
522
|
+
} else if (node.type === 'YieldExpression' || node.type === 'AwaitExpression') {
|
|
523
|
+
// Visit the argument if present
|
|
524
|
+
if (node.argument) {
|
|
525
|
+
visit(node.argument);
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
} else if (node.type === 'ChainExpression') {
|
|
529
|
+
// Visit the expression
|
|
530
|
+
if (node.expression) {
|
|
531
|
+
visit(node.expression);
|
|
532
|
+
}
|
|
533
|
+
return;
|
|
534
|
+
} else if (node.type === 'Super' || node.type === 'ThisExpression') {
|
|
535
|
+
// Leaf nodes, no children
|
|
536
|
+
return;
|
|
537
|
+
} else if (node.type === 'MetaProperty') {
|
|
538
|
+
// Visit meta and property (e.g., new.target, import.meta)
|
|
539
|
+
if (node.meta) {
|
|
540
|
+
visit(node.meta);
|
|
541
|
+
}
|
|
542
|
+
if (node.property) {
|
|
543
|
+
visit(node.property);
|
|
544
|
+
}
|
|
545
|
+
return;
|
|
546
|
+
} else if (node.type === 'EmptyStatement' || node.type === 'DebuggerStatement') {
|
|
547
|
+
// No children to visit
|
|
548
|
+
return;
|
|
549
|
+
} else if (node.type === 'LabeledStatement') {
|
|
550
|
+
// Visit label and statement
|
|
551
|
+
if (node.label) {
|
|
552
|
+
visit(node.label);
|
|
553
|
+
}
|
|
554
|
+
if (node.body) {
|
|
555
|
+
visit(node.body);
|
|
556
|
+
}
|
|
557
|
+
return;
|
|
558
|
+
} else if (node.type === 'BreakStatement' || node.type === 'ContinueStatement') {
|
|
559
|
+
// Visit label if present
|
|
560
|
+
if (node.label) {
|
|
561
|
+
visit(node.label);
|
|
562
|
+
}
|
|
563
|
+
return;
|
|
564
|
+
} else if (node.type === 'WithStatement') {
|
|
565
|
+
// Visit object and body
|
|
566
|
+
if (node.object) {
|
|
567
|
+
visit(node.object);
|
|
568
|
+
}
|
|
569
|
+
if (node.body) {
|
|
570
|
+
visit(node.body);
|
|
571
|
+
}
|
|
572
|
+
return;
|
|
573
|
+
} else if (node.type === 'JSXFragment') {
|
|
574
|
+
// Visit children in order
|
|
575
|
+
if (node.children) {
|
|
576
|
+
for (const child of node.children) {
|
|
577
|
+
visit(child);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return;
|
|
581
|
+
} else if (node.type === 'JSXClosingElement' || node.type === 'JSXClosingFragment' || node.type === 'JSXOpeningFragment') {
|
|
582
|
+
// These are handled by their parent nodes
|
|
583
|
+
return;
|
|
584
|
+
} else if (node.type === 'JSXMemberExpression') {
|
|
585
|
+
// Visit object and property (e.g., <Foo.Bar>)
|
|
586
|
+
if (node.object) {
|
|
587
|
+
visit(node.object);
|
|
588
|
+
}
|
|
589
|
+
if (node.property) {
|
|
590
|
+
visit(node.property);
|
|
591
|
+
}
|
|
592
|
+
return;
|
|
593
|
+
} else if (node.type === 'JSXNamespacedName') {
|
|
594
|
+
// Visit namespace and name (e.g., <svg:circle>)
|
|
595
|
+
if (node.namespace) {
|
|
596
|
+
visit(node.namespace);
|
|
597
|
+
}
|
|
598
|
+
if (node.name) {
|
|
599
|
+
visit(node.name);
|
|
600
|
+
}
|
|
601
|
+
return;
|
|
602
|
+
} else if (node.type === 'JSXEmptyExpression') {
|
|
603
|
+
// No children
|
|
604
|
+
return;
|
|
605
|
+
} else if (node.type === 'TemplateElement') {
|
|
606
|
+
// Leaf node, no children to visit
|
|
607
|
+
return;
|
|
608
|
+
} else if (node.type === 'PrivateIdentifier') {
|
|
609
|
+
// Leaf node
|
|
610
|
+
return;
|
|
611
|
+
} else if (node.type === 'PropertyDefinition') {
|
|
612
|
+
// Visit key and value
|
|
613
|
+
if (node.key) {
|
|
614
|
+
visit(node.key);
|
|
615
|
+
}
|
|
616
|
+
if (node.value) {
|
|
617
|
+
visit(node.value);
|
|
618
|
+
}
|
|
619
|
+
return;
|
|
620
|
+
} else if (node.type === 'StaticBlock') {
|
|
621
|
+
// Visit body
|
|
622
|
+
if (node.body) {
|
|
623
|
+
for (const statement of node.body) {
|
|
624
|
+
visit(statement);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return;
|
|
628
|
+
} else if (node.type === 'ImportExpression') {
|
|
629
|
+
// Visit source
|
|
630
|
+
if (node.source) {
|
|
631
|
+
visit(node.source);
|
|
632
|
+
}
|
|
633
|
+
return;
|
|
634
|
+
} else if (node.type === 'ParenthesizedExpression') {
|
|
635
|
+
// Visit the wrapped expression
|
|
636
|
+
if (node.expression) {
|
|
637
|
+
visit(node.expression);
|
|
638
|
+
}
|
|
639
|
+
return;
|
|
640
|
+
} else if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression') {
|
|
641
|
+
// Type assertion: value as Type
|
|
642
|
+
if (node.expression) {
|
|
643
|
+
visit(node.expression);
|
|
644
|
+
}
|
|
645
|
+
// Skip typeAnnotation
|
|
646
|
+
return;
|
|
647
|
+
} else if (node.type === 'TSNonNullExpression') {
|
|
648
|
+
// Non-null assertion: value!
|
|
649
|
+
if (node.expression) {
|
|
650
|
+
visit(node.expression);
|
|
651
|
+
}
|
|
652
|
+
return;
|
|
653
|
+
} else if (node.type === 'TSTypeAssertion') {
|
|
654
|
+
// Type assertion: <Type>value
|
|
655
|
+
if (node.expression) {
|
|
656
|
+
visit(node.expression);
|
|
657
|
+
}
|
|
658
|
+
// Skip typeAnnotation
|
|
659
|
+
return;
|
|
660
|
+
} else if (node.type === 'TSTypeParameterInstantiation' || node.type === 'TSTypeParameterDeclaration') {
|
|
661
|
+
// Generic type parameters - visit to collect type variable names
|
|
662
|
+
if (node.params) {
|
|
663
|
+
for (const param of node.params) {
|
|
664
|
+
visit(param);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return;
|
|
668
|
+
} else if (node.type === 'TSTypeParameter') {
|
|
669
|
+
// Type parameter like T in <T>
|
|
670
|
+
if (node.name) {
|
|
671
|
+
visit(node.name);
|
|
672
|
+
}
|
|
673
|
+
if (node.constraint) {
|
|
674
|
+
visit(node.constraint);
|
|
675
|
+
}
|
|
676
|
+
if (node.default) {
|
|
677
|
+
visit(node.default);
|
|
678
|
+
}
|
|
679
|
+
return;
|
|
680
|
+
} else if (node.type === 'TSTypeAnnotation') {
|
|
681
|
+
// Type annotation - visit the type
|
|
682
|
+
if (node.typeAnnotation) {
|
|
683
|
+
visit(node.typeAnnotation);
|
|
684
|
+
}
|
|
685
|
+
return;
|
|
686
|
+
} else if (node.type === 'TSTypeReference') {
|
|
687
|
+
// Type reference like "string" or "Array<T>"
|
|
688
|
+
if (node.typeName) {
|
|
689
|
+
visit(node.typeName);
|
|
690
|
+
}
|
|
691
|
+
if (node.typeParameters) {
|
|
692
|
+
visit(node.typeParameters);
|
|
693
|
+
}
|
|
694
|
+
return;
|
|
695
|
+
} else if (node.type === 'TSQualifiedName') {
|
|
696
|
+
// Qualified name (e.g., Foo.Bar in types)
|
|
697
|
+
if (node.left) {
|
|
698
|
+
visit(node.left);
|
|
699
|
+
}
|
|
700
|
+
if (node.right) {
|
|
701
|
+
visit(node.right);
|
|
702
|
+
}
|
|
703
|
+
return;
|
|
704
|
+
} else if (node.type === 'TSArrayType') {
|
|
705
|
+
// Array type like T[]
|
|
706
|
+
if (node.elementType) {
|
|
707
|
+
visit(node.elementType);
|
|
708
|
+
}
|
|
709
|
+
return;
|
|
710
|
+
} else if (node.type === 'TSTupleType') {
|
|
711
|
+
// Tuple type like [string, number]
|
|
712
|
+
if (node.elementTypes) {
|
|
713
|
+
for (const type of node.elementTypes) {
|
|
714
|
+
visit(type);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return;
|
|
718
|
+
} else if (node.type === 'TSUnionType' || node.type === 'TSIntersectionType') {
|
|
719
|
+
// Union (A | B) or Intersection (A & B) types
|
|
720
|
+
if (node.types) {
|
|
721
|
+
for (const type of node.types) {
|
|
722
|
+
visit(type);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return;
|
|
726
|
+
} else if (node.type === 'TSFunctionType' || node.type === 'TSConstructorType') {
|
|
727
|
+
// Function or constructor type
|
|
728
|
+
if (node.typeParameters) {
|
|
729
|
+
visit(node.typeParameters);
|
|
730
|
+
}
|
|
731
|
+
if (node.parameters) {
|
|
732
|
+
for (const param of node.parameters) {
|
|
733
|
+
visit(param);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (node.typeAnnotation) {
|
|
737
|
+
visit(node.typeAnnotation);
|
|
738
|
+
}
|
|
739
|
+
return;
|
|
740
|
+
} else if (node.type === 'TSTypeLiteral') {
|
|
741
|
+
// Object type literal { foo: string }
|
|
742
|
+
if (node.members) {
|
|
743
|
+
for (const member of node.members) {
|
|
744
|
+
visit(member);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return;
|
|
748
|
+
} else if (node.type === 'TSPropertySignature') {
|
|
749
|
+
// Property signature in type
|
|
750
|
+
if (node.key) {
|
|
751
|
+
visit(node.key);
|
|
752
|
+
}
|
|
753
|
+
if (node.typeAnnotation) {
|
|
754
|
+
visit(node.typeAnnotation);
|
|
755
|
+
}
|
|
756
|
+
return;
|
|
757
|
+
} else if (node.type === 'TSMethodSignature') {
|
|
758
|
+
// Method signature in type
|
|
759
|
+
if (node.key) {
|
|
760
|
+
visit(node.key);
|
|
761
|
+
}
|
|
762
|
+
if (node.typeParameters) {
|
|
763
|
+
visit(node.typeParameters);
|
|
764
|
+
}
|
|
765
|
+
if (node.parameters) {
|
|
766
|
+
for (const param of node.parameters) {
|
|
767
|
+
visit(param);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
if (node.typeAnnotation) {
|
|
771
|
+
visit(node.typeAnnotation);
|
|
772
|
+
}
|
|
773
|
+
return;
|
|
774
|
+
} else if (node.type === 'TSIndexSignature') {
|
|
775
|
+
// Index signature [key: string]: Type
|
|
776
|
+
if (node.parameters) {
|
|
777
|
+
for (const param of node.parameters) {
|
|
778
|
+
visit(param);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (node.typeAnnotation) {
|
|
782
|
+
visit(node.typeAnnotation);
|
|
783
|
+
}
|
|
784
|
+
return;
|
|
785
|
+
} else if (node.type === 'TSCallSignatureDeclaration' || node.type === 'TSConstructSignatureDeclaration') {
|
|
786
|
+
// Call or construct signature
|
|
787
|
+
if (node.typeParameters) {
|
|
788
|
+
visit(node.typeParameters);
|
|
789
|
+
}
|
|
790
|
+
if (node.parameters) {
|
|
791
|
+
for (const param of node.parameters) {
|
|
792
|
+
visit(param);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (node.typeAnnotation) {
|
|
796
|
+
visit(node.typeAnnotation);
|
|
797
|
+
}
|
|
798
|
+
return;
|
|
799
|
+
} else if (node.type === 'TSConditionalType') {
|
|
800
|
+
// Conditional type: T extends U ? X : Y
|
|
801
|
+
if (node.checkType) {
|
|
802
|
+
visit(node.checkType);
|
|
803
|
+
}
|
|
804
|
+
if (node.extendsType) {
|
|
805
|
+
visit(node.extendsType);
|
|
806
|
+
}
|
|
807
|
+
if (node.trueType) {
|
|
808
|
+
visit(node.trueType);
|
|
809
|
+
}
|
|
810
|
+
if (node.falseType) {
|
|
811
|
+
visit(node.falseType);
|
|
812
|
+
}
|
|
813
|
+
return;
|
|
814
|
+
} else if (node.type === 'TSInferType') {
|
|
815
|
+
// Infer type: infer T
|
|
816
|
+
if (node.typeParameter) {
|
|
817
|
+
visit(node.typeParameter);
|
|
818
|
+
}
|
|
819
|
+
return;
|
|
820
|
+
} else if (node.type === 'TSParenthesizedType') {
|
|
821
|
+
// Parenthesized type: (T)
|
|
822
|
+
if (node.typeAnnotation) {
|
|
823
|
+
visit(node.typeAnnotation);
|
|
824
|
+
}
|
|
825
|
+
return;
|
|
826
|
+
} else if (node.type === 'TSTypeOperator') {
|
|
827
|
+
// Type operator: keyof T, readonly T
|
|
828
|
+
if (node.typeAnnotation) {
|
|
829
|
+
visit(node.typeAnnotation);
|
|
830
|
+
}
|
|
831
|
+
return;
|
|
832
|
+
} else if (node.type === 'TSIndexedAccessType') {
|
|
833
|
+
// Indexed access: T[K]
|
|
834
|
+
if (node.objectType) {
|
|
835
|
+
visit(node.objectType);
|
|
836
|
+
}
|
|
837
|
+
if (node.indexType) {
|
|
838
|
+
visit(node.indexType);
|
|
839
|
+
}
|
|
840
|
+
return;
|
|
841
|
+
} else if (node.type === 'TSMappedType') {
|
|
842
|
+
// Mapped type: { [K in keyof T]: ... }
|
|
843
|
+
if (node.typeParameter) {
|
|
844
|
+
visit(node.typeParameter);
|
|
845
|
+
}
|
|
846
|
+
if (node.typeAnnotation) {
|
|
847
|
+
visit(node.typeAnnotation);
|
|
848
|
+
}
|
|
849
|
+
return;
|
|
850
|
+
} else if (node.type === 'TSLiteralType') {
|
|
851
|
+
// Literal type: "foo" | 123 | true
|
|
852
|
+
if (node.literal) {
|
|
853
|
+
visit(node.literal);
|
|
854
|
+
}
|
|
855
|
+
return;
|
|
856
|
+
} else if (node.type === 'TSExpressionWithTypeArguments') {
|
|
857
|
+
// Expression with type arguments: Foo<Bar>
|
|
858
|
+
if (node.expression) {
|
|
859
|
+
visit(node.expression);
|
|
860
|
+
}
|
|
861
|
+
if (node.typeParameters) {
|
|
862
|
+
visit(node.typeParameters);
|
|
863
|
+
}
|
|
864
|
+
return;
|
|
865
|
+
} else if (node.type === 'TSImportType') {
|
|
866
|
+
// Import type: import("module").Type
|
|
867
|
+
if (node.argument) {
|
|
868
|
+
visit(node.argument);
|
|
869
|
+
}
|
|
870
|
+
if (node.qualifier) {
|
|
871
|
+
visit(node.qualifier);
|
|
872
|
+
}
|
|
873
|
+
if (node.typeParameters) {
|
|
874
|
+
visit(node.typeParameters);
|
|
875
|
+
}
|
|
876
|
+
return;
|
|
877
|
+
} else if (node.type === 'TSTypeQuery') {
|
|
878
|
+
// Type query: typeof x
|
|
879
|
+
if (node.exprName) {
|
|
880
|
+
visit(node.exprName);
|
|
881
|
+
}
|
|
882
|
+
return;
|
|
883
|
+
} else if (node.type === 'TSInterfaceDeclaration') {
|
|
884
|
+
// Interface declaration
|
|
885
|
+
if (node.id) {
|
|
886
|
+
visit(node.id);
|
|
887
|
+
}
|
|
888
|
+
if (node.typeParameters) {
|
|
889
|
+
visit(node.typeParameters);
|
|
890
|
+
}
|
|
891
|
+
if (node.extends) {
|
|
892
|
+
for (const ext of node.extends) {
|
|
893
|
+
visit(ext);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (node.body) {
|
|
897
|
+
visit(node.body);
|
|
898
|
+
}
|
|
899
|
+
return;
|
|
900
|
+
} else if (node.type === 'TSInterfaceBody') {
|
|
901
|
+
// Interface body
|
|
902
|
+
if (node.body) {
|
|
903
|
+
for (const member of node.body) {
|
|
904
|
+
visit(member);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return;
|
|
908
|
+
} else if (node.type === 'TSTypeAliasDeclaration') {
|
|
909
|
+
// Type alias
|
|
910
|
+
if (node.id) {
|
|
911
|
+
visit(node.id);
|
|
912
|
+
}
|
|
913
|
+
if (node.typeParameters) {
|
|
914
|
+
visit(node.typeParameters);
|
|
915
|
+
}
|
|
916
|
+
if (node.typeAnnotation) {
|
|
917
|
+
visit(node.typeAnnotation);
|
|
918
|
+
}
|
|
919
|
+
return;
|
|
920
|
+
} else if (node.type === 'TSEnumDeclaration') {
|
|
921
|
+
// Visit id and members
|
|
922
|
+
if (node.id) {
|
|
923
|
+
visit(node.id);
|
|
924
|
+
}
|
|
925
|
+
if (node.members) {
|
|
926
|
+
for (const member of node.members) {
|
|
927
|
+
visit(member);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return;
|
|
931
|
+
} else if (node.type === 'TSEnumMember') {
|
|
932
|
+
// Visit id and initializer
|
|
933
|
+
if (node.id) {
|
|
934
|
+
visit(node.id);
|
|
935
|
+
}
|
|
936
|
+
if (node.initializer) {
|
|
937
|
+
visit(node.initializer);
|
|
938
|
+
}
|
|
939
|
+
return;
|
|
940
|
+
} else if (node.type === 'TSModuleDeclaration') {
|
|
941
|
+
// Namespace/module declaration
|
|
942
|
+
if (node.id) {
|
|
943
|
+
visit(node.id);
|
|
944
|
+
}
|
|
945
|
+
if (node.body) {
|
|
946
|
+
visit(node.body);
|
|
947
|
+
}
|
|
948
|
+
return;
|
|
949
|
+
} else if (node.type === 'TSModuleBlock') {
|
|
950
|
+
// Module body
|
|
951
|
+
if (node.body) {
|
|
952
|
+
for (const statement of node.body) {
|
|
953
|
+
visit(statement);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return;
|
|
957
|
+
} else if (node.type === 'TSNamedTupleMember') {
|
|
958
|
+
// Named tuple member: [name: Type]
|
|
959
|
+
if (node.label) {
|
|
960
|
+
visit(node.label);
|
|
961
|
+
}
|
|
962
|
+
if (node.elementType) {
|
|
963
|
+
visit(node.elementType);
|
|
964
|
+
}
|
|
965
|
+
return;
|
|
966
|
+
} else if (node.type === 'TSRestType') {
|
|
967
|
+
// Rest type: ...T[]
|
|
968
|
+
if (node.typeAnnotation) {
|
|
969
|
+
visit(node.typeAnnotation);
|
|
970
|
+
}
|
|
971
|
+
return;
|
|
972
|
+
} else if (node.type === 'TSOptionalType') {
|
|
973
|
+
// Optional type: T?
|
|
974
|
+
if (node.typeAnnotation) {
|
|
975
|
+
visit(node.typeAnnotation);
|
|
976
|
+
}
|
|
977
|
+
return;
|
|
978
|
+
} else if (node.type === 'TSAnyKeyword' || node.type === 'TSUnknownKeyword' || node.type === 'TSNumberKeyword' || node.type === 'TSObjectKeyword' || node.type === 'TSBooleanKeyword' || node.type === 'TSBigIntKeyword' || node.type === 'TSStringKeyword' || node.type === 'TSSymbolKeyword' || node.type === 'TSVoidKeyword' || node.type === 'TSUndefinedKeyword' || node.type === 'TSNullKeyword' || node.type === 'TSNeverKeyword' || node.type === 'TSThisType' || node.type === 'TSIntrinsicKeyword') {
|
|
979
|
+
// Primitive type keywords - leaf nodes, no children
|
|
980
|
+
return;
|
|
462
981
|
}
|
|
463
982
|
|
|
464
|
-
|
|
983
|
+
throw new Error(`Unhandled AST node type in mapping walker: ${node.type}`);
|
|
465
984
|
}
|
|
466
985
|
});
|
|
467
986
|
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import is_reference from 'is-reference';
|
|
17
17
|
import { escape } from '../../../../utils/escaping.js';
|
|
18
18
|
import { is_event_attribute } from '../../../../utils/events.js';
|
|
19
|
+
import { render_stylesheets } from '../stylesheet.js';
|
|
19
20
|
|
|
20
21
|
function add_ripple_internal_import(context) {
|
|
21
22
|
if (!context.state.to_ts) {
|
|
@@ -93,17 +94,33 @@ const visitors = {
|
|
|
93
94
|
|
|
94
95
|
if (node.css !== null && node.css) {
|
|
95
96
|
context.state.stylesheets.push(node.css);
|
|
97
|
+
// Register CSS hash during rendering
|
|
98
|
+
body_statements.unshift(
|
|
99
|
+
b.stmt(
|
|
100
|
+
b.call(
|
|
101
|
+
b.member(b.id('__output'), b.id('register_css')),
|
|
102
|
+
b.literal(node.css.hash),
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
);
|
|
96
106
|
}
|
|
97
107
|
|
|
98
|
-
|
|
108
|
+
let component_fn = b.function(
|
|
99
109
|
node.id,
|
|
100
110
|
node.params.length > 0 ? [b.id('__output'), node.params[0]] : [b.id('__output')],
|
|
101
111
|
b.block([
|
|
102
112
|
...(metadata.await
|
|
103
|
-
? [b.
|
|
113
|
+
? [b.return(b.call('_$_.async', b.thunk(b.block(body_statements), true)))]
|
|
104
114
|
: body_statements),
|
|
105
115
|
]),
|
|
106
116
|
);
|
|
117
|
+
|
|
118
|
+
// Mark function as async if needed
|
|
119
|
+
if (metadata.await) {
|
|
120
|
+
component_fn = b.async(component_fn);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return component_fn;
|
|
107
124
|
},
|
|
108
125
|
|
|
109
126
|
CallExpression(node, context) {
|
|
@@ -127,6 +144,20 @@ const visitors = {
|
|
|
127
144
|
return context.next();
|
|
128
145
|
},
|
|
129
146
|
|
|
147
|
+
TSTypeAliasDeclaration(_, context) {
|
|
148
|
+
if (!context.state.to_ts) {
|
|
149
|
+
return b.empty;
|
|
150
|
+
}
|
|
151
|
+
context.next();
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
TSInterfaceDeclaration(_, context) {
|
|
155
|
+
if (!context.state.to_ts) {
|
|
156
|
+
return b.empty;
|
|
157
|
+
}
|
|
158
|
+
context.next();
|
|
159
|
+
},
|
|
160
|
+
|
|
130
161
|
ExportNamedDeclaration(node, context) {
|
|
131
162
|
if (!context.state.to_ts && node.exportKind === 'type') {
|
|
132
163
|
return b.empty;
|
|
@@ -164,13 +195,10 @@ const visitors = {
|
|
|
164
195
|
let class_attribute = null;
|
|
165
196
|
|
|
166
197
|
const handle_static_attr = (name, value) => {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
: `="${value === true ? '' : escape_html(value, true)}"`
|
|
172
|
-
}`,
|
|
173
|
-
);
|
|
198
|
+
const attr_str = ` ${name}${is_boolean_attribute(name) && value === true
|
|
199
|
+
? ''
|
|
200
|
+
: `="${value === true ? '' : escape_html(value, true)}"`
|
|
201
|
+
}`;
|
|
174
202
|
|
|
175
203
|
if (is_spreading) {
|
|
176
204
|
// For spread attributes, store just the actual value, not the full attribute string
|
|
@@ -181,7 +209,7 @@ const visitors = {
|
|
|
181
209
|
spread_attributes.push(b.prop('init', b.literal(name), actual_value));
|
|
182
210
|
} else {
|
|
183
211
|
state.init.push(
|
|
184
|
-
b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(
|
|
212
|
+
b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(attr_str))),
|
|
185
213
|
);
|
|
186
214
|
}
|
|
187
215
|
};
|
|
@@ -241,7 +269,8 @@ const visitors = {
|
|
|
241
269
|
let expression = visit(class_attribute.value, { ...state, metadata });
|
|
242
270
|
|
|
243
271
|
if (node.metadata.scoped && state.component.css) {
|
|
244
|
-
|
|
272
|
+
// Pass array to clsx so it can handle objects properly
|
|
273
|
+
expression = b.array([expression, b.literal(state.component.css.hash)]);
|
|
245
274
|
}
|
|
246
275
|
|
|
247
276
|
state.init.push(
|
|
@@ -256,7 +285,7 @@ const visitors = {
|
|
|
256
285
|
} else if (node.metadata.scoped && state.component.css) {
|
|
257
286
|
const value = state.component.css.hash;
|
|
258
287
|
|
|
259
|
-
|
|
288
|
+
handle_static_attr('class', value);
|
|
260
289
|
}
|
|
261
290
|
|
|
262
291
|
if (spread_attributes !== null && spread_attributes.length > 0) {
|
|
@@ -337,11 +366,33 @@ const visitors = {
|
|
|
337
366
|
}
|
|
338
367
|
}
|
|
339
368
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
369
|
+
// For SSR, determine if we should await based on component metadata
|
|
370
|
+
const component_call = b.call(visit(node.id, state), b.id('__output'), b.object(props));
|
|
371
|
+
|
|
372
|
+
// Check if this is a locally defined component and if it's async
|
|
373
|
+
const component_name = node.id.type === 'Identifier' ? node.id.name : null;
|
|
374
|
+
const local_metadata = component_name
|
|
375
|
+
? state.component_metadata.find((m) => m.id === component_name)
|
|
376
|
+
: null;
|
|
343
377
|
|
|
344
|
-
|
|
378
|
+
if (local_metadata) {
|
|
379
|
+
// Component is defined locally - we know if it's async or not
|
|
380
|
+
if (local_metadata.async) {
|
|
381
|
+
state.init.push(b.stmt(b.await(component_call)));
|
|
382
|
+
} else {
|
|
383
|
+
state.init.push(b.stmt(component_call));
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
// Component is imported or dynamic - check .async property at runtime
|
|
387
|
+
const conditional_await = b.conditional(
|
|
388
|
+
b.member(visit(node.id, state), b.id('async')),
|
|
389
|
+
b.await(component_call),
|
|
390
|
+
component_call
|
|
391
|
+
);
|
|
392
|
+
state.init.push(b.stmt(conditional_await));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}, ForOfStatement(node, context) {
|
|
345
396
|
if (!is_inside_component(context)) {
|
|
346
397
|
context.next();
|
|
347
398
|
return;
|
|
@@ -392,6 +443,131 @@ const visitors = {
|
|
|
392
443
|
}
|
|
393
444
|
},
|
|
394
445
|
|
|
446
|
+
ImportDeclaration(node, context) {
|
|
447
|
+
if (!context.state.to_ts && node.importKind === 'type') {
|
|
448
|
+
return b.empty;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
...node,
|
|
453
|
+
specifiers: node.specifiers
|
|
454
|
+
.filter((spec) => spec.importKind !== 'type')
|
|
455
|
+
.map((spec) => context.visit(spec)),
|
|
456
|
+
};
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
TryStatement(node, context) {
|
|
460
|
+
if (!is_inside_component(context)) {
|
|
461
|
+
return context.next();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// If there's a pending block, this is an async operation
|
|
465
|
+
const has_pending = node.pending !== null;
|
|
466
|
+
if (has_pending && context.state.metadata?.await === false) {
|
|
467
|
+
context.state.metadata.await = true;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const metadata = { await: false };
|
|
471
|
+
const body = transform_body(node.block.body, {
|
|
472
|
+
...context,
|
|
473
|
+
state: { ...context.state, metadata },
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Check if the try block itself contains async operations
|
|
477
|
+
const is_async = metadata.await || has_pending;
|
|
478
|
+
|
|
479
|
+
if (is_async) {
|
|
480
|
+
if (context.state.metadata?.await === false) {
|
|
481
|
+
context.state.metadata.await = true;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// For SSR with pending block: render the resolved content wrapped in async
|
|
485
|
+
// In a streaming SSR implementation, we'd render pending first, then stream resolved
|
|
486
|
+
const try_statements = node.handler !== null
|
|
487
|
+
? [
|
|
488
|
+
b.try(
|
|
489
|
+
b.block(body),
|
|
490
|
+
b.catch_clause(
|
|
491
|
+
node.handler.param || b.id('error'),
|
|
492
|
+
b.block(
|
|
493
|
+
transform_body(node.handler.body.body, {
|
|
494
|
+
...context,
|
|
495
|
+
state: { ...context.state, scope: context.state.scopes.get(node.handler.body) },
|
|
496
|
+
}),
|
|
497
|
+
),
|
|
498
|
+
),
|
|
499
|
+
),
|
|
500
|
+
]
|
|
501
|
+
: body;
|
|
502
|
+
|
|
503
|
+
context.state.init.push(
|
|
504
|
+
b.stmt(b.await(b.call('_$_.async', b.thunk(b.block(try_statements), true)))),
|
|
505
|
+
);
|
|
506
|
+
} else {
|
|
507
|
+
// No async, just regular try/catch
|
|
508
|
+
if (node.handler !== null) {
|
|
509
|
+
const handler_body = transform_body(node.handler.body.body, {
|
|
510
|
+
...context,
|
|
511
|
+
state: { ...context.state, scope: context.state.scopes.get(node.handler.body) },
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
context.state.init.push(
|
|
515
|
+
b.try(
|
|
516
|
+
b.block(body),
|
|
517
|
+
b.catch_clause(
|
|
518
|
+
node.handler.param || b.id('error'),
|
|
519
|
+
b.block(handler_body),
|
|
520
|
+
),
|
|
521
|
+
),
|
|
522
|
+
);
|
|
523
|
+
} else {
|
|
524
|
+
context.state.init.push(...body);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
|
|
529
|
+
AwaitExpression(node, context) {
|
|
530
|
+
if (context.state.to_ts) {
|
|
531
|
+
return context.next();
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (context.state.metadata?.await === false) {
|
|
535
|
+
context.state.metadata.await = true;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return b.await(context.visit(node.argument));
|
|
539
|
+
},
|
|
540
|
+
|
|
541
|
+
TrackedObjectExpression(node, context) {
|
|
542
|
+
// For SSR, we just evaluate the object as-is since there's no reactivity
|
|
543
|
+
return b.object(node.properties.map((prop) => context.visit(prop)));
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
TrackedArrayExpression(node, context) {
|
|
547
|
+
// For SSR, we just evaluate the array as-is since there's no reactivity
|
|
548
|
+
return b.array(node.elements.map((el) => context.visit(el)));
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
MemberExpression(node, context) {
|
|
552
|
+
const parent = context.path.at(-1);
|
|
553
|
+
|
|
554
|
+
if (node.tracked || (node.property.type === 'Identifier' && node.property.tracked)) {
|
|
555
|
+
add_ripple_internal_import(context);
|
|
556
|
+
|
|
557
|
+
return b.call(
|
|
558
|
+
'_$_.get',
|
|
559
|
+
b.member(
|
|
560
|
+
context.visit(node.object),
|
|
561
|
+
node.computed ? context.visit(node.property) : node.property,
|
|
562
|
+
node.computed,
|
|
563
|
+
node.optional,
|
|
564
|
+
),
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return context.next();
|
|
569
|
+
},
|
|
570
|
+
|
|
395
571
|
Text(node, { visit, state }) {
|
|
396
572
|
const metadata = { await: false };
|
|
397
573
|
const expression = visit(node.expression, { ...state, metadata });
|
|
@@ -408,21 +584,66 @@ const visitors = {
|
|
|
408
584
|
);
|
|
409
585
|
}
|
|
410
586
|
},
|
|
587
|
+
|
|
588
|
+
Html(node, { visit, state }) {
|
|
589
|
+
const metadata = { await: false };
|
|
590
|
+
const expression = visit(node.expression, { ...state, metadata });
|
|
591
|
+
|
|
592
|
+
// For Html nodes, we render the content as-is without escaping
|
|
593
|
+
if (expression.type === 'Literal') {
|
|
594
|
+
state.init.push(
|
|
595
|
+
b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(expression.value))),
|
|
596
|
+
);
|
|
597
|
+
} else {
|
|
598
|
+
// If it's dynamic, we need to evaluate it and push it directly (not escaped)
|
|
599
|
+
state.init.push(b.stmt(b.call(b.member(b.id('__output'), b.id('push')), expression)));
|
|
600
|
+
}
|
|
601
|
+
},
|
|
411
602
|
};
|
|
412
603
|
|
|
413
604
|
export function transform_server(filename, source, analysis) {
|
|
605
|
+
// Use component metadata collected during the analyze phase
|
|
606
|
+
const component_metadata = analysis.component_metadata || [];
|
|
607
|
+
|
|
414
608
|
const state = {
|
|
415
609
|
imports: new Set(),
|
|
416
610
|
init: null,
|
|
417
611
|
scope: analysis.scope,
|
|
418
612
|
scopes: analysis.scopes,
|
|
419
613
|
stylesheets: [],
|
|
614
|
+
component_metadata,
|
|
420
615
|
};
|
|
421
616
|
|
|
422
617
|
const program = /** @type {ESTree.Program} */ (
|
|
423
618
|
walk(analysis.ast, { ...state, namespace: 'html' }, visitors)
|
|
424
619
|
);
|
|
425
620
|
|
|
621
|
+
const css = render_stylesheets(state.stylesheets);
|
|
622
|
+
|
|
623
|
+
// Add CSS registration if there are stylesheets
|
|
624
|
+
if (state.stylesheets.length > 0 && css) {
|
|
625
|
+
// Register each stylesheet's CSS
|
|
626
|
+
for (const stylesheet of state.stylesheets) {
|
|
627
|
+
const css_for_component = render_stylesheets([stylesheet]);
|
|
628
|
+
program.body.push(
|
|
629
|
+
b.stmt(
|
|
630
|
+
b.call('_$_.register_css', b.literal(stylesheet.hash), b.literal(css_for_component)),
|
|
631
|
+
),
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Add async property to component functions
|
|
637
|
+
for (const metadata of state.component_metadata) {
|
|
638
|
+
if (metadata.async) {
|
|
639
|
+
program.body.push(
|
|
640
|
+
b.stmt(
|
|
641
|
+
b.assignment('=', b.member(b.id(metadata.id), b.id('async')), b.true),
|
|
642
|
+
),
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
426
647
|
for (const import_node of state.imports) {
|
|
427
648
|
program.body.unshift(b.stmt(b.id(import_node)));
|
|
428
649
|
}
|
|
@@ -432,9 +653,6 @@ export function transform_server(filename, source, analysis) {
|
|
|
432
653
|
sourceMapSource: path.basename(filename),
|
|
433
654
|
});
|
|
434
655
|
|
|
435
|
-
// TODO: extract css
|
|
436
|
-
const css = '';
|
|
437
|
-
|
|
438
656
|
return {
|
|
439
657
|
ast: program,
|
|
440
658
|
js,
|
|
@@ -5,6 +5,7 @@ import { handle_root_events } from './internal/client/events.js';
|
|
|
5
5
|
import { init_operations } from './internal/client/operations.js';
|
|
6
6
|
import { active_block, tracked, derived } from './internal/client/runtime.js';
|
|
7
7
|
import { create_anchor } from './internal/client/utils.js';
|
|
8
|
+
import { remove_ssr_css } from './internal/client/css.js';
|
|
8
9
|
|
|
9
10
|
// Re-export JSX runtime functions for jsxImportSource: "ripple"
|
|
10
11
|
export { jsx, jsxs, Fragment } from '../jsx-runtime.js';
|
|
@@ -16,6 +17,7 @@ export { jsx, jsxs, Fragment } from '../jsx-runtime.js';
|
|
|
16
17
|
*/
|
|
17
18
|
export function mount(component, options) {
|
|
18
19
|
init_operations();
|
|
20
|
+
remove_ssr_css();
|
|
19
21
|
|
|
20
22
|
const props = options.props || {};
|
|
21
23
|
const target = options.target;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export function remove_ssr_css() {
|
|
2
|
+
if (!document || typeof requestAnimationFrame !== "function") {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
remove_styles();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function remove_styles() {
|
|
10
|
+
if (import.meta.env.DEV) {
|
|
11
|
+
const styles = document.querySelector('style[data-vite-dev-id]');
|
|
12
|
+
if (styles) {
|
|
13
|
+
remove();
|
|
14
|
+
} else {
|
|
15
|
+
requestAnimationFrame(remove_styles);
|
|
16
|
+
}
|
|
17
|
+
} else if (import.meta.env.PROD) {
|
|
18
|
+
remove_when_css_loaded(() => requestAnimationFrame(remove));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function remove() {
|
|
23
|
+
document.querySelectorAll("style[data-ripple-ssr]").forEach((el) => el.remove());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {function} callback
|
|
28
|
+
* @returns {void}
|
|
29
|
+
*/
|
|
30
|
+
function remove_when_css_loaded(callback) {
|
|
31
|
+
/** @type {HTMLLinkElement[]} */
|
|
32
|
+
const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]'));
|
|
33
|
+
let remaining = links.length;
|
|
34
|
+
|
|
35
|
+
if (remaining === 0) {
|
|
36
|
+
callback();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const done = () => {
|
|
41
|
+
remaining--;
|
|
42
|
+
if (remaining === 0) {
|
|
43
|
+
// clean up all listeners
|
|
44
|
+
links.forEach((link) => {
|
|
45
|
+
link.removeEventListener('load', onLoad);
|
|
46
|
+
link.removeEventListener('error', onError);
|
|
47
|
+
});
|
|
48
|
+
callback();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function onLoad() {
|
|
53
|
+
done();
|
|
54
|
+
}
|
|
55
|
+
function onError() {
|
|
56
|
+
done();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
links.forEach((link) => {
|
|
60
|
+
if (link.sheet) {
|
|
61
|
+
// already loaded (possibly cached)
|
|
62
|
+
done();
|
|
63
|
+
} else {
|
|
64
|
+
link.addEventListener('load', onLoad);
|
|
65
|
+
link.addEventListener('error', onError);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global CSS registry for SSR
|
|
3
|
+
* Maps CSS hashes to their content
|
|
4
|
+
* This persists across requests for performance (CSS is immutable per hash)
|
|
5
|
+
* @type {Map<string, string>}
|
|
6
|
+
*/
|
|
7
|
+
const css_registry = new Map();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Register a component's CSS
|
|
11
|
+
* Only sets if the hash doesn't already exist (CSS is immutable per hash)
|
|
12
|
+
* @param {string} hash - The CSS hash
|
|
13
|
+
* @param {string} content - The CSS content
|
|
14
|
+
*/
|
|
15
|
+
export function register_component_css(hash, content) {
|
|
16
|
+
if (!css_registry.has(hash)) {
|
|
17
|
+
css_registry.set(hash, content);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get CSS content for a set of hashes
|
|
23
|
+
* @param {Set<string>} hashes
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
export function get_css_for_hashes(hashes) {
|
|
27
|
+
const css_parts = [];
|
|
28
|
+
for (const hash of hashes) {
|
|
29
|
+
const content = css_registry.get(hash);
|
|
30
|
+
if (content) {
|
|
31
|
+
css_parts.push(content);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return css_parts.join('\n');
|
|
35
|
+
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/** @import { Component, Derived } from '#server' */
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import { is_tracked_object } from '../client/utils';
|
|
5
|
-
|
|
2
|
+
import { DERIVED, UNINITIALIZED } from '../client/constants.js';
|
|
3
|
+
import { is_tracked_object } from '../client/utils.js';
|
|
6
4
|
import { escape } from '../../../utils/escaping.js';
|
|
7
5
|
import { is_boolean_attribute } from '../../../compiler/utils';
|
|
8
|
-
|
|
9
6
|
import { clsx } from 'clsx';
|
|
10
7
|
|
|
11
8
|
export { escape };
|
|
9
|
+
export { register_component_css as register_css } from './css-registry.js';
|
|
12
10
|
|
|
13
11
|
/** @type {Component | null} */
|
|
14
12
|
export let active_component = null;
|
|
@@ -29,6 +27,8 @@ const replacements = {
|
|
|
29
27
|
class Output {
|
|
30
28
|
head = '';
|
|
31
29
|
body = '';
|
|
30
|
+
/** @type {Set<string>} */
|
|
31
|
+
css = new Set();
|
|
32
32
|
/** @type {Output | null} */
|
|
33
33
|
#parent = null;
|
|
34
34
|
|
|
@@ -46,25 +46,32 @@ class Output {
|
|
|
46
46
|
push(str) {
|
|
47
47
|
this.body += str;
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} hash
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
register_css(hash) {
|
|
55
|
+
this.css.add(hash);
|
|
56
|
+
}
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
/**
|
|
52
60
|
* @param {((output: Output, props: Record<string, any>) => void | Promise<void>) & { async?: boolean }} component
|
|
53
|
-
* @returns {Promise<{head: string, body: string}>}
|
|
61
|
+
* @returns {Promise<{head: string, body: string, css: Set<string>}>}
|
|
54
62
|
*/
|
|
55
63
|
export async function render(component) {
|
|
56
64
|
const output = new Output(null);
|
|
57
65
|
|
|
58
|
-
// TODO add expando "async" property to component functions during SSR
|
|
59
66
|
if (component.async) {
|
|
60
67
|
await component(output, {});
|
|
61
68
|
} else {
|
|
62
69
|
component(output, {});
|
|
63
70
|
}
|
|
64
71
|
|
|
65
|
-
const { head, body } = output;
|
|
72
|
+
const { head, body, css } = output;
|
|
66
73
|
|
|
67
|
-
return { head, body };
|
|
74
|
+
return { head, body, css };
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
/**
|
|
@@ -91,7 +98,15 @@ export function pop_component() {
|
|
|
91
98
|
* @returns {Promise<void>}
|
|
92
99
|
*/
|
|
93
100
|
export async function async(fn) {
|
|
94
|
-
|
|
101
|
+
await fn();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
export function aborted() {
|
|
108
|
+
// For SSR, we don't abort rendering
|
|
109
|
+
return false;
|
|
95
110
|
}
|
|
96
111
|
|
|
97
112
|
/**
|
|
@@ -118,7 +133,7 @@ export function get(tracked) {
|
|
|
118
133
|
return tracked;
|
|
119
134
|
}
|
|
120
135
|
|
|
121
|
-
return (tracked.f & DERIVED) !== 0 ? get_derived(/** @type {Derived} */
|
|
136
|
+
return (tracked.f & DERIVED) !== 0 ? get_derived(/** @type {Derived} */(tracked)) : tracked.v;
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
/**
|
|
@@ -153,13 +168,13 @@ export function spread_attrs(attrs, css_hash) {
|
|
|
153
168
|
|
|
154
169
|
if (typeof value === 'function') continue;
|
|
155
170
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
171
|
+
if (is_tracked_object(value)) {
|
|
172
|
+
value = get(value);
|
|
173
|
+
}
|
|
159
174
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
if (name === 'class' && css_hash) {
|
|
176
|
+
value = value == null ? css_hash : [value, css_hash];
|
|
177
|
+
}
|
|
163
178
|
|
|
164
179
|
attr_str += attr(name, value, is_boolean_attribute(name));
|
|
165
180
|
}
|
package/src/server/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { render } from '../runtime/internal/server/index.js';
|
|
1
|
+
export { render } from '../runtime/internal/server/index.js';
|
|
2
|
+
export { get_css_for_hashes } from '../runtime/internal/server/css-registry.js';
|