ripple 0.3.2 → 0.3.4
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/CHANGELOG.md +85 -0
- package/package.json +2 -2
- package/src/compiler/identifier-utils.js +0 -2
- package/src/compiler/phases/1-parse/index.js +101 -195
- package/src/compiler/phases/2-analyze/index.js +82 -174
- package/src/compiler/phases/2-analyze/prune.js +2 -2
- package/src/compiler/phases/3-transform/client/index.js +174 -264
- package/src/compiler/phases/3-transform/segments.js +0 -22
- package/src/compiler/phases/3-transform/server/index.js +185 -42
- package/src/compiler/types/index.d.ts +14 -33
- package/src/compiler/utils.js +32 -20
- package/src/runtime/index-client.js +0 -17
- package/src/runtime/internal/client/bindings.js +118 -7
- package/src/runtime/internal/client/render.js +5 -1
- package/src/runtime/internal/client/runtime.js +1 -1
- package/src/runtime/internal/client/types.d.ts +4 -0
- package/tests/client/array/array.copy-within.test.ripple +7 -7
- package/tests/client/array/array.derived.test.ripple +24 -24
- package/tests/client/array/array.iteration.test.ripple +7 -7
- package/tests/client/array/array.mutations.test.ripple +17 -17
- package/tests/client/array/array.to-methods.test.ripple +4 -4
- package/tests/client/async-suspend.test.ripple +3 -3
- package/tests/client/basic/basic.attributes.test.ripple +31 -31
- package/tests/client/basic/basic.collections.test.ripple +6 -6
- package/tests/client/basic/basic.components.test.ripple +8 -8
- package/tests/client/basic/basic.errors.test.ripple +31 -34
- package/tests/client/basic/basic.events.test.ripple +11 -11
- package/tests/client/basic/basic.get-set.test.ripple +18 -18
- package/tests/client/basic/basic.reactivity.test.ripple +36 -36
- package/tests/client/basic/basic.rendering.test.ripple +7 -7
- package/tests/client/basic/basic.utilities.test.ripple +4 -4
- package/tests/client/boundaries.test.ripple +7 -7
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.ripple.snap +24 -0
- package/tests/client/compiler/compiler.assignments.test.ripple +12 -10
- package/tests/client/compiler/compiler.basic.test.ripple +58 -60
- package/tests/client/compiler/compiler.tracked-access.test.ripple +14 -8
- package/tests/client/compiler/compiler.typescript.test.ripple +31 -0
- package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
- package/tests/client/composite/composite.props.test.ripple +9 -9
- package/tests/client/composite/composite.reactivity.test.ripple +23 -23
- package/tests/client/composite/composite.render.test.ripple +52 -4
- package/tests/client/computed-properties.test.ripple +3 -3
- package/tests/client/context.test.ripple +3 -3
- package/tests/client/css/global-additional-cases.test.ripple +5 -2
- package/tests/client/css/style-identifier.test.ripple +40 -49
- package/tests/client/date.test.ripple +39 -39
- package/tests/client/dynamic-elements.test.ripple +37 -37
- package/tests/client/events.test.ripple +25 -25
- package/tests/client/for.test.ripple +8 -8
- package/tests/client/head.test.ripple +7 -7
- package/tests/client/html.test.ripple +2 -2
- package/tests/client/input-value.test.ripple +376 -177
- package/tests/client/lazy-destructuring.test.ripple +185 -0
- package/tests/client/map.test.ripple +20 -20
- package/tests/client/media-query.test.ripple +4 -4
- package/tests/client/object.test.ripple +5 -5
- package/tests/client/portal.test.ripple +4 -4
- package/tests/client/ref.test.ripple +3 -3
- package/tests/client/return.test.ripple +17 -17
- package/tests/client/set.test.ripple +10 -10
- package/tests/client/svg.test.ripple +6 -5
- package/tests/client/switch.test.ripple +10 -10
- package/tests/client/tracked-expression.test.ripple +3 -1
- package/tests/client/try.test.ripple +4 -4
- package/tests/client/url/url.derived.test.ripple +6 -7
- package/tests/client/url/url.parsing.test.ripple +9 -9
- package/tests/client/url/url.partial-removal.test.ripple +9 -9
- package/tests/client/url/url.reactivity.test.ripple +16 -16
- package/tests/client/url/url.serialization.test.ripple +3 -3
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +7 -8
- package/tests/client/url-search-params/url-search-params.initialization.test.ripple +6 -4
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +12 -12
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +18 -18
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +16 -16
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +4 -4
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -3
- package/tests/hydration/build-components.js +4 -10
- package/tests/hydration/compiled/client/basic.js +4 -4
- package/tests/hydration/compiled/client/events.js +2 -0
- package/tests/hydration/compiled/client/for.js +2 -0
- package/tests/hydration/compiled/client/head.js +13 -11
- package/tests/hydration/compiled/client/hmr.js +4 -2
- package/tests/hydration/compiled/client/html.js +82 -95
- package/tests/hydration/compiled/client/if-children.js +8 -9
- package/tests/hydration/compiled/client/if.js +2 -0
- package/tests/hydration/compiled/client/mixed-control-flow.js +4 -2
- package/tests/hydration/compiled/client/portal.js +1 -1
- package/tests/hydration/compiled/client/reactivity.js +2 -0
- package/tests/hydration/compiled/client/return.js +2 -0
- package/tests/hydration/compiled/client/switch.js +2 -0
- package/tests/hydration/compiled/server/composite.js +2 -2
- package/tests/hydration/compiled/server/events.js +2 -0
- package/tests/hydration/compiled/server/for.js +2 -0
- package/tests/hydration/compiled/server/head.js +13 -11
- package/tests/hydration/compiled/server/hmr.js +2 -0
- package/tests/hydration/compiled/server/html.js +2 -0
- package/tests/hydration/compiled/server/if-children.js +2 -0
- package/tests/hydration/compiled/server/if.js +2 -0
- package/tests/hydration/compiled/server/mixed-control-flow.js +2 -0
- package/tests/hydration/compiled/server/portal.js +1 -1
- package/tests/hydration/compiled/server/reactivity.js +2 -0
- package/tests/hydration/compiled/server/return.js +2 -0
- package/tests/hydration/compiled/server/switch.js +2 -0
- package/tests/hydration/components/composite.ripple +1 -1
- package/tests/hydration/components/events.ripple +10 -8
- package/tests/hydration/components/for.ripple +22 -20
- package/tests/hydration/components/head.ripple +8 -6
- package/tests/hydration/components/hmr.ripple +3 -1
- package/tests/hydration/components/html.ripple +3 -1
- package/tests/hydration/components/if-children.ripple +9 -7
- package/tests/hydration/components/if.ripple +7 -5
- package/tests/hydration/components/mixed-control-flow.ripple +5 -3
- package/tests/hydration/components/portal.ripple +2 -2
- package/tests/hydration/components/reactivity.ripple +11 -9
- package/tests/hydration/components/return.ripple +13 -11
- package/tests/hydration/components/switch.ripple +6 -4
- package/tests/server/__snapshots__/compiler.test.ripple.snap +22 -0
- package/tests/server/await.test.ripple +2 -2
- package/tests/server/basic.attributes.test.ripple +21 -19
- package/tests/server/basic.components.test.ripple +5 -4
- package/tests/server/basic.test.ripple +21 -20
- package/tests/server/compiler.test.ripple +36 -5
- package/tests/server/composite.props.test.ripple +7 -6
- package/tests/server/context.test.ripple +3 -1
- package/tests/server/dynamic-elements.test.ripple +24 -24
- package/tests/server/head.test.ripple +7 -5
- package/tests/server/style-identifier.test.ripple +95 -16
- package/types/index.d.ts +4 -1
|
@@ -68,6 +68,46 @@ function mark_control_flow_has_template(path) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Set up lazy destructuring transforms for bindings extracted from a lazy pattern.
|
|
73
|
+
* Converts each destructured identifier into a binding that lazily accesses properties
|
|
74
|
+
* on the source identifier (e.g., `a` → `source.a` for object, `a` → `source[0]` for array).
|
|
75
|
+
* @param {AST.ObjectPattern | AST.ArrayPattern} pattern - The destructuring pattern with lazy: true
|
|
76
|
+
* @param {AST.Identifier} source_id - The identifier to access properties on
|
|
77
|
+
* @param {AnalysisState} state - The analysis state
|
|
78
|
+
* @param {boolean} writable - Whether assignments/updates should be supported (let vs const)
|
|
79
|
+
*/
|
|
80
|
+
function setup_lazy_transforms(pattern, source_id, state, writable) {
|
|
81
|
+
const paths = extract_paths(pattern);
|
|
82
|
+
|
|
83
|
+
for (const path of paths) {
|
|
84
|
+
const name = /** @type {AST.Identifier} */ (path.node).name;
|
|
85
|
+
const binding = state.scope.get(name);
|
|
86
|
+
|
|
87
|
+
if (binding !== null) {
|
|
88
|
+
binding.kind = path.has_default_value ? 'lazy_fallback' : 'lazy';
|
|
89
|
+
|
|
90
|
+
binding.transform = {
|
|
91
|
+
read: (_) => {
|
|
92
|
+
return path.expression(source_id);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (writable) {
|
|
97
|
+
binding.transform.assign = (node, value) => {
|
|
98
|
+
return b.assignment(
|
|
99
|
+
'=',
|
|
100
|
+
/** @type {AST.MemberExpression} */ (path.update_expression(source_id)),
|
|
101
|
+
value,
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
binding.transform.update = (node) =>
|
|
105
|
+
b.update(node.operator, path.update_expression(source_id), node.prefix);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
71
111
|
/**
|
|
72
112
|
* @param {AST.Function} node
|
|
73
113
|
* @param {AnalysisContext} context
|
|
@@ -78,6 +118,19 @@ function visit_function(node, context) {
|
|
|
78
118
|
path: [...context.path],
|
|
79
119
|
};
|
|
80
120
|
|
|
121
|
+
// Set up lazy transforms for any lazy destructured parameters
|
|
122
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
123
|
+
const param_node = node.params[i];
|
|
124
|
+
const param = param_node.type === 'AssignmentPattern' ? param_node.left : param_node;
|
|
125
|
+
|
|
126
|
+
if ((param.type === 'ObjectPattern' || param.type === 'ArrayPattern') && param.lazy) {
|
|
127
|
+
const param_id = b.id(context.state.scope.generate('param'));
|
|
128
|
+
setup_lazy_transforms(param, param_id, context.state, true);
|
|
129
|
+
// Store the generated identifier name on the pattern for the transform phase
|
|
130
|
+
param.metadata = { ...param.metadata, lazy_id: param_id.name };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
81
134
|
context.next({
|
|
82
135
|
...context.state,
|
|
83
136
|
function_depth: (context.state.function_depth ?? 0) + 1,
|
|
@@ -239,7 +292,7 @@ const visitors = {
|
|
|
239
292
|
if (context.path.at(-1)?.type !== 'Program') {
|
|
240
293
|
// fatal since we don't have a transformation defined for this case
|
|
241
294
|
error(
|
|
242
|
-
'`#
|
|
295
|
+
'`#server` block can only be declared at the module level.',
|
|
243
296
|
context.state.analysis.module.filename,
|
|
244
297
|
node,
|
|
245
298
|
);
|
|
@@ -291,6 +344,8 @@ const visitors = {
|
|
|
291
344
|
if (
|
|
292
345
|
binding.kind === 'prop' ||
|
|
293
346
|
binding.kind === 'prop_fallback' ||
|
|
347
|
+
binding.kind === 'lazy' ||
|
|
348
|
+
binding.kind === 'lazy_fallback' ||
|
|
294
349
|
binding.kind === 'for_pattern' ||
|
|
295
350
|
(is_reference(node, /** @type {AST.Node} */ (parent)) &&
|
|
296
351
|
node.tracked &&
|
|
@@ -303,45 +358,6 @@ const visitors = {
|
|
|
303
358
|
}
|
|
304
359
|
}
|
|
305
360
|
|
|
306
|
-
// Validate #ripple namespace usage
|
|
307
|
-
const source_name = node.metadata?.source_name;
|
|
308
|
-
if (typeof source_name === 'string' && source_name.startsWith('#ripple.')) {
|
|
309
|
-
// Cannot assign to a #ripple namespace identifier (left side)
|
|
310
|
-
if (
|
|
311
|
-
(parent?.type === 'AssignmentExpression' && parent.left === node) ||
|
|
312
|
-
parent?.type === 'UpdateExpression'
|
|
313
|
-
) {
|
|
314
|
-
error(
|
|
315
|
-
`Cannot assign to \`${source_name}\`. The \`#ripple\` namespace is read-only.`,
|
|
316
|
-
context.state.analysis.module.filename,
|
|
317
|
-
node,
|
|
318
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
319
|
-
context.state.analysis.comments,
|
|
320
|
-
);
|
|
321
|
-
return context.next();
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Valid: callee of a CallExpression
|
|
325
|
-
if (parent?.type === 'CallExpression' && parent.callee === node) {
|
|
326
|
-
return context.next();
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Valid: object of a MemberExpression (further validated in MemberExpression visitor)
|
|
330
|
-
if (parent?.type === 'MemberExpression' && parent.object === node) {
|
|
331
|
-
return context.next();
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Everything else is an invalid bare reference
|
|
335
|
-
error(
|
|
336
|
-
`\`${source_name}\` must be called as a function, e.g., \`${source_name}(...)\`.`,
|
|
337
|
-
context.state.analysis.module.filename,
|
|
338
|
-
node,
|
|
339
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
340
|
-
context.state.analysis.comments,
|
|
341
|
-
);
|
|
342
|
-
return context.next();
|
|
343
|
-
}
|
|
344
|
-
|
|
345
361
|
context.next();
|
|
346
362
|
},
|
|
347
363
|
|
|
@@ -358,13 +374,13 @@ const visitors = {
|
|
|
358
374
|
context.state.metadata.tracking = true;
|
|
359
375
|
}
|
|
360
376
|
|
|
361
|
-
// Track #
|
|
377
|
+
// Track #style.className or #style['className'] references
|
|
362
378
|
if (node.object.type === 'StyleIdentifier') {
|
|
363
379
|
const component = is_inside_component(context, true);
|
|
364
380
|
|
|
365
381
|
if (!component) {
|
|
366
382
|
error(
|
|
367
|
-
'`#
|
|
383
|
+
'`#style` can only be used within a component',
|
|
368
384
|
context.state.analysis.module.filename,
|
|
369
385
|
node,
|
|
370
386
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -378,19 +394,19 @@ const visitors = {
|
|
|
378
394
|
let className = null;
|
|
379
395
|
|
|
380
396
|
if (!node.computed && node.property.type === 'Identifier') {
|
|
381
|
-
// #
|
|
397
|
+
// #style.test
|
|
382
398
|
className = node.property.name;
|
|
383
399
|
} else if (
|
|
384
400
|
node.computed &&
|
|
385
401
|
node.property.type === 'Literal' &&
|
|
386
402
|
typeof node.property.value === 'string'
|
|
387
403
|
) {
|
|
388
|
-
// #
|
|
404
|
+
// #style['test']
|
|
389
405
|
className = node.property.value;
|
|
390
406
|
} else {
|
|
391
|
-
// #
|
|
407
|
+
// #style[expression] - dynamic, not allowed
|
|
392
408
|
error(
|
|
393
|
-
'`#
|
|
409
|
+
'`#style` property access must use a dot property or static string for css class name, not a dynamic expression',
|
|
394
410
|
context.state.analysis.module.filename,
|
|
395
411
|
node.property,
|
|
396
412
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -407,71 +423,6 @@ const visitors = {
|
|
|
407
423
|
context.state.analysis.metadata.serverIdentifierPresent = true;
|
|
408
424
|
}
|
|
409
425
|
|
|
410
|
-
// Validate #ripple namespace member access
|
|
411
|
-
if (
|
|
412
|
-
node.object.type === 'Identifier' &&
|
|
413
|
-
typeof node.object.metadata?.source_name === 'string' &&
|
|
414
|
-
node.object.metadata.source_name.startsWith('#ripple.')
|
|
415
|
-
) {
|
|
416
|
-
const ripple_source = node.object.metadata.source_name;
|
|
417
|
-
const member_parent = context.path.at(-1);
|
|
418
|
-
|
|
419
|
-
// No computed property access on #ripple namespace
|
|
420
|
-
if (node.computed) {
|
|
421
|
-
error(
|
|
422
|
-
`Computed property access is not allowed on \`${ripple_source}\`. Use dot notation instead.`,
|
|
423
|
-
context.state.analysis.module.filename,
|
|
424
|
-
node,
|
|
425
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
426
|
-
context.state.analysis.comments,
|
|
427
|
-
);
|
|
428
|
-
return context.next();
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (ripple_source === '#ripple.array') {
|
|
432
|
-
// Only .from, .of, and .fromAsync are allowed on #ripple.array
|
|
433
|
-
const allowed_methods = new Set(['from', 'of', 'fromAsync']);
|
|
434
|
-
const prop_name = node.property.type === 'Identifier' ? node.property.name : null;
|
|
435
|
-
|
|
436
|
-
if (prop_name === null || !allowed_methods.has(prop_name)) {
|
|
437
|
-
error(
|
|
438
|
-
`Only \`.from\`, \`.of\`, and \`.fromAsync\` are allowed on \`#ripple.array\`.${prop_name ? ` Got \`.${prop_name}\`.` : ''}`,
|
|
439
|
-
context.state.analysis.module.filename,
|
|
440
|
-
node.property,
|
|
441
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
442
|
-
context.state.analysis.comments,
|
|
443
|
-
);
|
|
444
|
-
return context.next();
|
|
445
|
-
}
|
|
446
|
-
} else {
|
|
447
|
-
// No member access allowed for other #ripple namespaces
|
|
448
|
-
error(
|
|
449
|
-
`Member access is not allowed on \`${ripple_source}\`. Use \`${ripple_source}(...)\` to call it directly.`,
|
|
450
|
-
context.state.analysis.module.filename,
|
|
451
|
-
node,
|
|
452
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
453
|
-
context.state.analysis.comments,
|
|
454
|
-
);
|
|
455
|
-
return context.next();
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// All #ripple member expressions must be called as a function
|
|
459
|
-
if (!(member_parent?.type === 'CallExpression' && member_parent.callee === node)) {
|
|
460
|
-
const prop_name = node.property.type === 'Identifier' ? node.property.name : null;
|
|
461
|
-
const full_name = prop_name ? `${ripple_source}.${prop_name}` : ripple_source;
|
|
462
|
-
error(
|
|
463
|
-
`\`${full_name}\` must be called as a function, e.g., \`${full_name}(...)\`.`,
|
|
464
|
-
context.state.analysis.module.filename,
|
|
465
|
-
node,
|
|
466
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
467
|
-
context.state.analysis.comments,
|
|
468
|
-
);
|
|
469
|
-
return context.next();
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
return context.next();
|
|
473
|
-
}
|
|
474
|
-
|
|
475
426
|
if (node.object.type === 'Identifier' && !node.object.tracked) {
|
|
476
427
|
const binding = context.state.scope.get(node.object.name);
|
|
477
428
|
|
|
@@ -544,38 +495,6 @@ const visitors = {
|
|
|
544
495
|
},
|
|
545
496
|
|
|
546
497
|
NewExpression(node, context) {
|
|
547
|
-
const callee = node.callee;
|
|
548
|
-
|
|
549
|
-
// Cannot use `new` with #ripple namespace
|
|
550
|
-
if (
|
|
551
|
-
callee.type === 'Identifier' &&
|
|
552
|
-
typeof callee.metadata?.source_name === 'string' &&
|
|
553
|
-
callee.metadata.source_name.startsWith('#ripple.')
|
|
554
|
-
) {
|
|
555
|
-
error(
|
|
556
|
-
`Cannot use \`new\` with \`${callee.metadata.source_name}\`. Use \`${callee.metadata.source_name}(...)\` instead.`,
|
|
557
|
-
context.state.analysis.module.filename,
|
|
558
|
-
node,
|
|
559
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
560
|
-
context.state.analysis.comments,
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (
|
|
565
|
-
callee.type === 'MemberExpression' &&
|
|
566
|
-
callee.object.type === 'Identifier' &&
|
|
567
|
-
typeof callee.object.metadata?.source_name === 'string' &&
|
|
568
|
-
callee.object.metadata.source_name.startsWith('#ripple.')
|
|
569
|
-
) {
|
|
570
|
-
error(
|
|
571
|
-
`Cannot use \`new\` with the \`#ripple\` namespace.`,
|
|
572
|
-
context.state.analysis.module.filename,
|
|
573
|
-
node,
|
|
574
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
575
|
-
context.state.analysis.comments,
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
498
|
context.next();
|
|
580
499
|
},
|
|
581
500
|
|
|
@@ -611,6 +530,18 @@ const visitors = {
|
|
|
611
530
|
}
|
|
612
531
|
visit(declarator, state);
|
|
613
532
|
} else {
|
|
533
|
+
// Handle lazy destructuring patterns
|
|
534
|
+
if (
|
|
535
|
+
(declarator.id.type === 'ObjectPattern' || declarator.id.type === 'ArrayPattern') &&
|
|
536
|
+
declarator.id.lazy
|
|
537
|
+
) {
|
|
538
|
+
const lazy_id = b.id(state.scope.generate('lazy'));
|
|
539
|
+
const writable = node.kind !== 'const';
|
|
540
|
+
setup_lazy_transforms(declarator.id, lazy_id, state, writable);
|
|
541
|
+
// Store the generated identifier name on the pattern for the transform phase
|
|
542
|
+
declarator.id.metadata = { ...declarator.id.metadata, lazy_id: lazy_id.name };
|
|
543
|
+
}
|
|
544
|
+
|
|
614
545
|
const paths = extract_paths(declarator.id);
|
|
615
546
|
|
|
616
547
|
for (const path of paths) {
|
|
@@ -640,10 +571,10 @@ const visitors = {
|
|
|
640
571
|
component.metadata.styleIdentifierPresent = true;
|
|
641
572
|
}
|
|
642
573
|
|
|
643
|
-
// #
|
|
574
|
+
// #style must only be used for property access (e.g., #style.className)
|
|
644
575
|
if (!parent || parent.type !== 'MemberExpression' || parent.object !== node) {
|
|
645
576
|
error(
|
|
646
|
-
'`#
|
|
577
|
+
'`#style` can only be used for property access, e.g., `#style.className`.',
|
|
647
578
|
context.state.analysis.module.filename,
|
|
648
579
|
node,
|
|
649
580
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -656,10 +587,10 @@ const visitors = {
|
|
|
656
587
|
ServerIdentifier(node, context) {
|
|
657
588
|
const parent = context.path.at(-1);
|
|
658
589
|
|
|
659
|
-
// #
|
|
590
|
+
// #server must only be used for member access (e.g., #server.functionName(...))
|
|
660
591
|
if (!parent || parent.type !== 'MemberExpression' || parent.object !== node) {
|
|
661
592
|
error(
|
|
662
|
-
'`#
|
|
593
|
+
'`#server` can only be used for member access, e.g., `#server.functionName(...)`.',
|
|
663
594
|
context.state.analysis.module.filename,
|
|
664
595
|
node,
|
|
665
596
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -685,32 +616,9 @@ const visitors = {
|
|
|
685
616
|
if (node.params.length > 0) {
|
|
686
617
|
const props = node.params[0];
|
|
687
618
|
|
|
688
|
-
if (props.type === 'ObjectPattern') {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
for (const path of paths) {
|
|
692
|
-
const name = /** @type {AST.Identifier} */ (path.node).name;
|
|
693
|
-
const binding = context.state.scope.get(name);
|
|
694
|
-
|
|
695
|
-
if (binding !== null) {
|
|
696
|
-
binding.kind = path.has_default_value ? 'prop_fallback' : 'prop';
|
|
697
|
-
|
|
698
|
-
binding.transform = {
|
|
699
|
-
read: (_) => {
|
|
700
|
-
return path.expression(b.id('__props'));
|
|
701
|
-
},
|
|
702
|
-
assign: (node, value) => {
|
|
703
|
-
return b.assignment(
|
|
704
|
-
'=',
|
|
705
|
-
/** @type {AST.MemberExpression} */ (path.expression(b.id('__props'))),
|
|
706
|
-
value,
|
|
707
|
-
);
|
|
708
|
-
},
|
|
709
|
-
update: (node) =>
|
|
710
|
-
b.update(node.operator, path.expression(b.id('__props')), node.prefix),
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
}
|
|
619
|
+
if ((props.type === 'ObjectPattern' || props.type === 'ArrayPattern') && props.lazy) {
|
|
620
|
+
// Lazy destructuring: &{...} or &[...] — set up lazy transforms
|
|
621
|
+
setup_lazy_transforms(props, b.id('__props'), context.state, true);
|
|
714
622
|
} else if (props.type === 'AssignmentPattern') {
|
|
715
623
|
error(
|
|
716
624
|
'Props are always an object, use destructured props with default values instead',
|
|
@@ -1408,7 +1316,7 @@ const visitors = {
|
|
|
1408
1316
|
attr.value.object.type === 'StyleIdentifier'
|
|
1409
1317
|
) {
|
|
1410
1318
|
error(
|
|
1411
|
-
'`#
|
|
1319
|
+
'`#style` cannot be used directly on DOM elements. Pass the class to a child component instead.',
|
|
1412
1320
|
state.analysis.module.filename,
|
|
1413
1321
|
attr.value.object,
|
|
1414
1322
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -1078,11 +1078,11 @@ export function prune_css(css, element, styleClasses, topScopedClasses) {
|
|
|
1078
1078
|
node.metadata.used = true;
|
|
1079
1079
|
}
|
|
1080
1080
|
|
|
1081
|
-
// Populate top_scoped_classes for truly standalone class selectors (for #
|
|
1081
|
+
// Populate top_scoped_classes for truly standalone class selectors (for #style support).
|
|
1082
1082
|
// A class is standalone only when the entire effective selector chain (after resolving
|
|
1083
1083
|
// nesting and stripping :global) is a single RelativeSelector with a single ClassSelector.
|
|
1084
1084
|
// This prevents classes from compound selectors like `.wrapper .nested` or selectors
|
|
1085
|
-
// inside :global() from being treated as valid #
|
|
1085
|
+
// inside :global() from being treated as valid #style targets.
|
|
1086
1086
|
if (selectors.length === 1) {
|
|
1087
1087
|
const sole_selector = selectors[0];
|
|
1088
1088
|
if (
|