Skip to content

Commit 44bec4c

Browse files
committed
fix: use fine grained for template if the component is not explicitly in legacy mode
1 parent 2af7ba2 commit 44bec4c

File tree

16 files changed

+155
-1
lines changed

16 files changed

+155
-1
lines changed

.changeset/cold-dingos-dream.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: use fine grained for template if the component is not explicitly in legacy mode

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,26 @@ export function analyze_component(root, source, options) {
431431
template,
432432
elements: [],
433433
runes,
434+
// if we are not in runes mode but we have no reserved references ($$props, $$restProps)
435+
// and no `export let` we might be in a wannabe runes component that is using runes in an external
436+
// module...we need to fallback to the runic behavior
437+
maybe_runes:
438+
!runes &&
439+
// if they explicitly disabled runes, use the legacy behavior
440+
options.runes !== false &&
441+
!module.scope.references.keys().some((name) => ['$$props', '$$restProps'].includes(name)) &&
442+
!instance.ast.body.some(
443+
(node) =>
444+
node.type === 'ExportNamedDeclaration' &&
445+
((node.declaration &&
446+
node.declaration.type === 'VariableDeclaration' &&
447+
node.declaration.kind === 'let') ||
448+
node.specifiers.some(
449+
(specifier) =>
450+
specifier.local.type === 'Identifier' &&
451+
instance.scope.get(specifier.local.name)?.declaration_kind === 'let'
452+
))
453+
),
434454
tracing: false,
435455
classes: new Map(),
436456
immutable: runes || options.immutable,

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ export function validate_mutation(node, context, expression) {
370370
export function build_expression(context, expression, metadata, state = context.state) {
371371
const value = /** @type {Expression} */ (context.visit(expression, state));
372372

373-
if (context.state.analysis.runes) {
373+
if (context.state.analysis.runes || context.state.analysis.maybe_runes) {
374374
return value;
375375
}
376376

packages/svelte/src/compiler/phases/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export interface ComponentAnalysis extends Analysis {
5151
/** Used for CSS pruning and scoping */
5252
elements: Array<AST.RegularElement | AST.SvelteElement>;
5353
runes: boolean;
54+
maybe_runes: boolean;
5455
tracing: boolean;
5556
exports: Array<{ name: string; alias: string | null }>;
5657
/** Whether the component uses `$$props` */
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
mode: ['client'],
6+
async test({ assert, target }) {
7+
const p = target.querySelector('p');
8+
const btn = target.querySelector('button');
9+
flushSync(() => {
10+
btn?.click();
11+
});
12+
assert.equal(p?.innerHTML, '0');
13+
}
14+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<svelte:options runes={false} />
2+
<script>
3+
import { get, set } from "./test.svelte.js";
4+
</script>
5+
6+
<p>{get()}</p>
7+
8+
<button onclick={()=>set()}></button>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
let count = $state(0);
2+
3+
export function get() {
4+
return count;
5+
}
6+
7+
export function set() {
8+
count++;
9+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
mode: ['client'],
6+
async test({ assert, target }) {
7+
const p = target.querySelector('p');
8+
const btn = target.querySelector('button');
9+
flushSync(() => {
10+
btn?.click();
11+
});
12+
assert.equal(p?.innerHTML, '1');
13+
}
14+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
import { get, set } from "./test.svelte.js";
3+
4+
export const x = 42;
5+
</script>
6+
7+
<p>{get()}</p>
8+
9+
<button onclick={()=>set()}></button>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
let count = $state(0);
2+
3+
export function get() {
4+
return count;
5+
}
6+
7+
export function set() {
8+
count++;
9+
}

0 commit comments

Comments
 (0)