ripple 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -380
- package/package.json +5 -2
- package/src/compiler/phases/1-parse/index.js +13 -4
- package/src/compiler/phases/2-analyze/index.js +7 -1
- package/src/compiler/phases/2-analyze/prune.js +232 -109
- package/src/compiler/phases/3-transform/index.js +44 -14
- package/src/compiler/phases/3-transform/segments.js +12 -9
- package/src/runtime/internal/client/blocks.js +27 -5
- package/src/runtime/internal/client/constants.js +1 -0
- package/src/runtime/internal/client/index.js +3 -4
- package/src/runtime/internal/client/render.js +32 -3
- package/src/runtime/internal/client/runtime.js +6 -1
- package/src/runtime/internal/client/utils.js +1 -0
- package/src/utils/builders.js +11 -0
- package/tests/__snapshots__/basic.test.ripple.snap +66 -0
- package/tests/basic.test.ripple +273 -0
- package/tests/use.test.ripple +32 -0
|
@@ -2,6 +2,41 @@ import { walk } from 'zimmerframe';
|
|
|
2
2
|
|
|
3
3
|
const seen = new Set();
|
|
4
4
|
const regex_backslash_and_following_character = /\\(.)/g;
|
|
5
|
+
const FORWARD = 0;
|
|
6
|
+
const BACKWARD = 1;
|
|
7
|
+
|
|
8
|
+
// CSS selector constants
|
|
9
|
+
const descendant_combinator = { name: ' ', type: 'Combinator' };
|
|
10
|
+
const nesting_selector = {
|
|
11
|
+
type: 'NestingSelector',
|
|
12
|
+
name: '&',
|
|
13
|
+
selectors: [],
|
|
14
|
+
metadata: { scoped: false }
|
|
15
|
+
};
|
|
16
|
+
const any_selector = {
|
|
17
|
+
type: 'RelativeSelector',
|
|
18
|
+
selectors: [{ type: 'TypeSelector', name: '*' }],
|
|
19
|
+
combinator: null,
|
|
20
|
+
metadata: { scoped: false }
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Whitelist for attribute selectors on specific elements
|
|
24
|
+
const whitelist_attribute_selector = new Map([
|
|
25
|
+
['details', ['open']],
|
|
26
|
+
['dialog', ['open']],
|
|
27
|
+
['form', ['novalidate']],
|
|
28
|
+
['iframe', ['allow', 'allowfullscreen', 'allowpaymentrequest', 'loading', 'referrerpolicy']],
|
|
29
|
+
['img', ['loading']],
|
|
30
|
+
['input', ['accept', 'autocomplete', 'capture', 'checked', 'disabled', 'max', 'maxlength', 'min', 'minlength', 'multiple', 'pattern', 'placeholder', 'readonly', 'required', 'size', 'step']],
|
|
31
|
+
['object', ['typemustmatch']],
|
|
32
|
+
['ol', ['reversed', 'start', 'type']],
|
|
33
|
+
['optgroup', ['disabled']],
|
|
34
|
+
['option', ['disabled', 'selected']],
|
|
35
|
+
['script', ['async', 'defer', 'nomodule', 'type']],
|
|
36
|
+
['select', ['disabled', 'multiple', 'required', 'size']],
|
|
37
|
+
['textarea', ['autocomplete', 'disabled', 'maxlength', 'minlength', 'placeholder', 'readonly', 'required', 'rows', 'wrap']],
|
|
38
|
+
['video', ['autoplay', 'controls', 'loop', 'muted', 'playsinline']]
|
|
39
|
+
]);
|
|
5
40
|
|
|
6
41
|
function get_relative_selectors(node) {
|
|
7
42
|
const selectors = truncate(node);
|
|
@@ -62,14 +97,14 @@ function truncate(node) {
|
|
|
62
97
|
});
|
|
63
98
|
}
|
|
64
99
|
|
|
65
|
-
function apply_selector(relative_selectors, rule, element) {
|
|
66
|
-
const
|
|
67
|
-
const relative_selector =
|
|
100
|
+
function apply_selector(relative_selectors, rule, element, direction) {
|
|
101
|
+
const rest_selectors = relative_selectors.slice();
|
|
102
|
+
const relative_selector = direction === FORWARD ? rest_selectors.shift() : rest_selectors.pop();
|
|
68
103
|
|
|
69
104
|
const matched =
|
|
70
105
|
!!relative_selector &&
|
|
71
|
-
relative_selector_might_apply_to_node(relative_selector, rule, element) &&
|
|
72
|
-
apply_combinator(relative_selector,
|
|
106
|
+
relative_selector_might_apply_to_node(relative_selector, rule, element, direction) &&
|
|
107
|
+
apply_combinator(relative_selector, rest_selectors, rule, element, direction);
|
|
73
108
|
|
|
74
109
|
if (matched) {
|
|
75
110
|
if (!is_outer_global(relative_selector)) {
|
|
@@ -82,55 +117,163 @@ function apply_selector(relative_selectors, rule, element) {
|
|
|
82
117
|
return matched;
|
|
83
118
|
}
|
|
84
119
|
|
|
85
|
-
function
|
|
86
|
-
|
|
120
|
+
function get_ancestor_elements(node, adjacent_only, seen = new Set()) {
|
|
121
|
+
const ancestors = [];
|
|
87
122
|
|
|
88
|
-
const
|
|
123
|
+
const path = node.metadata.path;
|
|
124
|
+
let i = path.length;
|
|
89
125
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
case '>': {
|
|
93
|
-
let parent_matched = false;
|
|
126
|
+
while (i--) {
|
|
127
|
+
const parent = path[i];
|
|
94
128
|
|
|
95
|
-
|
|
96
|
-
|
|
129
|
+
if (parent.type === 'Element') {
|
|
130
|
+
ancestors.push(parent);
|
|
131
|
+
if (adjacent_only) {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
97
136
|
|
|
98
|
-
|
|
99
|
-
|
|
137
|
+
return ancestors;
|
|
138
|
+
}
|
|
100
139
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
140
|
+
function get_descendant_elements(node, adjacent_only) {
|
|
141
|
+
const descendants = [];
|
|
142
|
+
|
|
143
|
+
function visit(current_node, depth = 0) {
|
|
144
|
+
if (current_node.type === 'Element' && current_node !== node) {
|
|
145
|
+
descendants.push(current_node);
|
|
146
|
+
if (adjacent_only) return; // Only direct children for '>' combinator
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Visit children based on Ripple's AST structure
|
|
150
|
+
if (current_node.children) {
|
|
151
|
+
for (const child of current_node.children) {
|
|
152
|
+
visit(child, depth + 1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (current_node.body) {
|
|
157
|
+
for (const child of current_node.body) {
|
|
158
|
+
visit(child, depth + 1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// For template nodes and text interpolations
|
|
163
|
+
if (current_node.expression && typeof current_node.expression === 'object') {
|
|
164
|
+
visit(current_node.expression, depth + 1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Start from node's children
|
|
169
|
+
if (node.children) {
|
|
170
|
+
for (const child of node.children) {
|
|
171
|
+
visit(child);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (node.body) {
|
|
176
|
+
for (const child of node.body) {
|
|
177
|
+
visit(child);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
105
180
|
|
|
106
|
-
|
|
181
|
+
return descendants;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function get_possible_element_siblings(node, direction, adjacent_only) {
|
|
185
|
+
const siblings = new Map();
|
|
186
|
+
const parent = get_element_parent(node);
|
|
187
|
+
|
|
188
|
+
if (!parent) {
|
|
189
|
+
return siblings;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Get the container that holds the siblings
|
|
193
|
+
const container = parent.children || parent.body || [];
|
|
194
|
+
const node_index = container.indexOf(node);
|
|
195
|
+
|
|
196
|
+
if (node_index === -1) return siblings;
|
|
197
|
+
|
|
198
|
+
// Determine which siblings to check based on direction
|
|
199
|
+
let start, end, step;
|
|
200
|
+
if (direction === FORWARD) {
|
|
201
|
+
start = node_index + 1;
|
|
202
|
+
end = container.length;
|
|
203
|
+
step = 1;
|
|
204
|
+
} else {
|
|
205
|
+
start = node_index - 1;
|
|
206
|
+
end = -1;
|
|
207
|
+
step = -1;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Collect siblings
|
|
211
|
+
for (let i = start; i !== end; i += step) {
|
|
212
|
+
const sibling = container[i];
|
|
213
|
+
|
|
214
|
+
if (sibling.type === 'Element' || sibling.type === 'Component') {
|
|
215
|
+
siblings.set(sibling, true);
|
|
216
|
+
if (adjacent_only) break; // Only immediate sibling for '+' combinator
|
|
217
|
+
}
|
|
218
|
+
// Stop at non-whitespace text nodes for adjacent selectors
|
|
219
|
+
else if (adjacent_only && sibling.type === 'Text' && sibling.value?.trim()) {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return siblings;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function apply_combinator(relative_selector, rest_selectors, rule, node, direction) {
|
|
228
|
+
const combinator =
|
|
229
|
+
direction == FORWARD ? rest_selectors[0]?.combinator : relative_selector.combinator;
|
|
230
|
+
if (!combinator) return true;
|
|
231
|
+
|
|
232
|
+
switch (combinator.name) {
|
|
233
|
+
case ' ':
|
|
234
|
+
case '>': {
|
|
235
|
+
const is_adjacent = combinator.name === '>';
|
|
236
|
+
const parents =
|
|
237
|
+
direction === FORWARD
|
|
238
|
+
? get_descendant_elements(node, is_adjacent)
|
|
239
|
+
: get_ancestor_elements(node, is_adjacent);
|
|
240
|
+
let parent_matched = false;
|
|
241
|
+
|
|
242
|
+
for (const parent of parents) {
|
|
243
|
+
if (apply_selector(rest_selectors, rule, parent, direction)) {
|
|
244
|
+
parent_matched = true;
|
|
107
245
|
}
|
|
108
246
|
}
|
|
109
247
|
|
|
110
|
-
return
|
|
248
|
+
return (
|
|
249
|
+
parent_matched ||
|
|
250
|
+
(direction === BACKWARD &&
|
|
251
|
+
(!is_adjacent || parents.length === 0) &&
|
|
252
|
+
rest_selectors.every((selector) => is_global(selector, rule)))
|
|
253
|
+
);
|
|
111
254
|
}
|
|
112
255
|
|
|
113
256
|
case '+':
|
|
114
257
|
case '~': {
|
|
115
|
-
const siblings = get_possible_element_siblings(node, name === '+');
|
|
258
|
+
const siblings = get_possible_element_siblings(node, direction, combinator.name === '+');
|
|
116
259
|
|
|
117
260
|
let sibling_matched = false;
|
|
118
261
|
|
|
119
262
|
for (const possible_sibling of siblings.keys()) {
|
|
120
|
-
if (possible_sibling.type === '
|
|
121
|
-
|
|
122
|
-
if (parent_selectors.length === 1 && parent_selectors[0].metadata.is_global) {
|
|
263
|
+
if (possible_sibling.type === 'Component') {
|
|
264
|
+
if (rest_selectors.length === 1 && rest_selectors[0].metadata.is_global) {
|
|
123
265
|
sibling_matched = true;
|
|
124
266
|
}
|
|
125
|
-
} else if (apply_selector(
|
|
267
|
+
} else if (apply_selector(rest_selectors, rule, possible_sibling, direction)) {
|
|
126
268
|
sibling_matched = true;
|
|
127
269
|
}
|
|
128
270
|
}
|
|
129
271
|
|
|
130
272
|
return (
|
|
131
273
|
sibling_matched ||
|
|
132
|
-
(
|
|
133
|
-
|
|
274
|
+
(direction === BACKWARD &&
|
|
275
|
+
get_element_parent(node) === null &&
|
|
276
|
+
rest_selectors.every((selector) => is_global(selector, rule)))
|
|
134
277
|
);
|
|
135
278
|
}
|
|
136
279
|
|
|
@@ -141,13 +284,18 @@ function apply_combinator(relative_selector, parent_selectors, rule, node) {
|
|
|
141
284
|
}
|
|
142
285
|
|
|
143
286
|
function get_element_parent(node) {
|
|
287
|
+
// Check if metadata and path exist
|
|
288
|
+
if (!node.metadata || !node.metadata.path) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
144
292
|
let path = node.metadata.path;
|
|
145
293
|
let i = path.length;
|
|
146
294
|
|
|
147
295
|
while (i--) {
|
|
148
296
|
const parent = path[i];
|
|
149
297
|
|
|
150
|
-
if (parent.type === '
|
|
298
|
+
if (parent.type === 'Element') {
|
|
151
299
|
return parent;
|
|
152
300
|
}
|
|
153
301
|
}
|
|
@@ -252,7 +400,7 @@ function is_outer_global(relative_selector) {
|
|
|
252
400
|
);
|
|
253
401
|
}
|
|
254
402
|
|
|
255
|
-
function relative_selector_might_apply_to_node(relative_selector, rule, element) {
|
|
403
|
+
function relative_selector_might_apply_to_node(relative_selector, rule, element, direction) {
|
|
256
404
|
// Sort :has(...) selectors in one bucket and everything else into another
|
|
257
405
|
const has_selectors = [];
|
|
258
406
|
const other_selectors = [];
|
|
@@ -268,13 +416,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
268
416
|
// If we're called recursively from a :has(...) selector, we're on the way of checking if the other selectors match.
|
|
269
417
|
// In that case ignore this check (because we just came from this) to avoid an infinite loop.
|
|
270
418
|
if (has_selectors.length > 0) {
|
|
271
|
-
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
|
|
272
|
-
const child_elements = [];
|
|
273
|
-
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
|
|
274
|
-
const descendant_elements = [];
|
|
275
|
-
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
|
|
276
|
-
let sibling_elements; // do them lazy because it's rarely used and expensive to calculate
|
|
277
|
-
|
|
278
419
|
// If this is a :has inside a global selector, we gotta include the element itself, too,
|
|
279
420
|
// because the global selector might be for an element that's outside the component,
|
|
280
421
|
// e.g. :root:has(.scoped), :global(.foo):has(.scoped), or :root { &:has(.scoped) {} }
|
|
@@ -290,37 +431,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
290
431
|
)
|
|
291
432
|
)
|
|
292
433
|
);
|
|
293
|
-
if (include_self) {
|
|
294
|
-
child_elements.push(element);
|
|
295
|
-
descendant_elements.push(element);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* @param {Compiler.AST.SvelteNode} node
|
|
300
|
-
* @param {{ is_child: boolean }} state
|
|
301
|
-
*/
|
|
302
|
-
function walk_children(node, state) {
|
|
303
|
-
walk(node, state, {
|
|
304
|
-
_(node, context) {
|
|
305
|
-
if (node.type === 'Element') {
|
|
306
|
-
descendant_elements.push(node);
|
|
307
|
-
|
|
308
|
-
if (context.state.is_child) {
|
|
309
|
-
child_elements.push(node);
|
|
310
|
-
context.state.is_child = false;
|
|
311
|
-
context.next();
|
|
312
|
-
context.state.is_child = true;
|
|
313
|
-
} else {
|
|
314
|
-
context.next();
|
|
315
|
-
}
|
|
316
|
-
} else {
|
|
317
|
-
context.next();
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
walk_children(element.fragment, { is_child: true });
|
|
324
434
|
|
|
325
435
|
// :has(...) is special in that it means "look downwards in the CSS tree". Since our matching algorithm goes
|
|
326
436
|
// upwards and back-to-front, we need to first check the selectors inside :has(...), then check the rest of the
|
|
@@ -331,37 +441,34 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
331
441
|
let matched = false;
|
|
332
442
|
|
|
333
443
|
for (const complex_selector of complex_selectors) {
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
...selectors[0],
|
|
341
|
-
combinator: null
|
|
342
|
-
};
|
|
444
|
+
const [first, ...rest] = truncate(complex_selector);
|
|
445
|
+
// if it was just a :global(...)
|
|
446
|
+
if (!first) {
|
|
447
|
+
complex_selector.metadata.used = true;
|
|
448
|
+
matched = true;
|
|
449
|
+
continue;
|
|
343
450
|
}
|
|
344
451
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
?
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
let selector_matched = false;
|
|
353
|
-
|
|
354
|
-
// Iterate over all descendant elements and check if the selector inside :has matches
|
|
355
|
-
for (const element of descendants) {
|
|
356
|
-
if (
|
|
357
|
-
selectors.length === 0 /* is :global(...) */ ||
|
|
358
|
-
(element.metadata.scoped && selector_matched) ||
|
|
359
|
-
apply_selector(selectors, rule, element)
|
|
360
|
-
) {
|
|
452
|
+
if (include_self) {
|
|
453
|
+
const selector_including_self = [
|
|
454
|
+
first.combinator ? { ...first, combinator: null } : first,
|
|
455
|
+
...rest
|
|
456
|
+
];
|
|
457
|
+
if (apply_selector(selector_including_self, rule, element, FORWARD)) {
|
|
361
458
|
complex_selector.metadata.used = true;
|
|
362
|
-
|
|
459
|
+
matched = true;
|
|
363
460
|
}
|
|
364
461
|
}
|
|
462
|
+
|
|
463
|
+
const selector_excluding_self = [
|
|
464
|
+
any_selector,
|
|
465
|
+
first.combinator ? first : { ...first, combinator: descendant_combinator },
|
|
466
|
+
...rest
|
|
467
|
+
];
|
|
468
|
+
if (apply_selector(selector_excluding_self, rule, element, FORWARD)) {
|
|
469
|
+
complex_selector.metadata.used = true;
|
|
470
|
+
matched = true;
|
|
471
|
+
}
|
|
365
472
|
}
|
|
366
473
|
|
|
367
474
|
if (!matched) {
|
|
@@ -386,7 +493,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
386
493
|
) {
|
|
387
494
|
const args = selector.args;
|
|
388
495
|
const complex_selector = args.children[0];
|
|
389
|
-
return apply_selector(complex_selector.children, rule, element);
|
|
496
|
+
return apply_selector(complex_selector.children, rule, element, BACKWARD);
|
|
390
497
|
}
|
|
391
498
|
|
|
392
499
|
// We came across a :global, everything beyond it is global and therefore a potential match
|
|
@@ -413,7 +520,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
413
520
|
selector.metadata.scoped = true;
|
|
414
521
|
}
|
|
415
522
|
|
|
416
|
-
/** @type {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null} */
|
|
417
523
|
let el = element;
|
|
418
524
|
while (el) {
|
|
419
525
|
el.metadata.scoped = true;
|
|
@@ -435,7 +541,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
435
541
|
if (is_global) {
|
|
436
542
|
complex_selector.metadata.used = true;
|
|
437
543
|
matched = true;
|
|
438
|
-
} else if (apply_selector(relative, rule, element)) {
|
|
544
|
+
} else if (apply_selector(relative, rule, element, BACKWARD)) {
|
|
439
545
|
complex_selector.metadata.used = true;
|
|
440
546
|
matched = true;
|
|
441
547
|
} else if (complex_selector.children.length > 1 && (name == 'is' || name == 'where')) {
|
|
@@ -465,7 +571,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
465
571
|
case 'AttributeSelector': {
|
|
466
572
|
const whitelisted = whitelist_attribute_selector.get(element.id.name.toLowerCase());
|
|
467
573
|
if (
|
|
468
|
-
!whitelisted?.includes(selector.
|
|
574
|
+
!whitelisted?.includes(selector.name.toLowerCase()) &&
|
|
469
575
|
!attribute_matches(
|
|
470
576
|
element,
|
|
471
577
|
selector.name,
|
|
@@ -480,12 +586,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
480
586
|
}
|
|
481
587
|
|
|
482
588
|
case 'ClassSelector': {
|
|
483
|
-
if (
|
|
484
|
-
!attribute_matches(element, 'class', name, '~=', false) &&
|
|
485
|
-
!element.attributes.some(
|
|
486
|
-
(attribute) => attribute.type === 'ClassDirective' && attribute.name === name
|
|
487
|
-
)
|
|
488
|
-
) {
|
|
589
|
+
if (!attribute_matches(element, 'class', name, '~=', false)) {
|
|
489
590
|
return false;
|
|
490
591
|
}
|
|
491
592
|
|
|
@@ -502,9 +603,9 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
502
603
|
|
|
503
604
|
case 'TypeSelector': {
|
|
504
605
|
if (
|
|
606
|
+
element.id.type === 'Identifier' &&
|
|
505
607
|
element.id.name.toLowerCase() !== name.toLowerCase() &&
|
|
506
|
-
name !== '*'
|
|
507
|
-
element.id.name[0].toLowerCase() === element.id.name[0]
|
|
608
|
+
name !== '*'
|
|
508
609
|
) {
|
|
509
610
|
return false;
|
|
510
611
|
}
|
|
@@ -519,7 +620,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
519
620
|
|
|
520
621
|
for (const complex_selector of parent.prelude.children) {
|
|
521
622
|
if (
|
|
522
|
-
apply_selector(get_relative_selectors(complex_selector), parent, element) ||
|
|
623
|
+
apply_selector(get_relative_selectors(complex_selector), parent, element, direction) ||
|
|
523
624
|
complex_selector.children.every((s) => is_global(s, parent))
|
|
524
625
|
) {
|
|
525
626
|
complex_selector.metadata.used = true;
|
|
@@ -540,6 +641,27 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
|
|
|
540
641
|
return true;
|
|
541
642
|
}
|
|
542
643
|
|
|
644
|
+
// Utility functions for parsing CSS values
|
|
645
|
+
function unquote(str) {
|
|
646
|
+
if ((str[0] === '"' && str[str.length - 1] === '"') ||
|
|
647
|
+
(str[0] === "'" && str[str.length - 1] === "'")) {
|
|
648
|
+
return str.slice(1, -1);
|
|
649
|
+
}
|
|
650
|
+
return str;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function get_parent_rules(rule) {
|
|
654
|
+
const rules = [rule];
|
|
655
|
+
let current = rule;
|
|
656
|
+
|
|
657
|
+
while (current.metadata.parent_rule) {
|
|
658
|
+
current = current.metadata.parent_rule;
|
|
659
|
+
rules.unshift(current);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return rules;
|
|
663
|
+
}
|
|
664
|
+
|
|
543
665
|
export function prune_css(css, element) {
|
|
544
666
|
walk(css, null, {
|
|
545
667
|
Rule(node, context) {
|
|
@@ -558,7 +680,8 @@ export function prune_css(css, element) {
|
|
|
558
680
|
apply_selector(
|
|
559
681
|
selectors,
|
|
560
682
|
/** @type {Compiler.AST.CSS.Rule} */ (node.metadata.rule),
|
|
561
|
-
element
|
|
683
|
+
element,
|
|
684
|
+
BACKWARD
|
|
562
685
|
)
|
|
563
686
|
) {
|
|
564
687
|
node.metadata.used = true;
|
|
@@ -32,6 +32,12 @@ function visit_function(node, context) {
|
|
|
32
32
|
const metadata = node.metadata;
|
|
33
33
|
const state = context.state;
|
|
34
34
|
|
|
35
|
+
delete node.returnType;
|
|
36
|
+
|
|
37
|
+
for (const param of node.params) {
|
|
38
|
+
delete param.typeAnnotation;
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
if (metadata?.hoisted === true) {
|
|
36
42
|
const params = build_hoisted_params(node, context);
|
|
37
43
|
|
|
@@ -497,6 +503,9 @@ const visitors = {
|
|
|
497
503
|
}
|
|
498
504
|
} else if (attr.type === 'SpreadAttribute') {
|
|
499
505
|
spread_attributes.push(b.spread(b.call('$.spread_object', visit(attr.argument, state))));
|
|
506
|
+
} else if (attr.type === 'UseAttribute') {
|
|
507
|
+
const id = state.flush_node();
|
|
508
|
+
state.init.push(b.stmt(b.call('$.use', id, b.thunk(visit(attr.argument, state)))));
|
|
500
509
|
}
|
|
501
510
|
}
|
|
502
511
|
|
|
@@ -581,6 +590,8 @@ const visitors = {
|
|
|
581
590
|
)
|
|
582
591
|
)
|
|
583
592
|
);
|
|
593
|
+
} else if (attr.type === 'UseAttribute') {
|
|
594
|
+
props.push(b.prop('init', b.call('$.use_prop'), visit(attr.argument, state), true));
|
|
584
595
|
} else {
|
|
585
596
|
throw new Error('TODO');
|
|
586
597
|
}
|
|
@@ -1184,23 +1195,42 @@ function transform_ts_child(node, context) {
|
|
|
1184
1195
|
const type = node.id.name;
|
|
1185
1196
|
const children = [];
|
|
1186
1197
|
let has_children_props = false;
|
|
1198
|
+
|
|
1199
|
+
// Filter out UseAttributes and handle them separately
|
|
1200
|
+
const use_attributes = [];
|
|
1201
|
+
const attributes = node.attributes
|
|
1202
|
+
.filter((attr) => {
|
|
1203
|
+
if (attr.type === 'UseAttribute') {
|
|
1204
|
+
use_attributes.push(attr);
|
|
1205
|
+
return false; // Filter out from JSX attributes
|
|
1206
|
+
}
|
|
1207
|
+
return true;
|
|
1208
|
+
})
|
|
1209
|
+
.map((attr) => {
|
|
1210
|
+
if (attr.type === 'Attribute') {
|
|
1211
|
+
const metadata = { await: false };
|
|
1212
|
+
const name = visit(attr.name, { ...state, metadata });
|
|
1213
|
+
const value = visit(attr.value, { ...state, metadata });
|
|
1214
|
+
const jsx_name = b.jsx_id(name.name);
|
|
1215
|
+
if (name.name === '$children') {
|
|
1216
|
+
has_children_props = true;
|
|
1217
|
+
}
|
|
1218
|
+
jsx_name.loc = name.loc;
|
|
1187
1219
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const jsx_name = b.jsx_id(name.name);
|
|
1194
|
-
if (name.name === '$children') {
|
|
1195
|
-
has_children_props = true;
|
|
1220
|
+
return b.jsx_attribute(jsx_name, b.jsx_expression_container(value));
|
|
1221
|
+
} else if (attr.type === 'SpreadAttribute') {
|
|
1222
|
+
const metadata = { await: false };
|
|
1223
|
+
const argument = visit(attr.argument, { ...state, metadata });
|
|
1224
|
+
return b.jsx_spread_attribute(argument);
|
|
1196
1225
|
}
|
|
1197
|
-
|
|
1226
|
+
});
|
|
1198
1227
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1228
|
+
// Add UseAttribute references separately for sourcemap purposes
|
|
1229
|
+
for (const use_attr of use_attributes) {
|
|
1230
|
+
const metadata = { await: false };
|
|
1231
|
+
const argument = visit(use_attr.argument, { ...state, metadata });
|
|
1232
|
+
state.init.push(b.stmt(argument));
|
|
1233
|
+
}
|
|
1204
1234
|
|
|
1205
1235
|
if (!node.selfClosing && !has_children_props && node.children.length > 0) {
|
|
1206
1236
|
const is_dom_element = type[0].toLowerCase() === type[0] && type[0] !== '$';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { decode } from '@jridgewell/sourcemap-codec';
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const mapping_data = {
|
|
4
4
|
verification: true,
|
|
5
5
|
completion: true,
|
|
6
6
|
semantic: true,
|
|
@@ -8,12 +8,11 @@ export const defaultMappingData = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Convert esrap SourceMap to Volar mappings
|
|
12
|
-
*
|
|
13
|
-
* @param {
|
|
14
|
-
* @param {string}
|
|
15
|
-
* @
|
|
16
|
-
* @returns {object} Object with code and mappings for Volar
|
|
11
|
+
* Convert esrap SourceMap to Volar mappings
|
|
12
|
+
* @param {object} source_map
|
|
13
|
+
* @param {string} source
|
|
14
|
+
* @param {string} generated_code
|
|
15
|
+
* @returns {object}
|
|
17
16
|
*/
|
|
18
17
|
export function convert_source_map_to_mappings(source_map, source, generated_code) {
|
|
19
18
|
const mappings = [];
|
|
@@ -57,6 +56,11 @@ export function convert_source_map_to_mappings(source_map, source, generated_cod
|
|
|
57
56
|
const generated_content = generated_code.substring(current_generated_offset, current_generated_offset + segment_length);
|
|
58
57
|
const source_content = source.substring(source_offset, source_offset + segment_length);
|
|
59
58
|
|
|
59
|
+
// Skip mappings for UseAttribute syntax to avoid overlapping sourcemaps
|
|
60
|
+
if (source_content.includes('{@use ') || source_content.match(/\{\s*@use\s+/)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
// Fix for $children mapping: when generated content is "$children",
|
|
61
65
|
// it should only map to the component name in the source, not include attributes
|
|
62
66
|
if (generated_content === '$children') {
|
|
@@ -68,12 +72,11 @@ export function convert_source_map_to_mappings(source_map, source, generated_cod
|
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
71
|
-
// Create Volar mapping with default mapping data
|
|
72
75
|
mappings.push({
|
|
73
76
|
sourceOffsets: [source_offset],
|
|
74
77
|
generatedOffsets: [current_generated_offset],
|
|
75
78
|
lengths: [segment_length],
|
|
76
|
-
data:
|
|
79
|
+
data: mapping_data
|
|
77
80
|
});
|
|
78
81
|
}
|
|
79
82
|
|