fix: use fine grained for template if the component is not explicitly in legacy mode (#16232)

* fix: use fine grained for template if the component is not explicitly in legacy mode

* chore: add comment

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>

* fix: add `LabeledStatement` to `instance.ast` check

* fix: spread `keys`

* fix: snapshots

* fix: use `compileOption.runes` if defined + add other tests

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/16216/head
Paolo Ricciuti 2 months ago committed by GitHub
parent 2af7ba2156
commit 922bee9a6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: use fine grained for template if the component is not explicitly in legacy mode

@ -431,6 +431,29 @@ export function analyze_component(root, source, options) {
template,
elements: [],
runes,
// if we are not in runes mode but we have no reserved references ($$props, $$restProps)
// and no `export let` we might be in a wannabe runes component that is using runes in an external
// module...we need to fallback to the runic behavior
maybe_runes:
!runes &&
// if they explicitly disabled runes, use the legacy behavior
options.runes !== false &&
![...module.scope.references.keys()].some((name) =>
['$$props', '$$restProps'].includes(name)
) &&
!instance.ast.body.some(
(node) =>
node.type === 'LabeledStatement' ||
(node.type === 'ExportNamedDeclaration' &&
((node.declaration &&
node.declaration.type === 'VariableDeclaration' &&
node.declaration.kind === 'let') ||
node.specifiers.some(
(specifier) =>
specifier.local.type === 'Identifier' &&
instance.scope.get(specifier.local.name)?.declaration_kind === 'let'
)))
),
tracing: false,
classes: new Map(),
immutable: runes || options.immutable,

@ -370,7 +370,10 @@ export function validate_mutation(node, context, expression) {
export function build_expression(context, expression, metadata, state = context.state) {
const value = /** @type {Expression} */ (context.visit(expression, state));
if (context.state.analysis.runes) {
// Components not explicitly in legacy mode might be expected to be in runes mode (especially since we didn't
// adjust this behavior until recently, which broke people's existing components), so we also bail in this case.
// Kind of an in-between-mode.
if (context.state.analysis.runes || context.state.analysis.maybe_runes) {
return value;
}

@ -51,6 +51,7 @@ export interface ComponentAnalysis extends Analysis {
/** Used for CSS pruning and scoping */
elements: Array<AST.RegularElement | AST.SvelteElement>;
runes: boolean;
maybe_runes: boolean;
tracing: boolean;
exports: Array<{ name: string; alias: string | null }>;
/** Whether the component uses `$$props` */

@ -158,7 +158,10 @@ async function common_setup(cwd: string, runes: boolean | undefined, config: Run
...config.compileOptions,
immutable: config.immutable,
accessors: 'accessors' in config ? config.accessors : true,
runes
runes:
config.compileOptions && 'runes' in config.compileOptions
? config.compileOptions.runes
: runes
};
// load_compiled can be used for debugging a test. It means the compiler will not run on the input

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '0');
}
});

@ -0,0 +1,8 @@
<svelte:options runes={false} />
<script>
import { get, set } from "./test.svelte.js";
</script>
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '0');
}
});

@ -0,0 +1,9 @@
<script>
import { get, set } from "./test.svelte.js";
$$props;
</script>
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '0');
}
});

@ -0,0 +1,9 @@
<script>
import { get, set } from "./test.svelte.js";
$$restProps;
</script>
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '1');
}
});

@ -0,0 +1,9 @@
<script>
import { get, set } from "./test.svelte.js";
export const x = 42;
</script>
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '0');
}
});

@ -0,0 +1,9 @@
<script>
import { get, set } from "./test.svelte.js";
$: console.log("");
</script>
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '0');
}
});

@ -0,0 +1,10 @@
<script>
import { get, set } from "./test.svelte.js";
export let x = 42;
</script>
{x}
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '0');
}
});

@ -0,0 +1,12 @@
<script>
import { get, set } from "./test.svelte.js";
let x = 42;
export { x };
</script>
{x}
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
runes: undefined
},
async test({ assert, target }) {
const p = target.querySelector('p');
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
assert.equal(p?.innerHTML, '1');
}
});

@ -0,0 +1,7 @@
<script>
import { get, set } from "./test.svelte.js";
</script>
<p>{get()}</p>
<button onclick={()=>set()}></button>

@ -0,0 +1,9 @@
let count = $state(0);
export function get() {
return count;
}
export function set() {
count++;
}

@ -8,13 +8,11 @@ export default function Purity($$anchor) {
var fragment = root();
var p = $.first_child(fragment);
p.textContent = (
$.untrack(() => Math.max(0, Math.min(0, 100)))
);
p.textContent = '0';
var p_1 = $.sibling(p, 2);
p_1.textContent = ($.untrack(() => location.href));
p_1.textContent = location.href;
var node = $.sibling(p_1, 2);

Loading…
Cancel
Save