ripple 0.2.180 → 0.2.183
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 +4 -2
- package/src/compiler/errors.js +3 -1
- package/src/compiler/index.d.ts +2 -1
- package/src/compiler/phases/1-parse/index.js +525 -311
- package/src/compiler/phases/1-parse/style.js +3 -1
- package/src/compiler/phases/2-analyze/css-analyze.js +116 -97
- package/src/compiler/phases/2-analyze/index.js +80 -50
- package/src/compiler/phases/2-analyze/prune.js +200 -58
- package/src/compiler/phases/2-analyze/validation.js +9 -7
- package/src/compiler/phases/3-transform/client/index.js +871 -394
- package/src/compiler/phases/3-transform/segments.js +6 -4
- package/src/compiler/phases/3-transform/server/index.js +278 -121
- package/src/compiler/scope.js +45 -93
- package/src/compiler/types/index.d.ts +619 -199
- package/src/compiler/types/parse.d.ts +1580 -0
- package/src/compiler/utils.js +62 -74
- package/src/runtime/internal/client/blocks.js +4 -1
- package/src/utils/ast.js +247 -192
- package/src/utils/builders.js +309 -247
- package/src/utils/sanitize_template_string.js +2 -2
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
/** @import
|
|
1
|
+
/** @import * as AST from 'estree' */
|
|
2
|
+
/** @import * as ESTreeJSX from 'estree-jsx' */
|
|
3
|
+
/** @import { SourceMapMappings } from '@jridgewell/sourcemap-codec' */
|
|
4
|
+
/** @import * as ESRap from 'esrap' */
|
|
5
|
+
/**
|
|
6
|
+
@import {
|
|
7
|
+
AnalysisResult,
|
|
8
|
+
TransformClientContext,
|
|
9
|
+
VisitorClientContext,
|
|
10
|
+
TransformClientState,
|
|
11
|
+
ScopeInterface,
|
|
12
|
+
Visitor,
|
|
13
|
+
Visitors
|
|
14
|
+
} from '#compiler';
|
|
15
|
+
*/
|
|
2
16
|
|
|
3
17
|
/** @typedef {Map<number, {offset: number, delta: number}>} PostProcessingChanges */
|
|
4
18
|
/** @typedef {number[]} LineOffsets */
|
|
@@ -47,6 +61,9 @@ import {
|
|
|
47
61
|
} from '../../../../utils/events.js';
|
|
48
62
|
import { createHash } from 'node:crypto';
|
|
49
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @param {TransformClientContext} context
|
|
66
|
+
*/
|
|
50
67
|
function add_ripple_internal_import(context) {
|
|
51
68
|
if (!context.state.to_ts) {
|
|
52
69
|
if (!context.state.imports.has(`import * as _$_ from 'ripple/internal/client'`)) {
|
|
@@ -55,6 +72,11 @@ function add_ripple_internal_import(context) {
|
|
|
55
72
|
}
|
|
56
73
|
}
|
|
57
74
|
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression} node
|
|
78
|
+
* @param {TransformClientContext} context
|
|
79
|
+
*/
|
|
58
80
|
function visit_function(node, context) {
|
|
59
81
|
// Function overload signatures don't have a body - they're TypeScript-only
|
|
60
82
|
// Remove them when compiling to JavaScript
|
|
@@ -62,11 +84,12 @@ function visit_function(node, context) {
|
|
|
62
84
|
return b.empty;
|
|
63
85
|
}
|
|
64
86
|
|
|
87
|
+
const state = context.state;
|
|
88
|
+
const metadata = /** @type {AST.FunctionExpression['metadata']} */ (node.metadata);
|
|
89
|
+
|
|
65
90
|
if (context.state.to_ts) {
|
|
66
|
-
return context.next(
|
|
91
|
+
return context.next(state);
|
|
67
92
|
}
|
|
68
|
-
const metadata = node.metadata;
|
|
69
|
-
const state = context.state;
|
|
70
93
|
|
|
71
94
|
delete node.returnType;
|
|
72
95
|
delete node.typeParameters;
|
|
@@ -96,46 +119,68 @@ function visit_function(node, context) {
|
|
|
96
119
|
new_body.push(...body.body);
|
|
97
120
|
}
|
|
98
121
|
|
|
99
|
-
return
|
|
122
|
+
return {
|
|
100
123
|
...node,
|
|
101
124
|
params: node.params.map((param) => context.visit(param, state)),
|
|
102
125
|
body: body.type === 'BlockStatement' ? { ...body, body: new_body } : body,
|
|
103
|
-
}
|
|
126
|
+
};
|
|
104
127
|
}
|
|
105
128
|
|
|
106
|
-
context.next(state);
|
|
129
|
+
return context.next(state);
|
|
107
130
|
}
|
|
108
131
|
|
|
132
|
+
/**
|
|
133
|
+
* @param {AST.Element} node
|
|
134
|
+
* @param {TransformClientContext} context
|
|
135
|
+
*/
|
|
109
136
|
function visit_head_element(node, context) {
|
|
110
137
|
const { state, visit } = context;
|
|
111
138
|
|
|
139
|
+
/** @type {TransformClientState['init']} */
|
|
112
140
|
const init = [];
|
|
141
|
+
/** @type {TransformClientState['update']} */
|
|
113
142
|
const update = [];
|
|
143
|
+
/** @type {TransformClientState['final']} */
|
|
114
144
|
const final = [];
|
|
145
|
+
/** @type {TransformClientState['template']} */
|
|
115
146
|
const template = [];
|
|
116
147
|
|
|
117
|
-
transform_children(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
148
|
+
transform_children(
|
|
149
|
+
node.children,
|
|
150
|
+
/** @type {VisitorClientContext} */ ({
|
|
151
|
+
visit,
|
|
152
|
+
state: { ...state, init, update, final, template, inside_head: true },
|
|
153
|
+
root: true,
|
|
154
|
+
}),
|
|
155
|
+
);
|
|
122
156
|
|
|
123
157
|
if (init.length > 0 || update.length > 0 || final.length > 0) {
|
|
124
|
-
context.state.init
|
|
125
|
-
b.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
158
|
+
context.state.init?.push(
|
|
159
|
+
b.stmt(
|
|
160
|
+
b.call(
|
|
161
|
+
'_$_.head',
|
|
162
|
+
b.arrow(
|
|
163
|
+
[b.id('__anchor')],
|
|
164
|
+
b.block([
|
|
165
|
+
...init,
|
|
166
|
+
.../** @type {AST.Statement[]} */ (update.map((u) => u.operation())),
|
|
167
|
+
...final,
|
|
168
|
+
]),
|
|
169
|
+
),
|
|
130
170
|
),
|
|
131
171
|
),
|
|
132
172
|
);
|
|
133
173
|
}
|
|
134
174
|
}
|
|
135
175
|
|
|
176
|
+
/**
|
|
177
|
+
* @param {NonNullable<TransformClientState['init']>} init
|
|
178
|
+
* @param {NonNullable<TransformClientState['update']>} update
|
|
179
|
+
* @param {TransformClientState} state
|
|
180
|
+
*/
|
|
136
181
|
function apply_updates(init, update, state) {
|
|
137
|
-
if (update
|
|
138
|
-
init
|
|
182
|
+
if (update?.length === 1 && !update[0].needsPrevTracking) {
|
|
183
|
+
init?.push(
|
|
139
184
|
b.stmt(
|
|
140
185
|
b.call(
|
|
141
186
|
'_$_.render',
|
|
@@ -145,7 +190,7 @@ function apply_updates(init, update, state) {
|
|
|
145
190
|
if (u.initial) {
|
|
146
191
|
return u.operation(u.expression);
|
|
147
192
|
}
|
|
148
|
-
return u.operation;
|
|
193
|
+
return u.operation();
|
|
149
194
|
}),
|
|
150
195
|
),
|
|
151
196
|
!!update.async,
|
|
@@ -154,7 +199,6 @@ function apply_updates(init, update, state) {
|
|
|
154
199
|
),
|
|
155
200
|
);
|
|
156
201
|
} else {
|
|
157
|
-
const index_map = new Map();
|
|
158
202
|
const initial = [];
|
|
159
203
|
const render_statements = [];
|
|
160
204
|
let index = 0;
|
|
@@ -164,7 +208,9 @@ function apply_updates(init, update, state) {
|
|
|
164
208
|
for (const u of update) {
|
|
165
209
|
if (u.initial) {
|
|
166
210
|
const id =
|
|
167
|
-
u.identity
|
|
211
|
+
u.identity?.type === 'Identifier'
|
|
212
|
+
? state.scope.get(u.identity.name)?.initial
|
|
213
|
+
: u.identity;
|
|
168
214
|
let updates = grouped_updates.get(id);
|
|
169
215
|
|
|
170
216
|
if (updates === undefined) {
|
|
@@ -179,7 +225,6 @@ function apply_updates(init, update, state) {
|
|
|
179
225
|
if (updates.length === 1) {
|
|
180
226
|
const u = updates[0];
|
|
181
227
|
const key = index_to_key(index);
|
|
182
|
-
index_map.set(u.operation, key);
|
|
183
228
|
initial.push(b.prop('init', b.id(key), u.initial));
|
|
184
229
|
render_statements.push(
|
|
185
230
|
b.var('__' + key, u.expression),
|
|
@@ -204,7 +249,7 @@ function apply_updates(init, update, state) {
|
|
|
204
249
|
index++;
|
|
205
250
|
} else {
|
|
206
251
|
const key = index_to_key(index);
|
|
207
|
-
/** @type {Array<Statement>} */
|
|
252
|
+
/** @type {Array<AST.Statement>} */
|
|
208
253
|
const if_body = [];
|
|
209
254
|
initial.push(b.prop('init', b.id(key), updates[0].initial));
|
|
210
255
|
render_statements.push(
|
|
@@ -215,7 +260,6 @@ function apply_updates(init, update, state) {
|
|
|
215
260
|
),
|
|
216
261
|
);
|
|
217
262
|
for (const u of updates) {
|
|
218
|
-
index_map.set(u.operation, key);
|
|
219
263
|
if_body.push(
|
|
220
264
|
u.needsPrevTracking
|
|
221
265
|
? u.operation(b.id('__' + key), b.member(b.id('__prev'), b.id(key)))
|
|
@@ -232,7 +276,7 @@ function apply_updates(init, update, state) {
|
|
|
232
276
|
|
|
233
277
|
for (const u of update) {
|
|
234
278
|
if (!u.initial && !u.needsPrevTracking) {
|
|
235
|
-
render_statements.push(u.operation);
|
|
279
|
+
render_statements.push(u.operation());
|
|
236
280
|
}
|
|
237
281
|
}
|
|
238
282
|
|
|
@@ -248,30 +292,37 @@ function apply_updates(init, update, state) {
|
|
|
248
292
|
}
|
|
249
293
|
}
|
|
250
294
|
|
|
295
|
+
/**
|
|
296
|
+
* @param {AST.Element} node
|
|
297
|
+
* @param {TransformClientContext} context
|
|
298
|
+
*/
|
|
251
299
|
function visit_title_element(node, context) {
|
|
252
300
|
const normalized = normalize_children(node.children, context);
|
|
253
301
|
const content = normalized[0];
|
|
254
302
|
|
|
255
303
|
const metadata = { tracking: false, await: false };
|
|
256
|
-
const
|
|
304
|
+
const visited = context.visit(content, { ...context.state, metadata });
|
|
305
|
+
const result = /** @type {AST.Expression} */ (
|
|
306
|
+
/** @type {{expression?: AST.Expression}} */ (visited).expression
|
|
307
|
+
);
|
|
257
308
|
|
|
258
309
|
if (metadata.tracking) {
|
|
259
|
-
context.state.init
|
|
310
|
+
context.state.init?.push(
|
|
260
311
|
b.stmt(
|
|
261
312
|
b.call(
|
|
262
313
|
'_$_.render',
|
|
263
|
-
b.thunk(b.block([b.assignment('=', b.id('_$_.document.title'), result)])),
|
|
314
|
+
b.thunk(b.block([b.stmt(b.assignment('=', b.id('_$_.document.title'), result))])),
|
|
264
315
|
),
|
|
265
316
|
),
|
|
266
317
|
);
|
|
267
318
|
} else {
|
|
268
|
-
context.state.init
|
|
319
|
+
context.state.init?.push(b.stmt(b.assignment('=', b.id('_$_.document.title'), result)));
|
|
269
320
|
}
|
|
270
321
|
}
|
|
271
322
|
|
|
272
323
|
/**
|
|
273
324
|
* @param {string} name
|
|
274
|
-
* @param {
|
|
325
|
+
* @param {TransformClientContext} context
|
|
275
326
|
* @returns {string}
|
|
276
327
|
*/
|
|
277
328
|
function import_from_ripple_if_needed(name, context) {
|
|
@@ -284,8 +335,15 @@ function import_from_ripple_if_needed(name, context) {
|
|
|
284
335
|
return alias ?? name;
|
|
285
336
|
}
|
|
286
337
|
|
|
338
|
+
/** @type {Visitors<AST.Node, TransformClientState>} */
|
|
287
339
|
const visitors = {
|
|
288
|
-
_
|
|
340
|
+
_(node, { next, state, path }) {
|
|
341
|
+
if (!node.metadata) {
|
|
342
|
+
node.metadata = { path: [...path] };
|
|
343
|
+
} else {
|
|
344
|
+
node.metadata.path = [...path];
|
|
345
|
+
}
|
|
346
|
+
|
|
289
347
|
const scope = state.scopes.get(node);
|
|
290
348
|
|
|
291
349
|
if (scope && scope !== state.scope) {
|
|
@@ -296,7 +354,7 @@ const visitors = {
|
|
|
296
354
|
},
|
|
297
355
|
|
|
298
356
|
Identifier(node, context) {
|
|
299
|
-
const parent = /** @type {Node} */ (context.path.at(-1));
|
|
357
|
+
const parent = /** @type {AST.Node} */ (context.path.at(-1));
|
|
300
358
|
|
|
301
359
|
if (is_reference(node, parent)) {
|
|
302
360
|
if (context.state.to_ts) {
|
|
@@ -353,26 +411,31 @@ const visitors = {
|
|
|
353
411
|
return b.id('_$_server_$_');
|
|
354
412
|
},
|
|
355
413
|
|
|
414
|
+
/** @type {Visitor<AST.ImportDeclaration, TransformClientState, AST.Node>} */
|
|
356
415
|
ImportDeclaration(node, context) {
|
|
357
416
|
if (!context.state.to_ts && node.importKind === 'type') {
|
|
358
417
|
return b.empty;
|
|
359
418
|
}
|
|
360
419
|
|
|
361
|
-
return {
|
|
420
|
+
return /** @type {AST.ImportDeclaration} */ ({
|
|
362
421
|
...node,
|
|
363
422
|
specifiers: node.specifiers
|
|
364
|
-
.filter(
|
|
423
|
+
.filter(
|
|
424
|
+
(spec) =>
|
|
425
|
+
context.state.to_ts || /** @type {AST.ImportSpecifier} */ (spec).importKind !== 'type',
|
|
426
|
+
)
|
|
365
427
|
.map((spec) => context.visit(spec)),
|
|
366
|
-
};
|
|
428
|
+
});
|
|
367
429
|
},
|
|
368
430
|
|
|
369
431
|
TSNonNullExpression(node, context) {
|
|
370
432
|
if (context.state.to_ts) {
|
|
371
433
|
return context.next();
|
|
372
434
|
}
|
|
373
|
-
return context.visit(node.expression);
|
|
435
|
+
return context.visit(/** @type {AST.Expression} */ (node.expression));
|
|
374
436
|
},
|
|
375
437
|
|
|
438
|
+
/** @type {Visitor<AST.CallExpression, TransformClientState, AST.Node>} */
|
|
376
439
|
CallExpression(node, context) {
|
|
377
440
|
if (!context.state.to_ts) {
|
|
378
441
|
delete node.typeArguments;
|
|
@@ -385,7 +448,7 @@ const visitors = {
|
|
|
385
448
|
}
|
|
386
449
|
|
|
387
450
|
if (!context.state.to_ts && is_ripple_track_call(callee, context)) {
|
|
388
|
-
if (callee.name === 'track') {
|
|
451
|
+
if (callee.type === 'Identifier' && callee.name === 'track') {
|
|
389
452
|
if (node.arguments.length === 0) {
|
|
390
453
|
node.arguments.push(b.void0, b.void0, b.void0);
|
|
391
454
|
} else if (node.arguments.length === 1) {
|
|
@@ -396,7 +459,10 @@ const visitors = {
|
|
|
396
459
|
}
|
|
397
460
|
return {
|
|
398
461
|
...node,
|
|
399
|
-
arguments:
|
|
462
|
+
arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
|
|
463
|
+
...node.arguments.map((arg) => context.visit(arg)),
|
|
464
|
+
b.id('__block'),
|
|
465
|
+
]),
|
|
400
466
|
};
|
|
401
467
|
}
|
|
402
468
|
|
|
@@ -422,11 +488,11 @@ const visitors = {
|
|
|
422
488
|
b.thunk(
|
|
423
489
|
b.call(
|
|
424
490
|
'_$_.call_property',
|
|
425
|
-
context.visit(callee.object),
|
|
426
|
-
context.visit(property),
|
|
491
|
+
/** @type {AST.Expression} */ (context.visit(callee.object)),
|
|
492
|
+
/** @type {AST.Expression} */ (context.visit(property)),
|
|
427
493
|
callee.optional ? b.true : undefined,
|
|
428
|
-
node.optional ? b.true : undefined,
|
|
429
|
-
|
|
494
|
+
/** @type {AST.SimpleCallExpression} */ (node).optional ? b.true : undefined,
|
|
495
|
+
.../** @type {AST.Expression[]} */ (node.arguments.map((arg) => context.visit(arg))),
|
|
430
496
|
),
|
|
431
497
|
),
|
|
432
498
|
);
|
|
@@ -439,8 +505,10 @@ const visitors = {
|
|
|
439
505
|
b.thunk(
|
|
440
506
|
{
|
|
441
507
|
...node,
|
|
442
|
-
callee: context.visit(callee),
|
|
443
|
-
arguments:
|
|
508
|
+
callee: /** @type {AST.Expression} */ (context.visit(callee)),
|
|
509
|
+
arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
|
|
510
|
+
node.arguments.map((arg) => context.visit(arg))
|
|
511
|
+
),
|
|
444
512
|
},
|
|
445
513
|
context.state.metadata?.await ?? false,
|
|
446
514
|
),
|
|
@@ -489,15 +557,19 @@ const visitors = {
|
|
|
489
557
|
calleeId.loc = callee.loc;
|
|
490
558
|
calleeId.metadata = {
|
|
491
559
|
tracked_shorthand: callee.type === 'TrackedMapExpression' ? '#Map' : '#Set',
|
|
560
|
+
path: [...context.path],
|
|
492
561
|
};
|
|
493
|
-
return b.new(
|
|
562
|
+
return b.new(
|
|
563
|
+
calleeId,
|
|
564
|
+
.../** @type {AST.Expression[]} */ (argsToUse.map((arg) => context.visit(arg))),
|
|
565
|
+
);
|
|
494
566
|
}
|
|
495
567
|
|
|
496
568
|
const helperName = callee.type === 'TrackedMapExpression' ? 'tracked_map' : 'tracked_set';
|
|
497
569
|
return b.call(
|
|
498
570
|
`_$_.${helperName}`,
|
|
499
571
|
b.id('__block'),
|
|
500
|
-
|
|
572
|
+
.../** @type {AST.Expression[]} */ (argsToUse.map((arg) => context.visit(arg))),
|
|
501
573
|
);
|
|
502
574
|
}
|
|
503
575
|
|
|
@@ -514,10 +586,13 @@ const visitors = {
|
|
|
514
586
|
return context.next();
|
|
515
587
|
}
|
|
516
588
|
|
|
589
|
+
/** @type {AST.NewExpression} */
|
|
517
590
|
const new_node = {
|
|
518
591
|
...node,
|
|
519
|
-
callee: context.visit(callee),
|
|
520
|
-
arguments:
|
|
592
|
+
callee: /** @type {AST.Expression} */ (context.visit(callee)),
|
|
593
|
+
arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
|
|
594
|
+
node.arguments.map((arg) => context.visit(arg))
|
|
595
|
+
),
|
|
521
596
|
};
|
|
522
597
|
if (!context.state.to_ts) {
|
|
523
598
|
delete new_node.typeArguments;
|
|
@@ -532,13 +607,21 @@ const visitors = {
|
|
|
532
607
|
|
|
533
608
|
return b.call(
|
|
534
609
|
b.member(b.id(arrayAlias), b.id('from')),
|
|
535
|
-
b.array(
|
|
610
|
+
b.array(
|
|
611
|
+
/** @type {(AST.Expression | AST.SpreadElement)[]} */ (
|
|
612
|
+
node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
|
|
613
|
+
),
|
|
614
|
+
),
|
|
536
615
|
);
|
|
537
616
|
}
|
|
538
617
|
|
|
539
618
|
return b.call(
|
|
540
619
|
'_$_.tracked_array',
|
|
541
|
-
b.array(
|
|
620
|
+
b.array(
|
|
621
|
+
/** @type {(AST.Expression | AST.SpreadElement)[]} */ (
|
|
622
|
+
node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
|
|
623
|
+
),
|
|
624
|
+
),
|
|
542
625
|
b.id('__block'),
|
|
543
626
|
);
|
|
544
627
|
},
|
|
@@ -547,23 +630,33 @@ const visitors = {
|
|
|
547
630
|
if (context.state.to_ts) {
|
|
548
631
|
const objectAlias = import_from_ripple_if_needed('TrackedObject', context);
|
|
549
632
|
|
|
550
|
-
return b.new(
|
|
633
|
+
return b.new(
|
|
634
|
+
b.id(objectAlias),
|
|
635
|
+
b.object(
|
|
636
|
+
/** @type {(AST.Property | AST.SpreadElement)[]} */ (
|
|
637
|
+
node.properties.map((prop) => context.visit(prop))
|
|
638
|
+
),
|
|
639
|
+
),
|
|
640
|
+
);
|
|
551
641
|
}
|
|
552
642
|
|
|
553
643
|
return b.call(
|
|
554
644
|
'_$_.tracked_object',
|
|
555
|
-
b.object(
|
|
645
|
+
b.object(
|
|
646
|
+
/** @type {(AST.Property | AST.SpreadElement)[]} */ (
|
|
647
|
+
node.properties.map((prop) => context.visit(prop))
|
|
648
|
+
),
|
|
649
|
+
),
|
|
556
650
|
b.id('__block'),
|
|
557
651
|
);
|
|
558
652
|
},
|
|
559
653
|
|
|
560
654
|
TrackedExpression(node, context) {
|
|
561
|
-
return b.call('_$_.get', context.visit(node.argument));
|
|
655
|
+
return b.call('_$_.get', /** @type {AST.Expression} */ (context.visit(node.argument)));
|
|
562
656
|
},
|
|
563
657
|
|
|
658
|
+
/** @type {Visitor<AST.MemberExpression, TransformClientState, AST.Node>} */
|
|
564
659
|
MemberExpression(node, context) {
|
|
565
|
-
const parent = context.path.at(-1);
|
|
566
|
-
|
|
567
660
|
if (context.state.metadata?.tracking === false) {
|
|
568
661
|
context.state.metadata.tracking = true;
|
|
569
662
|
}
|
|
@@ -575,8 +668,10 @@ const visitors = {
|
|
|
575
668
|
if (!context.state.to_ts) {
|
|
576
669
|
return b.call(
|
|
577
670
|
'_$_.get_property',
|
|
578
|
-
context.visit(node.object),
|
|
579
|
-
node.computed
|
|
671
|
+
/** @type {AST.Expression} */ (context.visit(node.object)),
|
|
672
|
+
node.computed
|
|
673
|
+
? /** @type {AST.Expression} */ (context.visit(node.property))
|
|
674
|
+
: b.literal(/** @type {AST.Identifier} */ (node.property).name),
|
|
580
675
|
node.optional ? b.true : undefined,
|
|
581
676
|
);
|
|
582
677
|
}
|
|
@@ -588,15 +683,15 @@ const visitors = {
|
|
|
588
683
|
const object = context.visit(node.object, { ...context.state, metadata });
|
|
589
684
|
|
|
590
685
|
if (metadata.tracking) {
|
|
591
|
-
if (context.state.metadata?.tracking === false) {
|
|
686
|
+
if (/** @type {boolean | undefined} */ (context.state.metadata?.tracking) === false) {
|
|
592
687
|
context.state.metadata.tracking = true;
|
|
593
688
|
}
|
|
594
689
|
|
|
595
690
|
return {
|
|
596
691
|
...node,
|
|
597
692
|
optional: true,
|
|
598
|
-
object,
|
|
599
|
-
property: context.visit(node.property),
|
|
693
|
+
object: /** @type {AST.Expression} */ (object),
|
|
694
|
+
property: /** @type {AST.Expression} */ (context.visit(node.property)),
|
|
600
695
|
};
|
|
601
696
|
}
|
|
602
697
|
if (metadata.await) {
|
|
@@ -626,13 +721,14 @@ const visitors = {
|
|
|
626
721
|
return context.next();
|
|
627
722
|
},
|
|
628
723
|
|
|
724
|
+
/** @type {Visitor<AST.VariableDeclarator, TransformClientState, AST.Node>} */
|
|
629
725
|
VariableDeclarator(node, context) {
|
|
630
726
|
// In TypeScript mode, capitalize identifiers that are used as dynamic components
|
|
631
727
|
if (context.state.to_ts) {
|
|
632
728
|
/**
|
|
633
729
|
* Recursively capitalize identifiers in patterns (ArrayPattern, ObjectPattern)
|
|
634
|
-
* @param {
|
|
635
|
-
* @returns {
|
|
730
|
+
* @param {AST.Pattern} pattern - The pattern node to process
|
|
731
|
+
* @returns {AST.Pattern} The transformed pattern
|
|
636
732
|
*/
|
|
637
733
|
const capitalize_pattern = (pattern) => {
|
|
638
734
|
if (pattern.type === 'Identifier') {
|
|
@@ -685,7 +781,7 @@ const visitors = {
|
|
|
685
781
|
return {
|
|
686
782
|
...pattern,
|
|
687
783
|
left: capitalize_pattern(pattern.left),
|
|
688
|
-
right: context.visit(pattern.right),
|
|
784
|
+
right: /** @type {AST.Expression} */ (context.visit(pattern.right)),
|
|
689
785
|
};
|
|
690
786
|
}
|
|
691
787
|
return pattern;
|
|
@@ -696,7 +792,7 @@ const visitors = {
|
|
|
696
792
|
return {
|
|
697
793
|
...node,
|
|
698
794
|
id: transformed_id,
|
|
699
|
-
init: node.init ? context.visit(node.init) : null,
|
|
795
|
+
init: node.init ? /** @type {AST.Expression} */ (context.visit(node.init)) : null,
|
|
700
796
|
};
|
|
701
797
|
}
|
|
702
798
|
}
|
|
@@ -704,15 +800,19 @@ const visitors = {
|
|
|
704
800
|
},
|
|
705
801
|
|
|
706
802
|
FunctionDeclaration(node, context) {
|
|
707
|
-
return
|
|
803
|
+
return /** @type AST.FunctionDeclaration | AST.EmptyStatement */ (
|
|
804
|
+
visit_function(node, context)
|
|
805
|
+
);
|
|
708
806
|
},
|
|
709
807
|
|
|
710
808
|
ArrowFunctionExpression(node, context) {
|
|
711
|
-
return
|
|
809
|
+
return /** @type AST.ArrowFunctionExpression | AST.EmptyStatement */ (
|
|
810
|
+
visit_function(node, context)
|
|
811
|
+
);
|
|
712
812
|
},
|
|
713
813
|
|
|
714
814
|
FunctionExpression(node, context) {
|
|
715
|
-
return visit_function(node, context);
|
|
815
|
+
return /** @type AST.FunctionExpression | AST.EmptyStatement */ (visit_function(node, context));
|
|
716
816
|
},
|
|
717
817
|
|
|
718
818
|
JSXText(node, context) {
|
|
@@ -759,9 +859,16 @@ const visitors = {
|
|
|
759
859
|
const props = b.object(
|
|
760
860
|
attributes.map((attr) => {
|
|
761
861
|
if (attr.type === 'JSXAttribute') {
|
|
762
|
-
return b.prop(
|
|
763
|
-
|
|
764
|
-
|
|
862
|
+
return b.prop(
|
|
863
|
+
'init',
|
|
864
|
+
/** @type {AST.Expression} */ (context.visit(attr.name)),
|
|
865
|
+
attr.value
|
|
866
|
+
? /** @type {AST.Expression} */ (context.visit(attr.value))
|
|
867
|
+
: b.literal(true),
|
|
868
|
+
);
|
|
869
|
+
} else {
|
|
870
|
+
// attr.type === 'JSXSpreadAttribute'
|
|
871
|
+
return b.spread(/** @type {AST.Expression} */ (context.visit(attr.argument)));
|
|
765
872
|
}
|
|
766
873
|
}),
|
|
767
874
|
);
|
|
@@ -772,8 +879,15 @@ const visitors = {
|
|
|
772
879
|
'init',
|
|
773
880
|
b.id('children'),
|
|
774
881
|
normalized_children.length === 1
|
|
775
|
-
?
|
|
776
|
-
|
|
882
|
+
? /** @type {AST.Expression} */ (
|
|
883
|
+
context.visit(/** @type {AST.Node} */ (normalized_children[0]))
|
|
884
|
+
)
|
|
885
|
+
: b.array(
|
|
886
|
+
normalized_children.map(
|
|
887
|
+
(child) =>
|
|
888
|
+
/** @type {AST.Expression} */ (context.visit(/** @type {AST.Node} */ (child))),
|
|
889
|
+
),
|
|
890
|
+
),
|
|
777
891
|
),
|
|
778
892
|
);
|
|
779
893
|
}
|
|
@@ -798,9 +912,16 @@ const visitors = {
|
|
|
798
912
|
const props = b.object(
|
|
799
913
|
attributes.map((attr) => {
|
|
800
914
|
if (attr.type === 'JSXAttribute') {
|
|
801
|
-
return b.prop(
|
|
802
|
-
|
|
803
|
-
|
|
915
|
+
return b.prop(
|
|
916
|
+
'init',
|
|
917
|
+
/** @type {AST.Expression} */ (context.visit(attr.name)),
|
|
918
|
+
attr.value
|
|
919
|
+
? /** @type {AST.Expression} */ (context.visit(attr.value))
|
|
920
|
+
: b.literal(true),
|
|
921
|
+
);
|
|
922
|
+
} else {
|
|
923
|
+
// attr.type === 'JSXSpreadAttribute'
|
|
924
|
+
return b.spread(/** @type {AST.Expression} */ (context.visit(attr.argument)));
|
|
804
925
|
}
|
|
805
926
|
}),
|
|
806
927
|
);
|
|
@@ -811,8 +932,15 @@ const visitors = {
|
|
|
811
932
|
'init',
|
|
812
933
|
b.id('children'),
|
|
813
934
|
normalized_children.length === 1
|
|
814
|
-
?
|
|
815
|
-
|
|
935
|
+
? /** @type {AST.Expression} */ (
|
|
936
|
+
context.visit(/** @type {AST.Node} */ (normalized_children[0]))
|
|
937
|
+
)
|
|
938
|
+
: b.array(
|
|
939
|
+
normalized_children.map(
|
|
940
|
+
(child) =>
|
|
941
|
+
/** @type {AST.Expression} */ (context.visit(/** @type {AST.Node} */ (child))),
|
|
942
|
+
),
|
|
943
|
+
),
|
|
816
944
|
),
|
|
817
945
|
);
|
|
818
946
|
}
|
|
@@ -821,7 +949,7 @@ const visitors = {
|
|
|
821
949
|
normalized_children.length > 1 ? '__compat.jsxs' : '__compat.jsx',
|
|
822
950
|
name.type === 'JSXIdentifier' && name.name[0].toLowerCase() === name.name[0]
|
|
823
951
|
? b.literal(name.name)
|
|
824
|
-
: context.visit(name),
|
|
952
|
+
: /** @type {AST.Expression} */ (context.visit(name)),
|
|
825
953
|
props,
|
|
826
954
|
);
|
|
827
955
|
},
|
|
@@ -829,13 +957,13 @@ const visitors = {
|
|
|
829
957
|
TsxCompat(node, context) {
|
|
830
958
|
const { state, visit } = context;
|
|
831
959
|
|
|
832
|
-
state.template
|
|
960
|
+
state.template?.push('<!>');
|
|
833
961
|
|
|
834
962
|
const normalized_children = node.children.filter((child) => {
|
|
835
963
|
return child.type !== 'JSXText' || child.value.trim() !== '';
|
|
836
964
|
});
|
|
837
965
|
const needs_fragment = normalized_children.length !== 1;
|
|
838
|
-
const id = state.flush_node();
|
|
966
|
+
const id = state.flush_node?.();
|
|
839
967
|
const children_fn = b.arrow(
|
|
840
968
|
[b.id('__compat')],
|
|
841
969
|
needs_fragment
|
|
@@ -846,14 +974,22 @@ const visitors = {
|
|
|
846
974
|
b.prop(
|
|
847
975
|
'init',
|
|
848
976
|
b.id('children'),
|
|
849
|
-
b.array(
|
|
977
|
+
b.array(
|
|
978
|
+
/** @type {(AST.Expression | AST.SpreadElement | null)[]} */ (
|
|
979
|
+
normalized_children.map((child) =>
|
|
980
|
+
visit(/** @type {AST.Node} */ (child), state),
|
|
981
|
+
)
|
|
982
|
+
),
|
|
983
|
+
),
|
|
850
984
|
),
|
|
851
985
|
]),
|
|
852
986
|
)
|
|
853
|
-
:
|
|
987
|
+
: /** @type {AST.Expression} */ (
|
|
988
|
+
visit(/** @type {AST.Node} */ (normalized_children[0]), state)
|
|
989
|
+
),
|
|
854
990
|
);
|
|
855
991
|
|
|
856
|
-
context.state.init
|
|
992
|
+
context.state.init?.push(
|
|
857
993
|
b.stmt(b.call('_$_.tsx_compat', b.literal(node.kind), id, children_fn)),
|
|
858
994
|
);
|
|
859
995
|
},
|
|
@@ -863,13 +999,13 @@ const visitors = {
|
|
|
863
999
|
|
|
864
1000
|
if (context.state.inside_head) {
|
|
865
1001
|
if (node.id.type === 'Identifier' && node.id.name === 'style') {
|
|
866
|
-
state.template
|
|
1002
|
+
state.template?.push(`<style>${sanitize_template_string(node.css)}</style>`);
|
|
867
1003
|
return;
|
|
868
1004
|
}
|
|
869
1005
|
if (node.id.type === 'Identifier' && node.id.name === 'script') {
|
|
870
|
-
const id = state.flush_node();
|
|
871
|
-
state.template
|
|
872
|
-
context.state.init
|
|
1006
|
+
const id = state.flush_node?.();
|
|
1007
|
+
state.template?.push('<!>');
|
|
1008
|
+
context.state.init?.push(
|
|
873
1009
|
b.stmt(b.call('_$_.script', id, b.literal(sanitize_template_string(node.content)))),
|
|
874
1010
|
);
|
|
875
1011
|
return;
|
|
@@ -878,17 +1014,22 @@ const visitors = {
|
|
|
878
1014
|
|
|
879
1015
|
const is_dom_element = is_element_dom_element(node);
|
|
880
1016
|
const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
|
|
1017
|
+
/** @type {(AST.Property | AST.SpreadElement)[] | null} */
|
|
881
1018
|
const spread_attributes = is_spreading ? [] : null;
|
|
882
1019
|
const child_namespace = is_dom_element
|
|
883
1020
|
? determine_namespace_for_children(node.id.name, state.namespace)
|
|
884
1021
|
: state.namespace;
|
|
885
1022
|
|
|
1023
|
+
/**
|
|
1024
|
+
* @param {string} name
|
|
1025
|
+
* @param {string | number | bigint | boolean | RegExp | null | undefined} value
|
|
1026
|
+
*/
|
|
886
1027
|
const handle_static_attr = (name, value) => {
|
|
887
1028
|
const attr_value = b.literal(
|
|
888
1029
|
` ${name}${
|
|
889
1030
|
is_boolean_attribute(name) && value === true
|
|
890
1031
|
? ''
|
|
891
|
-
: `="${value === true ? '' : escape_html(value, true)}"`
|
|
1032
|
+
: `="${value === true ? '' : escape_html(/** @type {string} */ (value), true)}"`
|
|
892
1033
|
}`,
|
|
893
1034
|
);
|
|
894
1035
|
|
|
@@ -898,21 +1039,23 @@ const visitors = {
|
|
|
898
1039
|
is_boolean_attribute(name) && value === true
|
|
899
1040
|
? b.literal(true)
|
|
900
1041
|
: b.literal(value === true ? '' : value);
|
|
901
|
-
spread_attributes
|
|
1042
|
+
spread_attributes?.push(b.prop('init', b.literal(name), actual_value));
|
|
902
1043
|
} else {
|
|
903
|
-
state.template
|
|
1044
|
+
state.template?.push(attr_value);
|
|
904
1045
|
}
|
|
905
1046
|
};
|
|
906
1047
|
|
|
907
1048
|
if (is_dom_element) {
|
|
908
1049
|
let class_attribute = null;
|
|
909
1050
|
let style_attribute = null;
|
|
1051
|
+
const component = /** @type {AST.Component} */ (state.component);
|
|
1052
|
+
/** @type {TransformClientState['update']} */
|
|
910
1053
|
const local_updates = [];
|
|
911
1054
|
const is_void = is_void_element(node.id.name);
|
|
912
1055
|
|
|
913
1056
|
let scoping_hash = null;
|
|
914
|
-
if (node.metadata
|
|
915
|
-
scoping_hash =
|
|
1057
|
+
if (node.metadata?.scoped && component.css) {
|
|
1058
|
+
scoping_hash = component.css.hash;
|
|
916
1059
|
} else {
|
|
917
1060
|
let inside_dynamic_children = false;
|
|
918
1061
|
for (let i = context.path.length - 1; i >= 0; i--) {
|
|
@@ -933,7 +1076,7 @@ const visitors = {
|
|
|
933
1076
|
}
|
|
934
1077
|
}
|
|
935
1078
|
|
|
936
|
-
state.template
|
|
1079
|
+
state.template?.push(`<${node.id.name}`);
|
|
937
1080
|
|
|
938
1081
|
for (const attr of node.attributes) {
|
|
939
1082
|
if (attr.type === 'Attribute') {
|
|
@@ -963,9 +1106,11 @@ const visitors = {
|
|
|
963
1106
|
}
|
|
964
1107
|
|
|
965
1108
|
if (name === 'value') {
|
|
966
|
-
const id = state.flush_node();
|
|
1109
|
+
const id = state.flush_node?.();
|
|
967
1110
|
const metadata = { tracking: false, await: false };
|
|
968
|
-
const expression =
|
|
1111
|
+
const expression = /** @type {AST.Expression} */ (
|
|
1112
|
+
visit(attr.value, { ...state, metadata })
|
|
1113
|
+
);
|
|
969
1114
|
|
|
970
1115
|
if (metadata.tracking) {
|
|
971
1116
|
local_updates.push({
|
|
@@ -975,16 +1120,18 @@ const visitors = {
|
|
|
975
1120
|
initial: b.void0,
|
|
976
1121
|
});
|
|
977
1122
|
} else {
|
|
978
|
-
state.init
|
|
1123
|
+
state.init?.push(b.stmt(b.call('_$_.set_value', id, expression)));
|
|
979
1124
|
}
|
|
980
1125
|
|
|
981
1126
|
continue;
|
|
982
1127
|
}
|
|
983
1128
|
|
|
984
1129
|
if (name === 'checked') {
|
|
985
|
-
const id = state.flush_node();
|
|
1130
|
+
const id = state.flush_node?.();
|
|
986
1131
|
const metadata = { tracking: false, await: false };
|
|
987
|
-
const expression =
|
|
1132
|
+
const expression = /** @type {AST.Expression} */ (
|
|
1133
|
+
visit(attr.value, { ...state, metadata })
|
|
1134
|
+
);
|
|
988
1135
|
|
|
989
1136
|
if (metadata.tracking) {
|
|
990
1137
|
local_updates.push({
|
|
@@ -994,15 +1141,17 @@ const visitors = {
|
|
|
994
1141
|
initial: b.void0,
|
|
995
1142
|
});
|
|
996
1143
|
} else {
|
|
997
|
-
state.init
|
|
1144
|
+
state.init?.push(b.stmt(b.call('_$_.set_checked', id, expression)));
|
|
998
1145
|
}
|
|
999
1146
|
continue;
|
|
1000
1147
|
}
|
|
1001
1148
|
|
|
1002
1149
|
if (name === 'selected') {
|
|
1003
|
-
const id = state.flush_node();
|
|
1150
|
+
const id = state.flush_node?.();
|
|
1004
1151
|
const metadata = { tracking: false, await: false };
|
|
1005
|
-
const expression =
|
|
1152
|
+
const expression = /** @type {AST.Expression} */ (
|
|
1153
|
+
visit(attr.value, { ...state, metadata })
|
|
1154
|
+
);
|
|
1006
1155
|
|
|
1007
1156
|
if (metadata.tracking) {
|
|
1008
1157
|
local_updates.push({
|
|
@@ -1012,15 +1161,17 @@ const visitors = {
|
|
|
1012
1161
|
initial: b.void0,
|
|
1013
1162
|
});
|
|
1014
1163
|
} else {
|
|
1015
|
-
state.init
|
|
1164
|
+
state.init?.push(b.stmt(b.call('_$_.set_selected', id, expression)));
|
|
1016
1165
|
}
|
|
1017
1166
|
continue;
|
|
1018
1167
|
}
|
|
1019
1168
|
|
|
1020
1169
|
if (is_event_attribute(name)) {
|
|
1021
1170
|
const metadata = { tracking: false, await: false };
|
|
1022
|
-
let handler =
|
|
1023
|
-
|
|
1171
|
+
let handler = /** @type {AST.Expression} */ (
|
|
1172
|
+
visit(attr.value, { ...state, metadata })
|
|
1173
|
+
);
|
|
1174
|
+
const id = state.flush_node?.();
|
|
1024
1175
|
|
|
1025
1176
|
if (attr.metadata?.delegated) {
|
|
1026
1177
|
const event_name = normalize_event_name(name);
|
|
@@ -1029,34 +1180,49 @@ const visitors = {
|
|
|
1029
1180
|
state.events.add(event_name);
|
|
1030
1181
|
}
|
|
1031
1182
|
|
|
1032
|
-
state.init
|
|
1033
|
-
b.stmt(
|
|
1183
|
+
state.init?.push(
|
|
1184
|
+
b.stmt(
|
|
1185
|
+
b.assignment(
|
|
1186
|
+
'=',
|
|
1187
|
+
b.member(/** @type {AST.Identifier} */ (id), '__' + event_name),
|
|
1188
|
+
handler,
|
|
1189
|
+
),
|
|
1190
|
+
),
|
|
1034
1191
|
);
|
|
1035
1192
|
} else {
|
|
1036
1193
|
const event_name = get_original_event_name(name);
|
|
1037
1194
|
// Check if handler is reactive (contains tracking)
|
|
1038
1195
|
if (metadata.tracking) {
|
|
1039
1196
|
// Use reactive_event with a thunk to re-evaluate when dependencies change
|
|
1040
|
-
state.init
|
|
1197
|
+
state.init?.push(
|
|
1041
1198
|
b.stmt(b.call('_$_.render_event', b.literal(event_name), id, b.thunk(handler))),
|
|
1042
1199
|
);
|
|
1043
1200
|
} else {
|
|
1044
|
-
state.init
|
|
1201
|
+
state.init?.push(b.stmt(b.call('_$_.event', b.literal(event_name), id, handler)));
|
|
1045
1202
|
}
|
|
1046
1203
|
}
|
|
1047
1204
|
|
|
1048
1205
|
continue;
|
|
1049
1206
|
}
|
|
1050
1207
|
const metadata = { tracking: false, await: false };
|
|
1051
|
-
const expression =
|
|
1208
|
+
const expression = /** @type {AST.Expression} */ (
|
|
1209
|
+
visit(attr.value, { ...state, metadata })
|
|
1210
|
+
);
|
|
1052
1211
|
// All other attributes
|
|
1053
1212
|
if (metadata.tracking) {
|
|
1054
1213
|
const attribute = name;
|
|
1055
|
-
const id = state.flush_node();
|
|
1214
|
+
const id = state.flush_node?.();
|
|
1056
1215
|
|
|
1057
1216
|
if (is_dom_property(attribute)) {
|
|
1058
1217
|
local_updates.push({
|
|
1059
|
-
operation:
|
|
1218
|
+
operation: () =>
|
|
1219
|
+
b.stmt(
|
|
1220
|
+
b.assignment(
|
|
1221
|
+
'=',
|
|
1222
|
+
b.member(/** @type {AST.Identifier} */ (id), attribute),
|
|
1223
|
+
expression,
|
|
1224
|
+
),
|
|
1225
|
+
),
|
|
1060
1226
|
});
|
|
1061
1227
|
} else {
|
|
1062
1228
|
local_updates.push({
|
|
@@ -1068,28 +1234,47 @@ const visitors = {
|
|
|
1068
1234
|
});
|
|
1069
1235
|
}
|
|
1070
1236
|
} else {
|
|
1071
|
-
const id = state.flush_node();
|
|
1237
|
+
const id = state.flush_node?.();
|
|
1072
1238
|
|
|
1073
1239
|
if (is_dom_property(name)) {
|
|
1074
|
-
state.init
|
|
1240
|
+
state.init?.push(
|
|
1241
|
+
b.stmt(
|
|
1242
|
+
b.assignment(
|
|
1243
|
+
'=',
|
|
1244
|
+
b.member(/** @type {AST.Identifier} */ (id), name),
|
|
1245
|
+
expression,
|
|
1246
|
+
),
|
|
1247
|
+
),
|
|
1248
|
+
);
|
|
1075
1249
|
} else {
|
|
1076
|
-
state.init
|
|
1250
|
+
state.init?.push(
|
|
1077
1251
|
b.stmt(b.call('_$_.set_attribute', id, b.literal(name), expression)),
|
|
1078
1252
|
);
|
|
1079
1253
|
}
|
|
1080
1254
|
}
|
|
1081
1255
|
}
|
|
1082
1256
|
} else if (attr.type === 'SpreadAttribute') {
|
|
1083
|
-
spread_attributes
|
|
1257
|
+
spread_attributes?.push(
|
|
1258
|
+
b.spread(/** @type {AST.Expression} */ (visit(attr.argument, state))),
|
|
1259
|
+
);
|
|
1084
1260
|
} else if (attr.type === 'RefAttribute') {
|
|
1085
|
-
const id = state.flush_node();
|
|
1086
|
-
state.init
|
|
1261
|
+
const id = state.flush_node?.();
|
|
1262
|
+
state.init?.push(
|
|
1263
|
+
b.stmt(
|
|
1264
|
+
b.call(
|
|
1265
|
+
'_$_.ref',
|
|
1266
|
+
id,
|
|
1267
|
+
b.thunk(/** @type {AST.Expression} */ (visit(attr.argument, state))),
|
|
1268
|
+
),
|
|
1269
|
+
),
|
|
1270
|
+
);
|
|
1087
1271
|
}
|
|
1088
1272
|
}
|
|
1089
1273
|
|
|
1090
1274
|
if (class_attribute !== null) {
|
|
1091
|
-
|
|
1092
|
-
|
|
1275
|
+
const attr_value = /** @type {AST.Expression} */ (class_attribute.value);
|
|
1276
|
+
if (attr_value.type === 'Literal') {
|
|
1277
|
+
let value = attr_value.value;
|
|
1093
1278
|
|
|
1094
1279
|
if (scoping_hash) {
|
|
1095
1280
|
value = `${scoping_hash} ${value}`;
|
|
@@ -1097,9 +1282,11 @@ const visitors = {
|
|
|
1097
1282
|
|
|
1098
1283
|
handle_static_attr(class_attribute.name.name, value);
|
|
1099
1284
|
} else {
|
|
1100
|
-
const id = state.flush_node();
|
|
1285
|
+
const id = state.flush_node?.();
|
|
1101
1286
|
const metadata = { tracking: false, await: false };
|
|
1102
|
-
|
|
1287
|
+
const expression = /** @type {AST.Expression} */ (
|
|
1288
|
+
visit(attr_value, { ...state, metadata })
|
|
1289
|
+
);
|
|
1103
1290
|
|
|
1104
1291
|
const hash_arg = scoping_hash ? b.literal(scoping_hash) : undefined;
|
|
1105
1292
|
const is_html = context.state.namespace === 'html' && node.id.name !== 'svg';
|
|
@@ -1109,11 +1296,11 @@ const visitors = {
|
|
|
1109
1296
|
operation: (key) =>
|
|
1110
1297
|
b.stmt(b.call('_$_.set_class', id, key, hash_arg, b.literal(is_html))),
|
|
1111
1298
|
expression,
|
|
1112
|
-
identity:
|
|
1299
|
+
identity: attr_value,
|
|
1113
1300
|
initial: b.literal(''),
|
|
1114
1301
|
});
|
|
1115
1302
|
} else {
|
|
1116
|
-
state.init
|
|
1303
|
+
state.init?.push(
|
|
1117
1304
|
b.stmt(b.call('_$_.set_class', id, expression, hash_arg, b.literal(is_html))),
|
|
1118
1305
|
);
|
|
1119
1306
|
}
|
|
@@ -1123,60 +1310,63 @@ const visitors = {
|
|
|
1123
1310
|
}
|
|
1124
1311
|
|
|
1125
1312
|
if (style_attribute !== null) {
|
|
1126
|
-
|
|
1127
|
-
|
|
1313
|
+
const attr_value = /** @type {AST.Expression} */ (style_attribute.value);
|
|
1314
|
+
if (attr_value.type === 'Literal') {
|
|
1315
|
+
handle_static_attr(style_attribute.name.name, attr_value.value);
|
|
1128
1316
|
} else {
|
|
1129
|
-
const id = state.flush_node();
|
|
1317
|
+
const id = state.flush_node?.();
|
|
1130
1318
|
const metadata = { tracking: false, await: false };
|
|
1131
|
-
const expression =
|
|
1319
|
+
const expression = /** @type {AST.Expression} */ (
|
|
1320
|
+
visit(attr_value, { ...state, metadata })
|
|
1321
|
+
);
|
|
1132
1322
|
|
|
1133
1323
|
if (metadata.tracking) {
|
|
1134
|
-
if (
|
|
1135
|
-
style_attribute.value.type === 'Literal' ||
|
|
1136
|
-
style_attribute.value.type === 'TemplateLiteral'
|
|
1137
|
-
) {
|
|
1324
|
+
if (attr_value.type === 'TemplateLiteral') {
|
|
1138
1325
|
// Doesn't need prev tracking
|
|
1139
1326
|
local_updates.push({
|
|
1140
|
-
operation: b.stmt(b.call('_$_.set_style', id, expression, b.void0)),
|
|
1327
|
+
operation: () => b.stmt(b.call('_$_.set_style', id, expression, b.void0)),
|
|
1141
1328
|
});
|
|
1142
1329
|
} else {
|
|
1143
1330
|
// Object or unknown - needs prev tracking
|
|
1144
1331
|
local_updates.push({
|
|
1145
1332
|
operation: (new_value, prev_value) =>
|
|
1146
1333
|
b.stmt(b.call('_$_.set_style', id, new_value, prev_value)),
|
|
1147
|
-
identity:
|
|
1334
|
+
identity: attr_value,
|
|
1148
1335
|
expression,
|
|
1149
1336
|
initial: b.void0,
|
|
1150
1337
|
needsPrevTracking: true,
|
|
1151
1338
|
});
|
|
1152
1339
|
}
|
|
1153
1340
|
} else {
|
|
1154
|
-
state.init
|
|
1341
|
+
state.init?.push(b.stmt(b.call('_$_.set_style', id, expression, b.void0)));
|
|
1155
1342
|
}
|
|
1156
1343
|
}
|
|
1157
1344
|
}
|
|
1158
1345
|
|
|
1159
|
-
state.template
|
|
1346
|
+
state.template?.push('>');
|
|
1160
1347
|
|
|
1161
1348
|
if (spread_attributes !== null && spread_attributes.length > 0) {
|
|
1162
|
-
const id = state.flush_node();
|
|
1163
|
-
state.init
|
|
1349
|
+
const id = state.flush_node?.();
|
|
1350
|
+
state.init?.push(
|
|
1164
1351
|
b.stmt(b.call('_$_.render_spread', id, b.thunk(b.object(spread_attributes)))),
|
|
1165
1352
|
);
|
|
1166
1353
|
}
|
|
1167
1354
|
|
|
1168
|
-
/** @type {
|
|
1355
|
+
/** @type {TransformClientState['init']} */
|
|
1169
1356
|
const init = [];
|
|
1170
|
-
/** @type {
|
|
1357
|
+
/** @type {TransformClientState['update']} */
|
|
1171
1358
|
const update = [];
|
|
1172
1359
|
|
|
1173
1360
|
if (!is_void) {
|
|
1174
|
-
transform_children(
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1361
|
+
transform_children(
|
|
1362
|
+
node.children,
|
|
1363
|
+
/** @type {VisitorClientContext} */ ({
|
|
1364
|
+
visit,
|
|
1365
|
+
state: { ...state, init, update, namespace: child_namespace },
|
|
1366
|
+
root: false,
|
|
1367
|
+
}),
|
|
1368
|
+
);
|
|
1369
|
+
state.template?.push(`</${node.id.name}>`);
|
|
1180
1370
|
}
|
|
1181
1371
|
|
|
1182
1372
|
update.push(...local_updates);
|
|
@@ -1185,20 +1375,22 @@ const visitors = {
|
|
|
1185
1375
|
if (state.scope.declarations.size > 0) {
|
|
1186
1376
|
apply_updates(init, update, state);
|
|
1187
1377
|
} else {
|
|
1188
|
-
state.update
|
|
1378
|
+
state.update?.push(...update);
|
|
1189
1379
|
}
|
|
1190
1380
|
}
|
|
1191
1381
|
|
|
1192
1382
|
if (init.length > 0) {
|
|
1193
|
-
state.init
|
|
1383
|
+
state.init?.push(b.block(init));
|
|
1194
1384
|
}
|
|
1195
1385
|
} else {
|
|
1196
|
-
const id = state.flush_node();
|
|
1386
|
+
const id = state.flush_node?.();
|
|
1197
1387
|
|
|
1198
|
-
state.template
|
|
1388
|
+
state.template?.push('<!>');
|
|
1199
1389
|
|
|
1200
1390
|
const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
|
|
1391
|
+
/** @type {(AST.Property | AST.SpreadElement)[]} */
|
|
1201
1392
|
const props = [];
|
|
1393
|
+
/** @type {AST.Expression | AST.BlockStatement | null} */
|
|
1202
1394
|
let children_prop = null;
|
|
1203
1395
|
|
|
1204
1396
|
for (const attr of node.attributes) {
|
|
@@ -1206,9 +1398,11 @@ const visitors = {
|
|
|
1206
1398
|
if (attr.name.type === 'Identifier') {
|
|
1207
1399
|
const metadata = { tracking: false, await: false };
|
|
1208
1400
|
let property =
|
|
1209
|
-
attr.value === null
|
|
1401
|
+
attr.value === null
|
|
1402
|
+
? b.literal(true)
|
|
1403
|
+
: /** @type {AST.Expression} */ (visit(attr.value, { ...state, metadata }));
|
|
1210
1404
|
|
|
1211
|
-
if (attr.name.name === 'class' && node.metadata
|
|
1405
|
+
if (attr.name.name === 'class' && node.metadata?.scoped && state.component?.css) {
|
|
1212
1406
|
if (property.type === 'Literal') {
|
|
1213
1407
|
property = b.literal(`${state.component.css.hash} ${property.value}`);
|
|
1214
1408
|
} else {
|
|
@@ -1233,24 +1427,38 @@ const visitors = {
|
|
|
1233
1427
|
props.push(b.prop('init', b.key(attr.name.name), property));
|
|
1234
1428
|
}
|
|
1235
1429
|
} else {
|
|
1236
|
-
props.push(
|
|
1430
|
+
props.push(
|
|
1431
|
+
b.prop(
|
|
1432
|
+
'init',
|
|
1433
|
+
b.key(attr.name.name),
|
|
1434
|
+
/** @type {AST.Expression} */ (visit(/** @type {AST.Node} */ (attr.value), state)),
|
|
1435
|
+
),
|
|
1436
|
+
);
|
|
1237
1437
|
}
|
|
1238
1438
|
} else if (attr.type === 'SpreadAttribute') {
|
|
1239
1439
|
props.push(
|
|
1240
1440
|
b.spread(
|
|
1241
|
-
|
|
1441
|
+
/** @type {AST.Expression} */
|
|
1442
|
+
(visit(attr.argument, { ...state, metadata: { ...state.metadata } })),
|
|
1242
1443
|
),
|
|
1243
1444
|
);
|
|
1244
1445
|
} else if (attr.type === 'RefAttribute') {
|
|
1245
1446
|
const ref_id = state.scope.generate('ref');
|
|
1246
|
-
state.setup
|
|
1247
|
-
props.push(
|
|
1447
|
+
state.setup?.push(b.var(ref_id, b.call('_$_.ref_prop')));
|
|
1448
|
+
props.push(
|
|
1449
|
+
b.prop(
|
|
1450
|
+
'init',
|
|
1451
|
+
b.id(ref_id),
|
|
1452
|
+
/** @type {AST.Expression} */ (visit(attr.argument, state)),
|
|
1453
|
+
true,
|
|
1454
|
+
),
|
|
1455
|
+
);
|
|
1248
1456
|
} else {
|
|
1249
1457
|
throw new Error('TODO');
|
|
1250
1458
|
}
|
|
1251
1459
|
}
|
|
1252
1460
|
|
|
1253
|
-
if (node.metadata
|
|
1461
|
+
if (node.metadata?.scoped && state.component?.css) {
|
|
1254
1462
|
const hasClassAttr = node.attributes.some(
|
|
1255
1463
|
(attr) =>
|
|
1256
1464
|
attr.type === 'Attribute' &&
|
|
@@ -1268,8 +1476,18 @@ const visitors = {
|
|
|
1268
1476
|
|
|
1269
1477
|
for (const child of node.children) {
|
|
1270
1478
|
if (child.type === 'Component') {
|
|
1271
|
-
|
|
1272
|
-
|
|
1479
|
+
// in this case, id cannot be null
|
|
1480
|
+
// as these are direct children of the component
|
|
1481
|
+
const id = /** @type {AST.Identifier} */ (child.id);
|
|
1482
|
+
props.push(
|
|
1483
|
+
b.prop(
|
|
1484
|
+
'init',
|
|
1485
|
+
id,
|
|
1486
|
+
/** @type {AST.Expression} */ (
|
|
1487
|
+
visit(child, { ...state, namespace: child_namespace })
|
|
1488
|
+
),
|
|
1489
|
+
),
|
|
1490
|
+
);
|
|
1273
1491
|
} else {
|
|
1274
1492
|
children_filtered.push(child);
|
|
1275
1493
|
}
|
|
@@ -1284,14 +1502,22 @@ const visitors = {
|
|
|
1284
1502
|
inherited_css: true,
|
|
1285
1503
|
};
|
|
1286
1504
|
|
|
1287
|
-
const children =
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1505
|
+
const children = /** @type {AST.Expression} */ (
|
|
1506
|
+
visit(children_component, {
|
|
1507
|
+
...context.state,
|
|
1508
|
+
scope: /** @type {ScopeInterface} */ (component_scope),
|
|
1509
|
+
namespace: child_namespace,
|
|
1510
|
+
})
|
|
1511
|
+
);
|
|
1292
1512
|
|
|
1293
1513
|
if (children_prop) {
|
|
1294
|
-
children_prop.body = b.logical(
|
|
1514
|
+
/** @type {AST.ArrowFunctionExpression} */ (children_prop).body = b.logical(
|
|
1515
|
+
'??',
|
|
1516
|
+
/** @type {AST.Expression} */ (
|
|
1517
|
+
/** @type {AST.ArrowFunctionExpression} */ (children_prop).body
|
|
1518
|
+
),
|
|
1519
|
+
children,
|
|
1520
|
+
);
|
|
1295
1521
|
} else {
|
|
1296
1522
|
props.push(b.prop('init', b.id('children'), children));
|
|
1297
1523
|
}
|
|
@@ -1299,7 +1525,7 @@ const visitors = {
|
|
|
1299
1525
|
|
|
1300
1526
|
const metadata = { tracking: false, await: false };
|
|
1301
1527
|
// We visit, but only to gather metadata
|
|
1302
|
-
b.call(visit(node.id, { ...state, metadata }));
|
|
1528
|
+
b.call(/** @type {AST.Expression} */ (visit(node.id, { ...state, metadata })));
|
|
1303
1529
|
|
|
1304
1530
|
// We're calling a component from within svg/mathml context
|
|
1305
1531
|
const is_with_ns = state.namespace !== DEFAULT_NAMESPACE;
|
|
@@ -1340,17 +1566,27 @@ const visitors = {
|
|
|
1340
1566
|
object_props = b.object(props);
|
|
1341
1567
|
}
|
|
1342
1568
|
if (metadata.tracking) {
|
|
1343
|
-
const shared = b.call(
|
|
1344
|
-
|
|
1569
|
+
const shared = b.call(
|
|
1570
|
+
'_$_.composite',
|
|
1571
|
+
b.thunk(/** @type {AST.Expression} */ (visit(node.id, state))),
|
|
1572
|
+
id,
|
|
1573
|
+
object_props,
|
|
1574
|
+
);
|
|
1575
|
+
state.init?.push(
|
|
1345
1576
|
is_with_ns
|
|
1346
|
-
? b.call('_$_.with_ns', b.literal(state.namespace), b.thunk(shared))
|
|
1577
|
+
? b.stmt(b.call('_$_.with_ns', b.literal(state.namespace), b.thunk(shared)))
|
|
1347
1578
|
: b.stmt(shared),
|
|
1348
1579
|
);
|
|
1349
1580
|
} else {
|
|
1350
|
-
const shared = b.call(
|
|
1351
|
-
|
|
1581
|
+
const shared = b.call(
|
|
1582
|
+
/** @type {AST.Expression} */ (visit(node.id, state)),
|
|
1583
|
+
id,
|
|
1584
|
+
object_props,
|
|
1585
|
+
b.id('_$_.active_block'),
|
|
1586
|
+
);
|
|
1587
|
+
state.init?.push(
|
|
1352
1588
|
is_with_ns
|
|
1353
|
-
? b.call('_$_.with_ns', b.literal(state.namespace), b.thunk(shared))
|
|
1589
|
+
? b.stmt(b.call('_$_.with_ns', b.literal(state.namespace), b.thunk(shared)))
|
|
1354
1590
|
: b.stmt(shared),
|
|
1355
1591
|
);
|
|
1356
1592
|
}
|
|
@@ -1374,11 +1610,17 @@ const visitors = {
|
|
|
1374
1610
|
|
|
1375
1611
|
const func = b.function(
|
|
1376
1612
|
node.id,
|
|
1377
|
-
node.params.map(
|
|
1613
|
+
node.params.map(
|
|
1614
|
+
(param) =>
|
|
1615
|
+
/** @type {AST.Pattern} */ (context.visit(param, { ...context.state, metadata })),
|
|
1616
|
+
),
|
|
1378
1617
|
b.block(body_statements),
|
|
1379
1618
|
);
|
|
1380
1619
|
// Mark that this function was originally a component
|
|
1381
|
-
func.metadata = {
|
|
1620
|
+
func.metadata = /** @type {AST.FunctionExpression['metadata']} */ ({
|
|
1621
|
+
...func.metadata,
|
|
1622
|
+
was_component: true,
|
|
1623
|
+
});
|
|
1382
1624
|
func.loc = node.loc; // Copy source location for Volar mappings
|
|
1383
1625
|
return func;
|
|
1384
1626
|
}
|
|
@@ -1422,7 +1664,10 @@ const visitors = {
|
|
|
1422
1664
|
]),
|
|
1423
1665
|
);
|
|
1424
1666
|
// Mark that this function was originally a component
|
|
1425
|
-
func.metadata = {
|
|
1667
|
+
func.metadata = /** @type {AST.FunctionExpression['metadata']} */ ({
|
|
1668
|
+
...func.metadata,
|
|
1669
|
+
was_component: true,
|
|
1670
|
+
});
|
|
1426
1671
|
func.loc = node.loc; // Copy source location for Volar mappings
|
|
1427
1672
|
return func;
|
|
1428
1673
|
},
|
|
@@ -1448,14 +1693,18 @@ const visitors = {
|
|
|
1448
1693
|
|
|
1449
1694
|
return b.call(
|
|
1450
1695
|
'_$_.set_property',
|
|
1451
|
-
|
|
1452
|
-
|
|
1696
|
+
/** @type {AST.Expression} */ (
|
|
1697
|
+
context.visit(left.object, { ...context.state, metadata: { tracking: false } })
|
|
1698
|
+
),
|
|
1699
|
+
left.computed
|
|
1700
|
+
? /** @type {AST.Expression} */ (context.visit(left.property))
|
|
1701
|
+
: b.literal(/** @type {AST.Identifier} */ (left.property).name),
|
|
1453
1702
|
operator === '='
|
|
1454
|
-
? context.visit(right)
|
|
1703
|
+
? /** @type {AST.Expression} */ (context.visit(right))
|
|
1455
1704
|
: b.binary(
|
|
1456
1705
|
operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
|
|
1457
|
-
/** @type {Expression} */ (context.visit(left)),
|
|
1458
|
-
/** @type {Expression} */ (context.visit(right)),
|
|
1706
|
+
/** @type {AST.Expression} */ (context.visit(left)),
|
|
1707
|
+
/** @type {AST.Expression} */ (context.visit(right)),
|
|
1459
1708
|
),
|
|
1460
1709
|
);
|
|
1461
1710
|
}
|
|
@@ -1467,15 +1716,17 @@ const visitors = {
|
|
|
1467
1716
|
|
|
1468
1717
|
return b.call(
|
|
1469
1718
|
'_$_.set',
|
|
1470
|
-
|
|
1719
|
+
/** @type {AST.Expression} */ (
|
|
1720
|
+
context.visit(left, { ...context.state, metadata: { tracking: null } })
|
|
1721
|
+
),
|
|
1471
1722
|
operator === '='
|
|
1472
|
-
? context.visit(right)
|
|
1723
|
+
? /** @type {AST.Expression} */ (context.visit(right))
|
|
1473
1724
|
: b.binary(
|
|
1474
1725
|
operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
|
|
1475
|
-
/** @type {Expression} */ (
|
|
1726
|
+
/** @type {AST.Expression} */ (
|
|
1476
1727
|
context.visit(left, { ...context.state, metadata: { tracking: false } })
|
|
1477
1728
|
),
|
|
1478
|
-
/** @type {Expression} */ (context.visit(right)),
|
|
1729
|
+
/** @type {AST.Expression} */ (context.visit(right)),
|
|
1479
1730
|
),
|
|
1480
1731
|
);
|
|
1481
1732
|
}
|
|
@@ -1500,8 +1751,11 @@ const visitors = {
|
|
|
1500
1751
|
|
|
1501
1752
|
return b.call(
|
|
1502
1753
|
node.prefix ? '_$_.update_pre_property' : '_$_.update_property',
|
|
1503
|
-
|
|
1504
|
-
|
|
1754
|
+
/** @type {AST.Expression} */
|
|
1755
|
+
(context.visit(argument.object, { ...context.state, metadata: { tracking: false } })),
|
|
1756
|
+
argument.computed
|
|
1757
|
+
? /** @type {AST.Expression} */ (context.visit(argument.property))
|
|
1758
|
+
: b.literal(/** @type {AST.Identifier} */ (argument.property).name),
|
|
1505
1759
|
node.operator === '--' ? b.literal(-1) : undefined,
|
|
1506
1760
|
);
|
|
1507
1761
|
}
|
|
@@ -1509,7 +1763,8 @@ const visitors = {
|
|
|
1509
1763
|
if (argument.type === 'Identifier' && argument.tracked) {
|
|
1510
1764
|
return b.call(
|
|
1511
1765
|
node.prefix ? '_$_.update_pre' : '_$_.update',
|
|
1512
|
-
|
|
1766
|
+
/** @type {AST.Expression} */
|
|
1767
|
+
(context.visit(argument, { ...context.state, metadata: { tracking: null } })),
|
|
1513
1768
|
node.operator === '--' ? b.literal(-1) : undefined,
|
|
1514
1769
|
);
|
|
1515
1770
|
}
|
|
@@ -1517,17 +1772,18 @@ const visitors = {
|
|
|
1517
1772
|
if (argument.type === 'TrackedExpression') {
|
|
1518
1773
|
return b.call(
|
|
1519
1774
|
node.prefix ? '_$_.update_pre' : '_$_.update',
|
|
1520
|
-
|
|
1775
|
+
/** @type {AST.Expression} */
|
|
1776
|
+
(context.visit(argument.argument, { ...context.state, metadata: { tracking: null } })),
|
|
1521
1777
|
node.operator === '--' ? b.literal(-1) : undefined,
|
|
1522
1778
|
);
|
|
1523
1779
|
}
|
|
1524
1780
|
|
|
1525
|
-
const left = object(argument);
|
|
1781
|
+
const left = object(/** @type {AST.MemberExpression | AST.Identifier} */ (argument));
|
|
1526
1782
|
const binding = left && context.state.scope.get(left.name);
|
|
1527
1783
|
const transformers = left && binding?.transform;
|
|
1528
1784
|
|
|
1529
1785
|
if (left === argument) {
|
|
1530
|
-
const update_fn = transformers?.update
|
|
1786
|
+
const update_fn = transformers?.update;
|
|
1531
1787
|
if (update_fn) {
|
|
1532
1788
|
return update_fn(node);
|
|
1533
1789
|
}
|
|
@@ -1551,23 +1807,23 @@ const visitors = {
|
|
|
1551
1807
|
|
|
1552
1808
|
// do only if not controller
|
|
1553
1809
|
if (!is_controlled) {
|
|
1554
|
-
context.state.template
|
|
1810
|
+
context.state.template?.push('<!>');
|
|
1555
1811
|
}
|
|
1556
1812
|
|
|
1557
|
-
const id = context.state.flush_node(is_controlled);
|
|
1558
|
-
const pattern = node.left.declarations[0].id;
|
|
1559
|
-
const body_scope = context.state.scopes.get(node.body);
|
|
1813
|
+
const id = context.state.flush_node?.(is_controlled);
|
|
1814
|
+
const pattern = /** @type {AST.VariableDeclaration} */ (node.left).declarations[0].id;
|
|
1815
|
+
const body_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node.body));
|
|
1560
1816
|
|
|
1561
|
-
context.state.init
|
|
1817
|
+
context.state.init?.push(
|
|
1562
1818
|
b.stmt(
|
|
1563
1819
|
b.call(
|
|
1564
1820
|
key != null ? '_$_.for_keyed' : '_$_.for',
|
|
1565
1821
|
id,
|
|
1566
|
-
b.thunk(context.visit(node.right)),
|
|
1822
|
+
b.thunk(/** @type {AST.Expression} */ (context.visit(node.right))),
|
|
1567
1823
|
b.arrow(
|
|
1568
1824
|
index ? [b.id('__anchor'), pattern, index] : [b.id('__anchor'), pattern],
|
|
1569
1825
|
b.block(
|
|
1570
|
-
transform_body(node.body.body, {
|
|
1826
|
+
transform_body(/** @type {AST.BlockStatement} */ (node.body).body, {
|
|
1571
1827
|
...context,
|
|
1572
1828
|
state: { ...context.state, scope: body_scope, namespace: context.state.namespace },
|
|
1573
1829
|
}),
|
|
@@ -1575,7 +1831,10 @@ const visitors = {
|
|
|
1575
1831
|
),
|
|
1576
1832
|
b.literal(flags),
|
|
1577
1833
|
key != null
|
|
1578
|
-
? b.arrow(
|
|
1834
|
+
? b.arrow(
|
|
1835
|
+
index ? [pattern, index] : [pattern],
|
|
1836
|
+
/** @type {AST.Expression} */ (context.visit(key)),
|
|
1837
|
+
)
|
|
1579
1838
|
: undefined,
|
|
1580
1839
|
),
|
|
1581
1840
|
),
|
|
@@ -1586,9 +1845,9 @@ const visitors = {
|
|
|
1586
1845
|
if (!is_inside_component(context)) {
|
|
1587
1846
|
return context.next();
|
|
1588
1847
|
}
|
|
1589
|
-
context.state.template
|
|
1848
|
+
context.state.template?.push('<!>');
|
|
1590
1849
|
|
|
1591
|
-
const id = context.state.flush_node();
|
|
1850
|
+
const id = context.state.flush_node?.();
|
|
1592
1851
|
const statements = [];
|
|
1593
1852
|
const cases = [];
|
|
1594
1853
|
|
|
@@ -1610,9 +1869,10 @@ const visitors = {
|
|
|
1610
1869
|
statements.push(b.var(b.id(consequent_id), b.arrow([b.id('__anchor')], consequent)));
|
|
1611
1870
|
|
|
1612
1871
|
cases.push(
|
|
1613
|
-
b.switch_case(
|
|
1614
|
-
|
|
1615
|
-
|
|
1872
|
+
b.switch_case(
|
|
1873
|
+
switch_case.test ? /** @type {AST.Expression} */ (context.visit(switch_case.test)) : null,
|
|
1874
|
+
[b.return(b.id(consequent_id))],
|
|
1875
|
+
),
|
|
1616
1876
|
);
|
|
1617
1877
|
i++;
|
|
1618
1878
|
}
|
|
@@ -1622,26 +1882,32 @@ const visitors = {
|
|
|
1622
1882
|
b.call(
|
|
1623
1883
|
'_$_.switch',
|
|
1624
1884
|
id,
|
|
1625
|
-
b.thunk(
|
|
1885
|
+
b.thunk(
|
|
1886
|
+
b.block([
|
|
1887
|
+
b.switch(/** @type {AST.Expression} */ (context.visit(node.discriminant)), cases),
|
|
1888
|
+
]),
|
|
1889
|
+
),
|
|
1626
1890
|
),
|
|
1627
1891
|
),
|
|
1628
1892
|
);
|
|
1629
1893
|
|
|
1630
|
-
context.state.init
|
|
1894
|
+
context.state.init?.push(b.block(statements));
|
|
1631
1895
|
},
|
|
1632
1896
|
|
|
1633
1897
|
IfStatement(node, context) {
|
|
1634
1898
|
if (!is_inside_component(context)) {
|
|
1635
1899
|
return context.next();
|
|
1636
1900
|
}
|
|
1637
|
-
context.state.template
|
|
1901
|
+
context.state.template?.push('<!>');
|
|
1638
1902
|
|
|
1639
|
-
const id = context.state.flush_node();
|
|
1903
|
+
const id = context.state.flush_node?.();
|
|
1640
1904
|
const statements = [];
|
|
1641
1905
|
|
|
1642
|
-
const consequent_scope =
|
|
1906
|
+
const consequent_scope = /** @type {ScopeInterface} */ (
|
|
1907
|
+
context.state.scopes.get(node.consequent)
|
|
1908
|
+
);
|
|
1643
1909
|
const consequent = b.block(
|
|
1644
|
-
transform_body(node.consequent.body, {
|
|
1910
|
+
transform_body(/** @type {AST.BlockStatement} */ (node.consequent).body, {
|
|
1645
1911
|
...context,
|
|
1646
1912
|
state: { ...context.state, scope: consequent_scope },
|
|
1647
1913
|
}),
|
|
@@ -1653,19 +1919,18 @@ const visitors = {
|
|
|
1653
1919
|
let alternate_id;
|
|
1654
1920
|
|
|
1655
1921
|
if (node.alternate !== null) {
|
|
1656
|
-
const
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
const alternate = b.block(
|
|
1922
|
+
const alternate = /** @type {AST.BlockStatement | AST.IfStatement} */ (node.alternate);
|
|
1923
|
+
const alternate_scope = context.state.scopes.get(alternate) || context.state.scope;
|
|
1924
|
+
/** @type {AST.Node[]} */
|
|
1925
|
+
let alternate_body = alternate.type === 'IfStatement' ? [alternate] : alternate.body;
|
|
1926
|
+
const alternate_block = b.block(
|
|
1662
1927
|
transform_body(alternate_body, {
|
|
1663
1928
|
...context,
|
|
1664
1929
|
state: { ...context.state, scope: alternate_scope },
|
|
1665
1930
|
}),
|
|
1666
1931
|
);
|
|
1667
1932
|
alternate_id = context.state.scope.generate('alternate');
|
|
1668
|
-
statements.push(b.var(b.id(alternate_id), b.arrow([b.id('__anchor')],
|
|
1933
|
+
statements.push(b.var(b.id(alternate_id), b.arrow([b.id('__anchor')], alternate_block)));
|
|
1669
1934
|
}
|
|
1670
1935
|
|
|
1671
1936
|
statements.push(
|
|
@@ -1677,7 +1942,7 @@ const visitors = {
|
|
|
1677
1942
|
[b.id('__render')],
|
|
1678
1943
|
b.block([
|
|
1679
1944
|
b.if(
|
|
1680
|
-
context.visit(node.test),
|
|
1945
|
+
/** @type {AST.Expression} */ (context.visit(node.test)),
|
|
1681
1946
|
b.stmt(b.call(b.id('__render'), b.id(consequent_id))),
|
|
1682
1947
|
alternate_id
|
|
1683
1948
|
? b.stmt(
|
|
@@ -1695,12 +1960,12 @@ const visitors = {
|
|
|
1695
1960
|
),
|
|
1696
1961
|
);
|
|
1697
1962
|
|
|
1698
|
-
context.state.init
|
|
1963
|
+
context.state.init?.push(b.block(statements));
|
|
1699
1964
|
},
|
|
1700
1965
|
|
|
1701
1966
|
TSAsExpression(node, context) {
|
|
1702
1967
|
if (!context.state.to_ts) {
|
|
1703
|
-
return context.visit(node.expression);
|
|
1968
|
+
return context.visit(/** @type {AST.Expression} */ (node.expression));
|
|
1704
1969
|
}
|
|
1705
1970
|
return context.next();
|
|
1706
1971
|
},
|
|
@@ -1708,7 +1973,11 @@ const visitors = {
|
|
|
1708
1973
|
TSInstantiationExpression(node, context) {
|
|
1709
1974
|
if (!context.state.to_ts) {
|
|
1710
1975
|
// In JavaScript, just return the expression wrapped in parentheses
|
|
1711
|
-
return b.sequence([
|
|
1976
|
+
return b.sequence([
|
|
1977
|
+
/** @type {AST.Expression} */ (
|
|
1978
|
+
context.visit(/** @type {AST.Expression} */ (node.expression))
|
|
1979
|
+
),
|
|
1980
|
+
]);
|
|
1712
1981
|
}
|
|
1713
1982
|
return context.next();
|
|
1714
1983
|
},
|
|
@@ -1719,7 +1988,10 @@ const visitors = {
|
|
|
1719
1988
|
}
|
|
1720
1989
|
|
|
1721
1990
|
// Remove TSDeclareFunction nodes (function overload signatures) in JavaScript mode
|
|
1722
|
-
if (
|
|
1991
|
+
if (
|
|
1992
|
+
!context.state.to_ts &&
|
|
1993
|
+
/** @type {AST.RippleDeclaration} */ (node.declaration)?.type === 'TSDeclareFunction'
|
|
1994
|
+
) {
|
|
1723
1995
|
return b.empty;
|
|
1724
1996
|
}
|
|
1725
1997
|
|
|
@@ -1741,34 +2013,37 @@ const visitors = {
|
|
|
1741
2013
|
if (!is_inside_component(context)) {
|
|
1742
2014
|
return context.next();
|
|
1743
2015
|
}
|
|
1744
|
-
context.state.template
|
|
2016
|
+
context.state.template?.push('<!>');
|
|
1745
2017
|
|
|
1746
|
-
const id = context.state.flush_node();
|
|
2018
|
+
const id = context.state.flush_node?.();
|
|
1747
2019
|
const metadata = { await: false };
|
|
1748
2020
|
let body = transform_body(node.block.body, {
|
|
1749
2021
|
...context,
|
|
1750
2022
|
state: { ...context.state, metadata },
|
|
1751
2023
|
});
|
|
1752
2024
|
|
|
1753
|
-
if (
|
|
2025
|
+
if (node.pending) {
|
|
1754
2026
|
body = [b.stmt(b.call('_$_.async', b.thunk(b.block(body), true)))];
|
|
1755
2027
|
}
|
|
1756
2028
|
|
|
1757
|
-
|
|
2029
|
+
const handler = /** @type {AST.CatchClause | null} */ (node.handler);
|
|
2030
|
+
const pending = /** @type {AST.BlockStatement | null} */ (node.pending);
|
|
2031
|
+
|
|
2032
|
+
context.state.init?.push(
|
|
1758
2033
|
b.stmt(
|
|
1759
2034
|
b.call(
|
|
1760
2035
|
'_$_.try',
|
|
1761
2036
|
id,
|
|
1762
2037
|
b.arrow([b.id('__anchor')], b.block(body)),
|
|
1763
|
-
|
|
2038
|
+
handler === null
|
|
1764
2039
|
? b.literal(null)
|
|
1765
2040
|
: b.arrow(
|
|
1766
|
-
[b.id('__anchor'), ...(
|
|
1767
|
-
b.block(transform_body(
|
|
2041
|
+
[b.id('__anchor'), ...(handler.param ? [handler.param] : [])],
|
|
2042
|
+
b.block(transform_body(handler.body.body, context)),
|
|
1768
2043
|
),
|
|
1769
|
-
|
|
2044
|
+
pending === null
|
|
1770
2045
|
? undefined
|
|
1771
|
-
: b.arrow([b.id('__anchor')], b.block(transform_body(
|
|
2046
|
+
: b.arrow([b.id('__anchor')], b.block(transform_body(pending.body, context))),
|
|
1772
2047
|
),
|
|
1773
2048
|
),
|
|
1774
2049
|
);
|
|
@@ -1783,11 +2058,19 @@ const visitors = {
|
|
|
1783
2058
|
context.state.metadata.await = true;
|
|
1784
2059
|
}
|
|
1785
2060
|
|
|
1786
|
-
return b.call(
|
|
2061
|
+
return b.call(
|
|
2062
|
+
b.await(
|
|
2063
|
+
b.call('_$_.maybe_tracked', /** @type {AST.Expression} */ (context.visit(node.argument))),
|
|
2064
|
+
),
|
|
2065
|
+
);
|
|
1787
2066
|
},
|
|
1788
2067
|
|
|
1789
2068
|
BinaryExpression(node, context) {
|
|
1790
|
-
return b.binary(
|
|
2069
|
+
return b.binary(
|
|
2070
|
+
node.operator,
|
|
2071
|
+
/** @type {AST.Expression} */ (context.visit(node.left)),
|
|
2072
|
+
/** @type {AST.Expression} */ (context.visit(node.right)),
|
|
2073
|
+
);
|
|
1791
2074
|
},
|
|
1792
2075
|
|
|
1793
2076
|
TemplateLiteral(node, context) {
|
|
@@ -1797,15 +2080,18 @@ const visitors = {
|
|
|
1797
2080
|
return b.literal(node.quasis[0].value.cooked);
|
|
1798
2081
|
}
|
|
1799
2082
|
|
|
1800
|
-
const expressions =
|
|
2083
|
+
const expressions = /** @type {AST.Expression[]} */ (
|
|
2084
|
+
node.expressions.map((expr) => context.visit(expr))
|
|
2085
|
+
);
|
|
1801
2086
|
return b.template(node.quasis, expressions);
|
|
1802
2087
|
},
|
|
1803
2088
|
|
|
1804
2089
|
BlockStatement(node, context) {
|
|
2090
|
+
/** @type {AST.Statement[]} */
|
|
1805
2091
|
const statements = [];
|
|
1806
2092
|
|
|
1807
2093
|
for (const statement of node.body) {
|
|
1808
|
-
statements.push(context.visit(statement));
|
|
2094
|
+
statements.push(/** @type {AST.Statement} */ (context.visit(statement)));
|
|
1809
2095
|
}
|
|
1810
2096
|
|
|
1811
2097
|
return b.block(statements);
|
|
@@ -1822,7 +2108,7 @@ const visitors = {
|
|
|
1822
2108
|
return b.const(
|
|
1823
2109
|
'_$_server_$_',
|
|
1824
2110
|
b.object(
|
|
1825
|
-
exports.map((name) => {
|
|
2111
|
+
/** @type {AST.ServerBlock['metadata']['exports']} */ (exports).map((name) => {
|
|
1826
2112
|
const func_path = file_path + '#' + name;
|
|
1827
2113
|
// needs to be a sha256 hash of func_path, to avoid leaking file structure
|
|
1828
2114
|
const hash = createHash('sha256').update(func_path).digest('hex').slice(0, 8);
|
|
@@ -1841,11 +2127,17 @@ const visitors = {
|
|
|
1841
2127
|
);
|
|
1842
2128
|
},
|
|
1843
2129
|
|
|
2130
|
+
/** @type {Visitor<AST.Program, TransformClientState, AST.Node>} */
|
|
1844
2131
|
Program(node, context) {
|
|
2132
|
+
/** @type {Array<AST.Statement | AST.Directive | AST.ModuleDeclaration>} */
|
|
1845
2133
|
const statements = [];
|
|
1846
2134
|
|
|
1847
2135
|
for (const statement of node.body) {
|
|
1848
|
-
statements.push(
|
|
2136
|
+
statements.push(
|
|
2137
|
+
/** @type {AST.Statement | AST.Directive | AST.ModuleDeclaration} */ (
|
|
2138
|
+
context.visit(statement)
|
|
2139
|
+
),
|
|
2140
|
+
);
|
|
1849
2141
|
}
|
|
1850
2142
|
|
|
1851
2143
|
return { ...node, body: statements };
|
|
@@ -1853,14 +2145,14 @@ const visitors = {
|
|
|
1853
2145
|
};
|
|
1854
2146
|
|
|
1855
2147
|
/**
|
|
1856
|
-
* @param {Array<string | Expression>} items
|
|
2148
|
+
* @param {Array<string | AST.Expression>} items
|
|
1857
2149
|
*/
|
|
1858
2150
|
function join_template(items) {
|
|
1859
2151
|
let quasi = b.quasi('');
|
|
1860
2152
|
const template = b.template([quasi], []);
|
|
1861
2153
|
|
|
1862
2154
|
/**
|
|
1863
|
-
* @param {Expression} expression
|
|
2155
|
+
* @param {AST.Expression} expression
|
|
1864
2156
|
*/
|
|
1865
2157
|
function push(expression) {
|
|
1866
2158
|
if (expression.type === 'TemplateLiteral') {
|
|
@@ -1872,8 +2164,8 @@ function join_template(items) {
|
|
|
1872
2164
|
push(e);
|
|
1873
2165
|
}
|
|
1874
2166
|
|
|
1875
|
-
const last =
|
|
1876
|
-
quasi.value.cooked += /** @type {string} */ (last
|
|
2167
|
+
const last = expression.quasis.at(-1);
|
|
2168
|
+
quasi.value.cooked += /** @type {string} */ (last?.value.cooked);
|
|
1877
2169
|
} else if (expression.type === 'Literal') {
|
|
1878
2170
|
/** @type {string} */ (quasi.value.cooked) += expression.value;
|
|
1879
2171
|
} else {
|
|
@@ -1899,27 +2191,33 @@ function join_template(items) {
|
|
|
1899
2191
|
return template;
|
|
1900
2192
|
}
|
|
1901
2193
|
|
|
2194
|
+
/**
|
|
2195
|
+
* @param {AST.Node} node
|
|
2196
|
+
* @param {TransformClientContext} context
|
|
2197
|
+
*/
|
|
1902
2198
|
function transform_ts_child(node, context) {
|
|
1903
2199
|
const { state, visit } = context;
|
|
1904
2200
|
|
|
1905
2201
|
if (node.type === 'Text') {
|
|
1906
|
-
state.init
|
|
2202
|
+
state.init?.push(b.stmt(/** @type {AST.Expression} */ (visit(node.expression, { ...state }))));
|
|
1907
2203
|
} else if (node.type === 'Html') {
|
|
1908
2204
|
// Do we need to do something special here?
|
|
1909
|
-
state.init
|
|
2205
|
+
state.init?.push(b.stmt(/** @type {AST.Expression} */ (visit(node.expression, { ...state }))));
|
|
1910
2206
|
} else if (node.type === 'Element') {
|
|
1911
2207
|
// Use capitalized name for dynamic components/elements in TypeScript output
|
|
1912
2208
|
// If node.id is not an Identifier (e.g., MemberExpression like props.children),
|
|
1913
2209
|
// we need to visit it to get the proper expression
|
|
2210
|
+
/** @type {string | AST.Node} */
|
|
1914
2211
|
let type_expression;
|
|
1915
2212
|
let type_is_expression = false;
|
|
1916
|
-
if (node.id.type === 'MemberExpression') {
|
|
2213
|
+
if (/** @type {AST.Node} */ (node.id).type === 'MemberExpression') {
|
|
1917
2214
|
// For MemberExpressions, we need to create a JSXExpression, not a JSXIdentifier
|
|
1918
2215
|
type_expression = visit(node.id, state);
|
|
1919
2216
|
type_is_expression = true;
|
|
1920
2217
|
} else {
|
|
1921
2218
|
type_expression = node.metadata?.ts_name || node.id.name;
|
|
1922
2219
|
}
|
|
2220
|
+
/** @type {ESTreeJSX.JSXElement['children']} */
|
|
1923
2221
|
const children = [];
|
|
1924
2222
|
let has_children_props = false;
|
|
1925
2223
|
|
|
@@ -1947,27 +2245,35 @@ function transform_ts_child(node, context) {
|
|
|
1947
2245
|
}
|
|
1948
2246
|
jsx_name.loc = attr.name.loc || name.loc;
|
|
1949
2247
|
|
|
1950
|
-
const jsx_attr = b.jsx_attribute(
|
|
2248
|
+
const jsx_attr = b.jsx_attribute(
|
|
2249
|
+
jsx_name,
|
|
2250
|
+
b.jsx_expression_container(/** @type {AST.Expression} */ (value)),
|
|
2251
|
+
);
|
|
1951
2252
|
// Preserve shorthand flag from parser (set for {identifier} syntax)
|
|
1952
2253
|
jsx_attr.shorthand = attr.shorthand ?? false;
|
|
1953
2254
|
return jsx_attr;
|
|
1954
2255
|
} else if (attr.type === 'SpreadAttribute') {
|
|
1955
2256
|
const metadata = { await: false };
|
|
1956
2257
|
const argument = visit(attr.argument, { ...state, metadata });
|
|
1957
|
-
return b.jsx_spread_attribute(argument);
|
|
2258
|
+
return b.jsx_spread_attribute(/** @type {AST.Expression} */ (argument));
|
|
1958
2259
|
} else if (attr.type === 'RefAttribute') {
|
|
1959
2260
|
const createRefKeyAlias = import_from_ripple_if_needed('createRefKey', context);
|
|
1960
2261
|
const metadata = { await: false };
|
|
1961
2262
|
const argument = visit(attr.argument, { ...state, metadata });
|
|
1962
|
-
const wrapper = b.object([
|
|
2263
|
+
const wrapper = b.object([
|
|
2264
|
+
b.prop('init', b.call(createRefKeyAlias), /** @type {AST.Expression} */ (argument), true),
|
|
2265
|
+
]);
|
|
1963
2266
|
return b.jsx_spread_attribute(wrapper);
|
|
2267
|
+
} else {
|
|
2268
|
+
// Should not happen
|
|
2269
|
+
throw new Error(`Unexpected attribute type: ${/** @type {AST.Attribute} */ (attr).type}`);
|
|
1964
2270
|
}
|
|
1965
2271
|
});
|
|
1966
2272
|
|
|
1967
2273
|
if (!node.selfClosing && !node.unclosed && !has_children_props && node.children.length > 0) {
|
|
1968
2274
|
const is_dom_element = is_element_dom_element(node);
|
|
1969
2275
|
|
|
1970
|
-
const component_scope = context.state.scopes.get(node);
|
|
2276
|
+
const component_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node));
|
|
1971
2277
|
const thunk = b.thunk(
|
|
1972
2278
|
b.block(
|
|
1973
2279
|
transform_body(node.children, {
|
|
@@ -1984,15 +2290,19 @@ function transform_ts_child(node, context) {
|
|
|
1984
2290
|
}
|
|
1985
2291
|
}
|
|
1986
2292
|
|
|
1987
|
-
|
|
2293
|
+
/** @type {ESTreeJSX.JSXIdentifier | AST.Node | undefined} */
|
|
2294
|
+
let opening_name_element;
|
|
2295
|
+
/** @type {AST.Node | ESTreeJSX.JSXClosingElement['name'] | undefined} */
|
|
2296
|
+
let closing_name_element;
|
|
1988
2297
|
|
|
1989
2298
|
if (type_is_expression) {
|
|
1990
2299
|
// For dynamic/expression-based components (e.g., props.children),
|
|
1991
2300
|
// use JSX expression instead of identifier
|
|
1992
|
-
opening_name_element = type_expression;
|
|
1993
|
-
closing_name_element =
|
|
2301
|
+
opening_name_element = /** @type {AST.Node} */ (type_expression);
|
|
2302
|
+
closing_name_element =
|
|
2303
|
+
node.selfClosing || node.unclosed ? undefined : /** @type {AST.Node} */ (type_expression);
|
|
1994
2304
|
} else {
|
|
1995
|
-
opening_name_element = b.jsx_id(type_expression);
|
|
2305
|
+
opening_name_element = b.jsx_id(/** @type {string} */ (type_expression));
|
|
1996
2306
|
// For tracked identifiers (dynamic components), adjust the loc to skip the '@' prefix
|
|
1997
2307
|
// and add metadata for mapping
|
|
1998
2308
|
if (node.id.tracked && node.id.loc) {
|
|
@@ -2009,6 +2319,7 @@ function transform_ts_child(node, context) {
|
|
|
2009
2319
|
opening_name_element.metadata = {
|
|
2010
2320
|
original_name: node.metadata.original_name,
|
|
2011
2321
|
is_capitalized: true,
|
|
2322
|
+
path: [...node.metadata.path],
|
|
2012
2323
|
};
|
|
2013
2324
|
}
|
|
2014
2325
|
} else {
|
|
@@ -2020,18 +2331,19 @@ function transform_ts_child(node, context) {
|
|
|
2020
2331
|
},
|
|
2021
2332
|
end: {
|
|
2022
2333
|
line: node.loc.start.line,
|
|
2023
|
-
column: node.loc.start.column + 2 + type_expression.length,
|
|
2334
|
+
column: node.loc.start.column + 2 + /** @type {string} */ (type_expression).length,
|
|
2024
2335
|
},
|
|
2025
2336
|
};
|
|
2026
2337
|
}
|
|
2027
2338
|
|
|
2028
2339
|
if (!node.selfClosing && !node.unclosed) {
|
|
2029
|
-
closing_name_element = b.jsx_id(type_expression);
|
|
2340
|
+
closing_name_element = b.jsx_id(/** @type {string} */ (type_expression));
|
|
2030
2341
|
// For tracked identifiers, also adjust closing tag location
|
|
2031
2342
|
if (node.id.tracked && node.id.loc) {
|
|
2032
2343
|
// Calculate position relative to closing tag
|
|
2033
2344
|
// Format: </@identifier>
|
|
2034
|
-
const closing_tag_start =
|
|
2345
|
+
const closing_tag_start =
|
|
2346
|
+
node.loc.end.column - /** @type {string} */ (type_expression).length - 3; // </@
|
|
2035
2347
|
closing_name_element.loc = {
|
|
2036
2348
|
start: {
|
|
2037
2349
|
line: node.loc.end.line,
|
|
@@ -2042,7 +2354,8 @@ function transform_ts_child(node, context) {
|
|
|
2042
2354
|
column:
|
|
2043
2355
|
closing_tag_start +
|
|
2044
2356
|
3 +
|
|
2045
|
-
(node.metadata?.original_name?.length ||
|
|
2357
|
+
(node.metadata?.original_name?.length ||
|
|
2358
|
+
/** @type {string} */ (type_expression).length),
|
|
2046
2359
|
},
|
|
2047
2360
|
};
|
|
2048
2361
|
// Add metadata if this was capitalized
|
|
@@ -2050,13 +2363,14 @@ function transform_ts_child(node, context) {
|
|
|
2050
2363
|
closing_name_element.metadata = {
|
|
2051
2364
|
original_name: node.metadata.original_name,
|
|
2052
2365
|
is_capitalized: true,
|
|
2366
|
+
path: [...node.metadata.path],
|
|
2053
2367
|
};
|
|
2054
2368
|
}
|
|
2055
2369
|
} else {
|
|
2056
2370
|
closing_name_element.loc = {
|
|
2057
2371
|
start: {
|
|
2058
2372
|
line: node.loc.end.line,
|
|
2059
|
-
column: node.loc.end.column - type_expression.length - 1,
|
|
2373
|
+
column: node.loc.end.column - /** @type {string} */ (type_expression).length - 1,
|
|
2060
2374
|
},
|
|
2061
2375
|
end: {
|
|
2062
2376
|
line: node.loc.end.line,
|
|
@@ -2068,13 +2382,11 @@ function transform_ts_child(node, context) {
|
|
|
2068
2382
|
}
|
|
2069
2383
|
|
|
2070
2384
|
let jsxElement = b.jsx_element(
|
|
2071
|
-
opening_name_element,
|
|
2072
|
-
node
|
|
2385
|
+
/** @type {ESTreeJSX.JSXIdentifier} */ (opening_name_element),
|
|
2386
|
+
node,
|
|
2073
2387
|
attributes,
|
|
2074
2388
|
children,
|
|
2075
|
-
|
|
2076
|
-
node.unclosed,
|
|
2077
|
-
closing_name_element,
|
|
2389
|
+
/** @type {ESTreeJSX.JSXClosingElement['name'] | undefined} */ (closing_name_element),
|
|
2078
2390
|
);
|
|
2079
2391
|
|
|
2080
2392
|
// Calculate the location for the entire JSXClosingElement (including </ and >)
|
|
@@ -2084,8 +2396,9 @@ function transform_ts_child(node, context) {
|
|
|
2084
2396
|
// - '<' is at node.loc.end.column - type_expression.length - 3
|
|
2085
2397
|
// - '>' is at node.loc.end.column - 1
|
|
2086
2398
|
const tag_name_length = node.id.tracked
|
|
2087
|
-
? (node.metadata?.original_name?.length || type_expression.length) +
|
|
2088
|
-
|
|
2399
|
+
? (node.metadata?.original_name?.length || /** @type {string} */ (type_expression).length) +
|
|
2400
|
+
1 // +1 for '@'
|
|
2401
|
+
: /** @type {string} */ (type_expression).length;
|
|
2089
2402
|
|
|
2090
2403
|
jsxElement.closingElement.loc = {
|
|
2091
2404
|
start: {
|
|
@@ -2104,19 +2417,22 @@ function transform_ts_child(node, context) {
|
|
|
2104
2417
|
jsxElement.metadata = {
|
|
2105
2418
|
ts_name: node.metadata.ts_name,
|
|
2106
2419
|
original_name: node.metadata.original_name,
|
|
2420
|
+
path: [...node.metadata.path],
|
|
2107
2421
|
};
|
|
2108
2422
|
}
|
|
2109
2423
|
// For unclosed elements, push the JSXElement directly without wrapping in ExpressionStatement
|
|
2110
2424
|
// This keeps it in the AST for mappings but avoids adding a semicolon
|
|
2111
2425
|
if (node.unclosed) {
|
|
2112
|
-
state.init
|
|
2426
|
+
state.init?.push(/** @type {AST.Statement} */ (/** @type {unknown} */ (jsxElement)));
|
|
2113
2427
|
} else {
|
|
2114
|
-
state.init
|
|
2428
|
+
state.init?.push(b.stmt(jsxElement));
|
|
2115
2429
|
}
|
|
2116
2430
|
} else if (node.type === 'IfStatement') {
|
|
2117
|
-
const consequent_scope =
|
|
2431
|
+
const consequent_scope = /** @type {ScopeInterface} */ (
|
|
2432
|
+
context.state.scopes.get(node.consequent)
|
|
2433
|
+
);
|
|
2118
2434
|
const consequent = b.block(
|
|
2119
|
-
transform_body(node.consequent.body, {
|
|
2435
|
+
transform_body(/** @type {AST.BlockStatement} */ (node.consequent).body, {
|
|
2120
2436
|
...context,
|
|
2121
2437
|
state: { ...context.state, scope: consequent_scope },
|
|
2122
2438
|
}),
|
|
@@ -2125,11 +2441,10 @@ function transform_ts_child(node, context) {
|
|
|
2125
2441
|
let alternate;
|
|
2126
2442
|
|
|
2127
2443
|
if (node.alternate !== null) {
|
|
2128
|
-
const
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
}
|
|
2444
|
+
const alternate_node = /** @type {AST.BlockStatement | AST.IfStatement} */ (node.alternate);
|
|
2445
|
+
const alternate_scope = context.state.scopes.get(alternate_node) || context.state.scope;
|
|
2446
|
+
const alternate_body =
|
|
2447
|
+
alternate_node.type === 'IfStatement' ? [alternate_node] : alternate_node.body;
|
|
2133
2448
|
alternate = b.block(
|
|
2134
2449
|
transform_body(alternate_body, {
|
|
2135
2450
|
...context,
|
|
@@ -2138,7 +2453,7 @@ function transform_ts_child(node, context) {
|
|
|
2138
2453
|
);
|
|
2139
2454
|
}
|
|
2140
2455
|
|
|
2141
|
-
state.init
|
|
2456
|
+
state.init?.push(b.if(/** @type {AST.Expression} */ (visit(node.test)), consequent, alternate));
|
|
2142
2457
|
} else if (node.type === 'SwitchStatement') {
|
|
2143
2458
|
const cases = [];
|
|
2144
2459
|
|
|
@@ -2151,28 +2466,40 @@ function transform_ts_child(node, context) {
|
|
|
2151
2466
|
});
|
|
2152
2467
|
|
|
2153
2468
|
cases.push(
|
|
2154
|
-
b.switch_case(
|
|
2469
|
+
b.switch_case(
|
|
2470
|
+
switch_case.test ? /** @type {AST.Expression} */ (context.visit(switch_case.test)) : null,
|
|
2471
|
+
consequent_body,
|
|
2472
|
+
),
|
|
2155
2473
|
);
|
|
2156
2474
|
}
|
|
2157
2475
|
|
|
2158
|
-
context.state.init
|
|
2476
|
+
context.state.init?.push(
|
|
2477
|
+
b.switch(/** @type {AST.Expression} */ (context.visit(node.discriminant)), cases),
|
|
2478
|
+
);
|
|
2159
2479
|
} else if (node.type === 'ForOfStatement') {
|
|
2160
|
-
const body_scope = context.state.scopes.get(node.body);
|
|
2161
|
-
const block_body = transform_body(node.body.body, {
|
|
2480
|
+
const body_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node.body));
|
|
2481
|
+
const block_body = transform_body(/** @type {AST.BlockStatement} */ (node.body).body, {
|
|
2162
2482
|
...context,
|
|
2163
2483
|
state: { ...context.state, scope: body_scope },
|
|
2164
2484
|
});
|
|
2165
2485
|
if (node.key) {
|
|
2166
|
-
block_body.unshift(b.stmt(visit(node.key)));
|
|
2486
|
+
block_body.unshift(b.stmt(/** @type {AST.Expression} */ (visit(node.key))));
|
|
2167
2487
|
}
|
|
2168
2488
|
if (node.index) {
|
|
2169
|
-
block_body.unshift(b.let(visit(node.index), b.literal(0)));
|
|
2489
|
+
block_body.unshift(b.let(/** @type {AST.Identifier} */ (visit(node.index)), b.literal(0)));
|
|
2170
2490
|
}
|
|
2171
2491
|
const body = b.block(block_body);
|
|
2172
2492
|
|
|
2173
|
-
state.init
|
|
2493
|
+
state.init?.push(
|
|
2494
|
+
b.for_of(
|
|
2495
|
+
/** @type {AST.Pattern} */ (visit(node.left)),
|
|
2496
|
+
/** @type {AST.Expression} */ (visit(node.right)),
|
|
2497
|
+
body,
|
|
2498
|
+
node.await,
|
|
2499
|
+
),
|
|
2500
|
+
);
|
|
2174
2501
|
} else if (node.type === 'TryStatement') {
|
|
2175
|
-
const try_scope = context.state.scopes.get(node.block);
|
|
2502
|
+
const try_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node.block));
|
|
2176
2503
|
const try_body = b.block(
|
|
2177
2504
|
transform_body(node.block.body, {
|
|
2178
2505
|
...context,
|
|
@@ -2182,7 +2509,9 @@ function transform_ts_child(node, context) {
|
|
|
2182
2509
|
|
|
2183
2510
|
let catch_handler = null;
|
|
2184
2511
|
if (node.handler) {
|
|
2185
|
-
const catch_scope =
|
|
2512
|
+
const catch_scope = /** @type {ScopeInterface} */ (
|
|
2513
|
+
context.state.scopes.get(node.handler.body)
|
|
2514
|
+
);
|
|
2186
2515
|
const catch_body = b.block(
|
|
2187
2516
|
transform_body(node.handler.body.body, {
|
|
2188
2517
|
...context,
|
|
@@ -2194,7 +2523,7 @@ function transform_ts_child(node, context) {
|
|
|
2194
2523
|
|
|
2195
2524
|
let pending_block = null;
|
|
2196
2525
|
if (node.pending) {
|
|
2197
|
-
const pending_scope = context.state.scopes.get(node.pending);
|
|
2526
|
+
const pending_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node.pending));
|
|
2198
2527
|
pending_block = b.try_item_block(
|
|
2199
2528
|
transform_body(node.pending.body, {
|
|
2200
2529
|
...context,
|
|
@@ -2206,7 +2535,9 @@ function transform_ts_child(node, context) {
|
|
|
2206
2535
|
|
|
2207
2536
|
let finally_block = null;
|
|
2208
2537
|
if (node.finalizer) {
|
|
2209
|
-
const finally_scope =
|
|
2538
|
+
const finally_scope = /** @type {ScopeInterface} */ (
|
|
2539
|
+
context.state.scopes.get(node.finalizer)
|
|
2540
|
+
);
|
|
2210
2541
|
finally_block = b.block(
|
|
2211
2542
|
transform_body(node.finalizer.body, {
|
|
2212
2543
|
...context,
|
|
@@ -2215,33 +2546,46 @@ function transform_ts_child(node, context) {
|
|
|
2215
2546
|
);
|
|
2216
2547
|
}
|
|
2217
2548
|
|
|
2218
|
-
state.init
|
|
2549
|
+
state.init?.push(b.try(try_body, catch_handler, finally_block, pending_block));
|
|
2219
2550
|
} else if (node.type === 'Component') {
|
|
2220
2551
|
const component = visit(node, state);
|
|
2221
2552
|
|
|
2222
|
-
state.init
|
|
2553
|
+
state.init?.push(/** @type {AST.Statement} */ (component));
|
|
2223
2554
|
} else if (node.type === 'BreakStatement') {
|
|
2224
|
-
state.init
|
|
2555
|
+
state.init?.push(/** @type {AST.Statement} */ (b.break));
|
|
2225
2556
|
} else if (node.type === 'TsxCompat') {
|
|
2226
|
-
const children =
|
|
2227
|
-
.
|
|
2228
|
-
|
|
2557
|
+
const children = /** @type {AST.TsxCompat['children']} */ (
|
|
2558
|
+
node.children
|
|
2559
|
+
.map((child) => visit(/** @type {AST.Node} */ (child), state))
|
|
2560
|
+
.filter((child) => child.type !== 'JSXText' || child.value.trim() !== '')
|
|
2561
|
+
);
|
|
2229
2562
|
|
|
2230
|
-
state.init
|
|
2563
|
+
state.init?.push(b.stmt(b.jsx_fragment(children)));
|
|
2231
2564
|
} else if (node.type === 'JSXExpressionContainer') {
|
|
2232
2565
|
// JSX comments {/* ... */} are JSXExpressionContainer with JSXEmptyExpression
|
|
2233
2566
|
// These should be preserved in the output as-is for prettier to handle
|
|
2234
|
-
|
|
2567
|
+
const jsx_container = b.jsx_expression_container(
|
|
2568
|
+
/** @type {AST.Expression} */ (visit(node.expression, state)),
|
|
2569
|
+
);
|
|
2570
|
+
state.init?.push(/** @type {AST.Statement} */ (/** @type {unknown} */ (jsx_container)));
|
|
2235
2571
|
} else {
|
|
2236
2572
|
throw new Error('TODO');
|
|
2237
2573
|
}
|
|
2238
2574
|
}
|
|
2239
2575
|
|
|
2576
|
+
/**
|
|
2577
|
+
*
|
|
2578
|
+
* @param {AST.Node[]} children
|
|
2579
|
+
* @param {VisitorClientContext} context
|
|
2580
|
+
*/
|
|
2240
2581
|
function transform_children(children, context) {
|
|
2241
2582
|
const { visit, state, root } = context;
|
|
2242
2583
|
const normalized = normalize_children(children, context);
|
|
2243
|
-
|
|
2244
|
-
|
|
2584
|
+
|
|
2585
|
+
const head_elements = /** @type {AST.Element[]} */ (
|
|
2586
|
+
children.filter(
|
|
2587
|
+
(node) => node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'head',
|
|
2588
|
+
)
|
|
2245
2589
|
);
|
|
2246
2590
|
|
|
2247
2591
|
const is_fragment =
|
|
@@ -2259,10 +2603,12 @@ function transform_children(children, context) {
|
|
|
2259
2603
|
normalized.filter(
|
|
2260
2604
|
(node) => node.type !== 'VariableDeclaration' && node.type !== 'EmptyStatement',
|
|
2261
2605
|
).length > 1;
|
|
2606
|
+
/** @type {AST.Identifier | null} */
|
|
2262
2607
|
let initial = null;
|
|
2263
2608
|
let prev = null;
|
|
2264
2609
|
let template_id = null;
|
|
2265
2610
|
|
|
2611
|
+
/** @param {AST.Node} node */
|
|
2266
2612
|
const get_id = (node) => {
|
|
2267
2613
|
return b.id(
|
|
2268
2614
|
node.type == 'Element' && is_element_dom_element(node)
|
|
@@ -2273,11 +2619,12 @@ function transform_children(children, context) {
|
|
|
2273
2619
|
);
|
|
2274
2620
|
};
|
|
2275
2621
|
|
|
2622
|
+
/** @param {AST.Node} node */
|
|
2276
2623
|
const create_initial = (node) => {
|
|
2277
2624
|
const id = is_fragment ? b.id(state.scope.generate('fragment')) : get_id(node);
|
|
2278
2625
|
initial = id;
|
|
2279
2626
|
template_id = state.scope.generate('root');
|
|
2280
|
-
state.setup
|
|
2627
|
+
state.setup?.push(b.var(id, b.call(template_id)));
|
|
2281
2628
|
};
|
|
2282
2629
|
|
|
2283
2630
|
for (const node of normalized) {
|
|
@@ -2293,22 +2640,23 @@ function transform_children(children, context) {
|
|
|
2293
2640
|
node.type === 'Component'
|
|
2294
2641
|
) {
|
|
2295
2642
|
const metadata = { await: false };
|
|
2296
|
-
state.init
|
|
2643
|
+
state.init?.push(/** @type {AST.Statement} */ (visit(node, { ...state, metadata })));
|
|
2297
2644
|
if (metadata.await) {
|
|
2298
|
-
state.init
|
|
2645
|
+
state.init?.push(b.if(b.call('_$_.aborted'), b.return(null)));
|
|
2299
2646
|
if (state.metadata?.await === false) {
|
|
2300
2647
|
state.metadata.await = true;
|
|
2301
2648
|
}
|
|
2302
2649
|
}
|
|
2303
2650
|
} else if (state.to_ts) {
|
|
2304
|
-
transform_ts_child(node, { visit, state });
|
|
2651
|
+
transform_ts_child(node, /** @type {VisitorClientContext} */ ({ visit, state }));
|
|
2305
2652
|
} else {
|
|
2306
2653
|
let metadata;
|
|
2307
|
-
|
|
2654
|
+
/** @type {AST.Expression | undefined} */
|
|
2655
|
+
let expression = undefined;
|
|
2308
2656
|
let isCreateTextOnly = false;
|
|
2309
2657
|
if (node.type === 'Text' || node.type === 'Html') {
|
|
2310
2658
|
metadata = { tracking: false, await: false };
|
|
2311
|
-
expression = visit(node.expression, { ...state, metadata });
|
|
2659
|
+
expression = /** @type {AST.Expression} */ (visit(node.expression, { ...state, metadata }));
|
|
2312
2660
|
isCreateTextOnly =
|
|
2313
2661
|
node.type === 'Text' && normalized.length === 1 && expression.type === 'Literal';
|
|
2314
2662
|
}
|
|
@@ -2318,30 +2666,32 @@ function transform_children(children, context) {
|
|
|
2318
2666
|
}
|
|
2319
2667
|
|
|
2320
2668
|
const current_prev = prev;
|
|
2669
|
+
/** @type {AST.Identifier | null} */
|
|
2321
2670
|
let cached;
|
|
2671
|
+
/** @param {boolean} [is_controlled] */
|
|
2322
2672
|
const flush_node = (is_controlled) => {
|
|
2323
2673
|
if (cached && !is_controlled) {
|
|
2324
2674
|
return cached;
|
|
2325
2675
|
} else if (current_prev !== null) {
|
|
2326
2676
|
const id = get_id(node);
|
|
2327
|
-
state.setup
|
|
2677
|
+
state.setup?.push(b.var(id, b.call('_$_.sibling', current_prev())));
|
|
2328
2678
|
cached = id;
|
|
2329
2679
|
return id;
|
|
2330
2680
|
} else if (initial !== null) {
|
|
2331
2681
|
if (is_fragment) {
|
|
2332
2682
|
const id = get_id(node);
|
|
2333
|
-
state.setup
|
|
2683
|
+
state.setup?.push(b.var(id, b.call('_$_.child_frag', initial)));
|
|
2334
2684
|
cached = id;
|
|
2335
2685
|
return id;
|
|
2336
2686
|
}
|
|
2337
2687
|
return initial;
|
|
2338
2688
|
} else if (state.flush_node !== null) {
|
|
2339
2689
|
if (is_controlled) {
|
|
2340
|
-
return state.flush_node();
|
|
2690
|
+
return state.flush_node?.();
|
|
2341
2691
|
}
|
|
2342
2692
|
|
|
2343
2693
|
const id = get_id(node);
|
|
2344
|
-
state.setup
|
|
2694
|
+
state.setup?.push(b.var(id, b.call('_$_.child', state.flush_node?.())));
|
|
2345
2695
|
cached = id;
|
|
2346
2696
|
return id;
|
|
2347
2697
|
} else {
|
|
@@ -2354,80 +2704,117 @@ function transform_children(children, context) {
|
|
|
2354
2704
|
const is_controlled = normalized.length === 1 && !root;
|
|
2355
2705
|
|
|
2356
2706
|
if (node.type === 'Element') {
|
|
2357
|
-
visit(node, {
|
|
2358
|
-
|
|
2359
|
-
|
|
2707
|
+
visit(node, {
|
|
2708
|
+
...state,
|
|
2709
|
+
flush_node: /** @type {TransformClientState['flush_node']} */ (flush_node),
|
|
2710
|
+
namespace: state.namespace,
|
|
2711
|
+
});
|
|
2360
2712
|
} else if (node.type === 'TsxCompat') {
|
|
2361
|
-
visit(node, {
|
|
2713
|
+
visit(node, {
|
|
2714
|
+
...state,
|
|
2715
|
+
flush_node: /** @type {TransformClientState['flush_node']} */ (flush_node),
|
|
2716
|
+
namespace: state.namespace,
|
|
2717
|
+
});
|
|
2362
2718
|
} else if (node.type === 'Html') {
|
|
2363
|
-
context.state.template
|
|
2719
|
+
context.state.template?.push('<!>');
|
|
2364
2720
|
|
|
2365
2721
|
const id = flush_node();
|
|
2366
|
-
state.update
|
|
2367
|
-
operation:
|
|
2368
|
-
b.
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2722
|
+
state.update?.push({
|
|
2723
|
+
operation: () =>
|
|
2724
|
+
b.stmt(
|
|
2725
|
+
b.call(
|
|
2726
|
+
'_$_.html',
|
|
2727
|
+
id,
|
|
2728
|
+
b.thunk(/** @type {AST.Expression} */ (expression)),
|
|
2729
|
+
state.namespace === 'svg' && b.true,
|
|
2730
|
+
state.namespace === 'mathml' && b.true,
|
|
2731
|
+
),
|
|
2374
2732
|
),
|
|
2375
|
-
),
|
|
2376
2733
|
});
|
|
2377
2734
|
} else if (node.type === 'Text') {
|
|
2378
|
-
if (metadata
|
|
2379
|
-
state.template
|
|
2735
|
+
if (metadata?.tracking) {
|
|
2736
|
+
state.template?.push(' ');
|
|
2380
2737
|
const id = flush_node();
|
|
2381
|
-
state.update
|
|
2738
|
+
state.update?.push({
|
|
2382
2739
|
operation: (key) => b.stmt(b.call('_$_.set_text', id, key)),
|
|
2383
|
-
expression,
|
|
2740
|
+
expression: /** @type {AST.Expression} */ (expression),
|
|
2384
2741
|
identity: node.expression,
|
|
2385
2742
|
initial: b.literal(' '),
|
|
2386
2743
|
});
|
|
2387
2744
|
if (metadata.await) {
|
|
2388
|
-
state.update.async = true;
|
|
2745
|
+
/** @type {NonNullable<TransformClientState['update']>} */ (state.update).async = true;
|
|
2389
2746
|
}
|
|
2390
2747
|
} else if (normalized.length === 1) {
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2748
|
+
const expr = /** @type {AST.Expression} */ (expression);
|
|
2749
|
+
if (expr.type === 'Literal') {
|
|
2750
|
+
if (
|
|
2751
|
+
/** @type {NonNullable<TransformClientState['template']>} */ (state.template).length >
|
|
2752
|
+
0
|
|
2753
|
+
) {
|
|
2754
|
+
state.template?.push(escape_html(expr.value));
|
|
2394
2755
|
} else {
|
|
2395
2756
|
const id = flush_node();
|
|
2396
|
-
state.init
|
|
2397
|
-
|
|
2757
|
+
state.init?.push(
|
|
2758
|
+
b.var(/** @type {AST.Identifier} */ (id), b.call('_$_.create_text', expr)),
|
|
2759
|
+
);
|
|
2760
|
+
state.final?.push(b.stmt(b.call('_$_.append', b.id('__anchor'), id)));
|
|
2398
2761
|
}
|
|
2399
2762
|
} else {
|
|
2400
2763
|
const id = flush_node();
|
|
2401
|
-
state.template
|
|
2764
|
+
state.template?.push(' ');
|
|
2402
2765
|
// avoid set_text overhead for single text nodes
|
|
2403
|
-
state.init
|
|
2766
|
+
state.init?.push(
|
|
2767
|
+
b.stmt(
|
|
2768
|
+
b.assignment(
|
|
2769
|
+
'=',
|
|
2770
|
+
b.member(/** @type {AST.Identifier} */ (id), b.id('nodeValue')),
|
|
2771
|
+
expr,
|
|
2772
|
+
),
|
|
2773
|
+
),
|
|
2774
|
+
);
|
|
2404
2775
|
}
|
|
2405
2776
|
} else {
|
|
2406
2777
|
// Handle Text nodes in fragments
|
|
2407
|
-
state.template
|
|
2778
|
+
state.template?.push(' ');
|
|
2408
2779
|
const id = flush_node();
|
|
2409
|
-
state.update
|
|
2780
|
+
state.update?.push({
|
|
2410
2781
|
operation: (key) => b.stmt(b.call('_$_.set_text', id, key)),
|
|
2411
|
-
expression,
|
|
2782
|
+
expression: /** @type {AST.Expression} */ (expression),
|
|
2412
2783
|
identity: node.expression,
|
|
2413
2784
|
initial: b.literal(' '),
|
|
2414
2785
|
});
|
|
2415
|
-
if (metadata
|
|
2416
|
-
state.update.async = true;
|
|
2786
|
+
if (metadata?.await) {
|
|
2787
|
+
/** @type {NonNullable<TransformClientState['update']>} */ (state.update).async = true;
|
|
2417
2788
|
}
|
|
2418
2789
|
}
|
|
2419
2790
|
} else if (node.type === 'ForOfStatement') {
|
|
2420
2791
|
node.is_controlled = is_controlled;
|
|
2421
|
-
visit(node, {
|
|
2792
|
+
visit(node, {
|
|
2793
|
+
...state,
|
|
2794
|
+
flush_node: /** @type {TransformClientState['flush_node']} */ (flush_node),
|
|
2795
|
+
namespace: state.namespace,
|
|
2796
|
+
});
|
|
2422
2797
|
} else if (node.type === 'IfStatement') {
|
|
2423
2798
|
node.is_controlled = is_controlled;
|
|
2424
|
-
visit(node, {
|
|
2799
|
+
visit(node, {
|
|
2800
|
+
...state,
|
|
2801
|
+
flush_node: /** @type {TransformClientState['flush_node']} */ (flush_node),
|
|
2802
|
+
namespace: state.namespace,
|
|
2803
|
+
});
|
|
2425
2804
|
} else if (node.type === 'TryStatement') {
|
|
2426
2805
|
node.is_controlled = is_controlled;
|
|
2427
|
-
visit(node, {
|
|
2806
|
+
visit(node, {
|
|
2807
|
+
...state,
|
|
2808
|
+
flush_node: /** @type {TransformClientState['flush_node']} */ (flush_node),
|
|
2809
|
+
namespace: state.namespace,
|
|
2810
|
+
});
|
|
2428
2811
|
} else if (node.type === 'SwitchStatement') {
|
|
2429
2812
|
node.is_controlled = is_controlled;
|
|
2430
|
-
visit(node, {
|
|
2813
|
+
visit(node, {
|
|
2814
|
+
...state,
|
|
2815
|
+
flush_node: /** @type {TransformClientState['flush_node']} */ (flush_node),
|
|
2816
|
+
namespace: state.namespace,
|
|
2817
|
+
});
|
|
2431
2818
|
} else if (node.type === 'BreakStatement') {
|
|
2432
2819
|
// do nothing
|
|
2433
2820
|
} else {
|
|
@@ -2441,9 +2828,11 @@ function transform_children(children, context) {
|
|
|
2441
2828
|
}
|
|
2442
2829
|
|
|
2443
2830
|
if (context.state.inside_head) {
|
|
2444
|
-
const title_element =
|
|
2445
|
-
(
|
|
2446
|
-
node
|
|
2831
|
+
const title_element = /** @type {AST.Element} */ (
|
|
2832
|
+
children.find(
|
|
2833
|
+
(node) =>
|
|
2834
|
+
node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'title',
|
|
2835
|
+
)
|
|
2447
2836
|
);
|
|
2448
2837
|
|
|
2449
2838
|
if (title_element) {
|
|
@@ -2460,14 +2849,29 @@ function transform_children(children, context) {
|
|
|
2460
2849
|
} else if (template_namespace === 'mathml') {
|
|
2461
2850
|
flags |= TEMPLATE_MATHML_NAMESPACE;
|
|
2462
2851
|
}
|
|
2463
|
-
state.final
|
|
2852
|
+
state.final?.push(b.stmt(b.call('_$_.append', b.id('__anchor'), initial)));
|
|
2464
2853
|
state.hoisted.push(
|
|
2465
|
-
b.var(
|
|
2854
|
+
b.var(
|
|
2855
|
+
template_id,
|
|
2856
|
+
b.call(
|
|
2857
|
+
'_$_.template',
|
|
2858
|
+
join_template(
|
|
2859
|
+
/** @type {NonNullable<TransformClientState['template']>} */ (state.template),
|
|
2860
|
+
),
|
|
2861
|
+
b.literal(flags),
|
|
2862
|
+
),
|
|
2863
|
+
),
|
|
2466
2864
|
);
|
|
2467
2865
|
}
|
|
2468
2866
|
}
|
|
2469
2867
|
|
|
2868
|
+
/**
|
|
2869
|
+
* @param {AST.Node[]} body
|
|
2870
|
+
* @param {TransformClientContext} context
|
|
2871
|
+
* @returns {AST.Statement[]}
|
|
2872
|
+
*/
|
|
2470
2873
|
function transform_body(body, { visit, state }) {
|
|
2874
|
+
/** @type {TransformClientState} */
|
|
2471
2875
|
const body_state = {
|
|
2472
2876
|
...state,
|
|
2473
2877
|
template: [],
|
|
@@ -2480,18 +2884,29 @@ function transform_body(body, { visit, state }) {
|
|
|
2480
2884
|
inside_head: state.inside_head || false,
|
|
2481
2885
|
};
|
|
2482
2886
|
|
|
2483
|
-
transform_children(
|
|
2887
|
+
transform_children(
|
|
2888
|
+
body,
|
|
2889
|
+
/** @type {VisitorClientContext} */ ({ visit, state: body_state, root: true }),
|
|
2890
|
+
);
|
|
2484
2891
|
|
|
2485
|
-
if (body_state.update.length > 0) {
|
|
2486
|
-
if (state.to_ts) {
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2892
|
+
if (/** @type {NonNullable<TransformClientState['update']>} */ (body_state.update).length > 0) {
|
|
2893
|
+
if (!state.to_ts) {
|
|
2894
|
+
apply_updates(
|
|
2895
|
+
/** @type {NonNullable<TransformClientState['init']>} */ (body_state.init),
|
|
2896
|
+
/** @type {NonNullable<TransformClientState['update']>} */ (body_state.update),
|
|
2897
|
+
state,
|
|
2898
|
+
);
|
|
2491
2899
|
}
|
|
2900
|
+
|
|
2901
|
+
// NOTE: transform_children in `to_ts` mode does NOT add to body_state.update
|
|
2902
|
+
// So, we skip adding doing any actions with body_state.update
|
|
2492
2903
|
}
|
|
2493
2904
|
|
|
2494
|
-
return [
|
|
2905
|
+
return [
|
|
2906
|
+
.../** @type {NonNullable<TransformClientState['setup']>} */ (body_state.setup),
|
|
2907
|
+
.../** @type {AST.Statement[]} */ (body_state.init),
|
|
2908
|
+
.../** @type {NonNullable<TransformClientState['final']>} */ (body_state.final),
|
|
2909
|
+
];
|
|
2495
2910
|
}
|
|
2496
2911
|
|
|
2497
2912
|
/**
|
|
@@ -2499,12 +2914,16 @@ function transform_body(body, { visit, state }) {
|
|
|
2499
2914
|
* @returns {Object} TSX language handler with TypeScript return type support
|
|
2500
2915
|
*/
|
|
2501
2916
|
function create_tsx_with_typescript_support() {
|
|
2502
|
-
const base_tsx = tsx();
|
|
2917
|
+
const base_tsx = /** @type {ESRap.Visitors<AST.Node>} */ (tsx());
|
|
2503
2918
|
|
|
2504
2919
|
// Add custom TypeScript node handlers that aren't in tsx
|
|
2505
2920
|
|
|
2506
|
-
|
|
2507
|
-
|
|
2921
|
+
/**
|
|
2922
|
+
* Shared handler for function-like nodes to support component->function mapping
|
|
2923
|
+
* Creates source maps for 'function' keyword by passing node to context.write()
|
|
2924
|
+
* @param {AST.Function} node
|
|
2925
|
+
* @param {ESRap.Context} context
|
|
2926
|
+
*/
|
|
2508
2927
|
const handle_function = (node, context) => {
|
|
2509
2928
|
if (node.async) {
|
|
2510
2929
|
context.write('async ');
|
|
@@ -2516,12 +2935,15 @@ function create_tsx_with_typescript_support() {
|
|
|
2516
2935
|
if (node.generator) {
|
|
2517
2936
|
context.write('*');
|
|
2518
2937
|
}
|
|
2938
|
+
|
|
2939
|
+
const id = /** @type {AST.FunctionExpression | AST.FunctionDeclaration} */ (node).id;
|
|
2940
|
+
|
|
2519
2941
|
// FunctionDeclaration always has a space before id, FunctionExpression only if id exists
|
|
2520
|
-
if (node.type === 'FunctionDeclaration' ||
|
|
2942
|
+
if (node.type === 'FunctionDeclaration' || id) {
|
|
2521
2943
|
context.write(' ');
|
|
2522
2944
|
}
|
|
2523
|
-
if (
|
|
2524
|
-
context.visit(
|
|
2945
|
+
if (id) {
|
|
2946
|
+
context.visit(id);
|
|
2525
2947
|
}
|
|
2526
2948
|
if (node.typeParameters) {
|
|
2527
2949
|
context.visit(node.typeParameters);
|
|
@@ -2543,10 +2965,14 @@ function create_tsx_with_typescript_support() {
|
|
|
2543
2965
|
|
|
2544
2966
|
return {
|
|
2545
2967
|
...base_tsx,
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2968
|
+
/**
|
|
2969
|
+
* Custom handler for Property nodes to prevent method shorthand for components
|
|
2970
|
+
* When a component is transformed to a FunctionExpression, we want to preserve
|
|
2971
|
+
* the explicit syntax (key: function name() {}) instead of method shorthand (key() {})
|
|
2972
|
+
* This ensures proper source mapping for the 'function'/'component' keyword
|
|
2973
|
+
* @param {AST.Property} node
|
|
2974
|
+
* @param {ESRap.Context} context
|
|
2975
|
+
*/
|
|
2550
2976
|
Property(node, context) {
|
|
2551
2977
|
// Check if the value is a function that was originally a component
|
|
2552
2978
|
const isComponent =
|
|
@@ -2593,14 +3019,18 @@ function create_tsx_with_typescript_support() {
|
|
|
2593
3019
|
context.write(node.computed ? ']: ' : ': ');
|
|
2594
3020
|
context.visit(node.value);
|
|
2595
3021
|
} else {
|
|
2596
|
-
base_tsx.Property(node, context);
|
|
3022
|
+
base_tsx.Property?.(node, context);
|
|
2597
3023
|
}
|
|
2598
3024
|
} else {
|
|
2599
3025
|
// Use default handler for non-component properties
|
|
2600
|
-
base_tsx.Property(node, context);
|
|
3026
|
+
base_tsx.Property?.(node, context);
|
|
2601
3027
|
}
|
|
2602
3028
|
},
|
|
2603
|
-
|
|
3029
|
+
/**
|
|
3030
|
+
* Custom handler for JSXClosingElement to ensure closing tag brackets have source mappings
|
|
3031
|
+
* @param {ESTreeJSX.JSXClosingElement} node
|
|
3032
|
+
* @param {ESRap.Context} context
|
|
3033
|
+
*/
|
|
2604
3034
|
JSXClosingElement(node, context) {
|
|
2605
3035
|
// Set location for '<' then write '</'
|
|
2606
3036
|
if (node.loc) {
|
|
@@ -2620,14 +3050,18 @@ function create_tsx_with_typescript_support() {
|
|
|
2620
3050
|
context.write('>');
|
|
2621
3051
|
}
|
|
2622
3052
|
},
|
|
2623
|
-
|
|
2624
|
-
|
|
3053
|
+
/**
|
|
3054
|
+
* Custom handler for ArrayPattern to ensure typeAnnotation is visited
|
|
3055
|
+
* esrap's TypeScript handler doesn't visit typeAnnotation for ArrayPattern (only for ObjectPattern)
|
|
3056
|
+
* @param {AST.ArrayPattern} node
|
|
3057
|
+
* @param {ESRap.Context} context
|
|
3058
|
+
*/
|
|
2625
3059
|
ArrayPattern(node, context) {
|
|
2626
3060
|
context.write('[');
|
|
2627
3061
|
for (let i = 0; i < node.elements.length; i++) {
|
|
2628
3062
|
if (i > 0) context.write(', ');
|
|
2629
3063
|
if (node.elements[i]) {
|
|
2630
|
-
context.visit(node.elements[i]);
|
|
3064
|
+
context.visit(/** @type {AST.Pattern} */ (node.elements[i]));
|
|
2631
3065
|
}
|
|
2632
3066
|
}
|
|
2633
3067
|
context.write(']');
|
|
@@ -2636,19 +3070,31 @@ function create_tsx_with_typescript_support() {
|
|
|
2636
3070
|
context.visit(node.typeAnnotation);
|
|
2637
3071
|
}
|
|
2638
3072
|
},
|
|
2639
|
-
|
|
2640
|
-
|
|
3073
|
+
/**
|
|
3074
|
+
* Custom handler for FunctionDeclaration to support component->function mapping
|
|
3075
|
+
* Needed for volar mappings and intellisense on function or component keyword
|
|
3076
|
+
* @param {AST.FunctionDeclaration} node
|
|
3077
|
+
* @param {ESRap.Context} context
|
|
3078
|
+
*/
|
|
2641
3079
|
FunctionDeclaration(node, context) {
|
|
2642
3080
|
handle_function(node, context);
|
|
2643
3081
|
},
|
|
2644
|
-
|
|
2645
|
-
|
|
3082
|
+
/**
|
|
3083
|
+
* Custom handler for FunctionExpression to support component->function mapping
|
|
3084
|
+
* This is used for components transformed by the Component visitor
|
|
3085
|
+
* @param {AST.FunctionExpression} node
|
|
3086
|
+
* @param {ESRap.Context} context
|
|
3087
|
+
*/
|
|
2646
3088
|
FunctionExpression(node, context) {
|
|
2647
3089
|
handle_function(node, context);
|
|
2648
3090
|
},
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
3091
|
+
/**
|
|
3092
|
+
* Custom handler for ImportDeclaration to ensure 'import' keyword has source mapping
|
|
3093
|
+
* This creates a source map entry at the start of the import statement
|
|
3094
|
+
* Esrap's default handler writes 'import' without passing the node, so no source map entry
|
|
3095
|
+
* @param {AST.ImportDeclaration} node
|
|
3096
|
+
* @param {ESRap.Context} context
|
|
3097
|
+
*/
|
|
2652
3098
|
ImportDeclaration(node, context) {
|
|
2653
3099
|
// Write 'import' keyword with node location for source mapping
|
|
2654
3100
|
context.write('import', node);
|
|
@@ -2702,9 +3148,13 @@ function create_tsx_with_typescript_support() {
|
|
|
2702
3148
|
// Write source
|
|
2703
3149
|
context.visit(node.source);
|
|
2704
3150
|
},
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
3151
|
+
/**
|
|
3152
|
+
* Custom handler for JSXOpeningElement to ensure '<' and '>' have source mappings
|
|
3153
|
+
* Esrap's default handler only maps the tag name, not the brackets
|
|
3154
|
+
* This creates mappings for the brackets so auto-close can find the cursor position
|
|
3155
|
+
* @param {ESTreeJSX.JSXOpeningElement} node
|
|
3156
|
+
* @param {ESRap.Context} context
|
|
3157
|
+
*/
|
|
2708
3158
|
JSXOpeningElement(node, context) {
|
|
2709
3159
|
// Set location for '<'
|
|
2710
3160
|
if (node.loc) {
|
|
@@ -2732,13 +3182,21 @@ function create_tsx_with_typescript_support() {
|
|
|
2732
3182
|
context.write('>');
|
|
2733
3183
|
}
|
|
2734
3184
|
},
|
|
2735
|
-
|
|
3185
|
+
/**
|
|
3186
|
+
* Custom handler for TSParenthesizedType: (Type)
|
|
3187
|
+
* @param {AST.TSParenthesizedType} node
|
|
3188
|
+
* @param {ESRap.Context} context
|
|
3189
|
+
*/
|
|
2736
3190
|
TSParenthesizedType(node, context) {
|
|
2737
3191
|
context.write('(');
|
|
2738
|
-
context.visit(node.typeAnnotation);
|
|
3192
|
+
context.visit(/** @type {AST.TSTypeAnnotation} */ (node.typeAnnotation));
|
|
2739
3193
|
context.write(')');
|
|
2740
3194
|
},
|
|
2741
|
-
|
|
3195
|
+
/**
|
|
3196
|
+
* Custom handler for TSMappedType: { [K in keyof T]: T[K] }
|
|
3197
|
+
* @param {AST.TSMappedType} node
|
|
3198
|
+
* @param {ESRap.Context} context
|
|
3199
|
+
*/
|
|
2742
3200
|
TSMappedType(node, context) {
|
|
2743
3201
|
context.write('{ ');
|
|
2744
3202
|
if (node.readonly) {
|
|
@@ -2770,8 +3228,12 @@ function create_tsx_with_typescript_support() {
|
|
|
2770
3228
|
}
|
|
2771
3229
|
context.write(' }');
|
|
2772
3230
|
},
|
|
2773
|
-
|
|
2774
|
-
|
|
3231
|
+
/**
|
|
3232
|
+
* Custom handler for TSTypeParameter: K in T (for mapped types)
|
|
3233
|
+
* acorn-ts has a bug where `in` is printed as `extends`, so we override it here
|
|
3234
|
+
* @param {AST.TSTypeParameter} node
|
|
3235
|
+
* @param {ESRap.Context} context
|
|
3236
|
+
*/
|
|
2775
3237
|
TSTypeParameter(node, context) {
|
|
2776
3238
|
// For mapped types, the name is just a string, not an Identifier node
|
|
2777
3239
|
// Pass the node as second parameter to context.write() to create source map entry
|
|
@@ -2789,7 +3251,11 @@ function create_tsx_with_typescript_support() {
|
|
|
2789
3251
|
context.visit(node.default);
|
|
2790
3252
|
}
|
|
2791
3253
|
},
|
|
2792
|
-
|
|
3254
|
+
/**
|
|
3255
|
+
* Override the ArrowFunctionExpression handler to support TypeScript return types
|
|
3256
|
+
* @param {AST.ArrowFunctionExpression} node
|
|
3257
|
+
* @param {ESRap.Context} context
|
|
3258
|
+
*/
|
|
2793
3259
|
ArrowFunctionExpression(node, context) {
|
|
2794
3260
|
if (node.async) context.write('async ');
|
|
2795
3261
|
|
|
@@ -2821,7 +3287,11 @@ function create_tsx_with_typescript_support() {
|
|
|
2821
3287
|
context.visit(node.body);
|
|
2822
3288
|
}
|
|
2823
3289
|
},
|
|
2824
|
-
|
|
3290
|
+
/**
|
|
3291
|
+
* Custom handler for TryStatement to support Ripple's pending block
|
|
3292
|
+
* @param {AST.TryStatement} node
|
|
3293
|
+
* @param {ESRap.Context} context
|
|
3294
|
+
*/
|
|
2825
3295
|
TryStatement(node, context) {
|
|
2826
3296
|
context.write('try ');
|
|
2827
3297
|
context.visit(node.block);
|
|
@@ -2830,8 +3300,10 @@ function create_tsx_with_typescript_support() {
|
|
|
2830
3300
|
// Output the pending block with source mapping for the 'pending' keyword
|
|
2831
3301
|
context.write(' ');
|
|
2832
3302
|
context.location(
|
|
2833
|
-
|
|
2834
|
-
node.pending.loc.start.
|
|
3303
|
+
/** @type {AST.SourceLocation} */
|
|
3304
|
+
(node.pending.loc).start.line,
|
|
3305
|
+
/** @type {AST.SourceLocation} */
|
|
3306
|
+
(node.pending.loc).start.column - 'pending '.length,
|
|
2835
3307
|
);
|
|
2836
3308
|
context.write('pending ');
|
|
2837
3309
|
context.visit(node.pending);
|
|
@@ -2860,10 +3332,10 @@ function create_tsx_with_typescript_support() {
|
|
|
2860
3332
|
* Transform Ripple AST to JavaScript/TypeScript
|
|
2861
3333
|
* @param {string} filename - Source filename
|
|
2862
3334
|
* @param {string} source - Original source code
|
|
2863
|
-
* @param {
|
|
3335
|
+
* @param {AnalysisResult} analysis - Analysis result
|
|
2864
3336
|
* @param {boolean} to_ts - Whether to generate TypeScript output
|
|
2865
3337
|
* @param {boolean} minify_css - Whether to minify CSS output
|
|
2866
|
-
* @returns {{ ast:
|
|
3338
|
+
* @returns {{ ast: AST.Program, js: { code: string, map: SourceMapMappings, post_processing_changes?: PostProcessingChanges, line_offsets?: LineOffsets }, css: string }}
|
|
2867
3339
|
*/
|
|
2868
3340
|
export function transform_client(filename, source, analysis, to_ts, minify_css) {
|
|
2869
3341
|
/**
|
|
@@ -2885,13 +3357,17 @@ export function transform_client(filename, source, analysis, to_ts, minify_css)
|
|
|
2885
3357
|
) {
|
|
2886
3358
|
for (const spec of stmt.specifiers || []) {
|
|
2887
3359
|
if (spec.type === 'ImportSpecifier' && spec.imported && spec.local) {
|
|
2888
|
-
ripple_user_imports.set(
|
|
3360
|
+
ripple_user_imports.set(
|
|
3361
|
+
/** @type {AST.Identifier} */ (spec.imported).name,
|
|
3362
|
+
spec.local.name,
|
|
3363
|
+
);
|
|
2889
3364
|
}
|
|
2890
3365
|
}
|
|
2891
3366
|
}
|
|
2892
3367
|
}
|
|
2893
3368
|
}
|
|
2894
3369
|
|
|
3370
|
+
/** @type {TransformClientState} */
|
|
2895
3371
|
const state = {
|
|
2896
3372
|
imports: new Set(),
|
|
2897
3373
|
ripple_user_imports,
|
|
@@ -2900,6 +3376,7 @@ export function transform_client(filename, source, analysis, to_ts, minify_css)
|
|
|
2900
3376
|
hoisted: [],
|
|
2901
3377
|
setup: null,
|
|
2902
3378
|
init: null,
|
|
3379
|
+
inside_head: false,
|
|
2903
3380
|
update: null,
|
|
2904
3381
|
final: null,
|
|
2905
3382
|
flush_node: null,
|
|
@@ -2908,11 +3385,11 @@ export function transform_client(filename, source, analysis, to_ts, minify_css)
|
|
|
2908
3385
|
stylesheets: [],
|
|
2909
3386
|
to_ts,
|
|
2910
3387
|
filename,
|
|
3388
|
+
namespace: 'html',
|
|
3389
|
+
metadata: {},
|
|
2911
3390
|
};
|
|
2912
3391
|
|
|
2913
|
-
const program = /** @type {Program} */ (
|
|
2914
|
-
walk(/** @type {Node} */ (analysis.ast), { ...state, namespace: 'html' }, visitors)
|
|
2915
|
-
);
|
|
3392
|
+
const program = /** @type {AST.Program} */ (walk(analysis.ast, { ...state }, visitors));
|
|
2916
3393
|
|
|
2917
3394
|
for (const hoisted of state.hoisted) {
|
|
2918
3395
|
program.body.unshift(hoisted);
|