Merge branch 'elliott/variadic-snippets' of github.com:sveltejs/svelte into elliott/variadic-snippets

pull/10320/head
S. Elliott Johnson 7 months ago
commit 979170ce05

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: improve nested effect heuristics

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve ssr template code generation

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: make `@types/estree` a dependency

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve template literal expression output generation

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve outro behavior with transitions

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve intro transitions on dynamic mount

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: remove internal functions from `svelte/transition` exports

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve code generation

@ -10,10 +10,13 @@
}, },
"changesets": [ "changesets": [
"afraid-moose-matter", "afraid-moose-matter",
"angry-books-jam",
"beige-flies-wash", "beige-flies-wash",
"beige-rabbits-shave", "beige-rabbits-shave",
"brave-walls-destroy", "brave-walls-destroy",
"breezy-carrots-flash",
"bright-peas-juggle", "bright-peas-juggle",
"bright-snakes-sing",
"brown-spoons-boil", "brown-spoons-boil",
"chatty-cups-drop", "chatty-cups-drop",
"chatty-taxis-juggle", "chatty-taxis-juggle",
@ -38,6 +41,8 @@
"early-ads-tie", "early-ads-tie",
"eight-steaks-shout", "eight-steaks-shout",
"eighty-bikes-camp", "eighty-bikes-camp",
"eighty-days-cheat",
"empty-bulldogs-exercise",
"empty-crabs-think", "empty-crabs-think",
"fair-crabs-check", "fair-crabs-check",
"famous-knives-sneeze", "famous-knives-sneeze",
@ -73,6 +78,7 @@
"itchy-terms-guess", "itchy-terms-guess",
"khaki-mails-draw", "khaki-mails-draw",
"khaki-moose-arrive", "khaki-moose-arrive",
"kind-baboons-approve",
"kind-deers-lay", "kind-deers-lay",
"kind-eagles-join", "kind-eagles-join",
"large-clouds-carry", "large-clouds-carry",
@ -91,6 +97,7 @@
"lovely-items-turn", "lovely-items-turn",
"lovely-rules-eat", "lovely-rules-eat",
"lucky-schools-hang", "lucky-schools-hang",
"lucky-toes-begin",
"moody-frogs-exist", "moody-frogs-exist",
"moody-owls-cry", "moody-owls-cry",
"nasty-lions-double", "nasty-lions-double",
@ -98,6 +105,7 @@
"neat-dingos-clap", "neat-dingos-clap",
"new-boats-wait", "new-boats-wait",
"ninety-dingos-walk", "ninety-dingos-walk",
"odd-buckets-lie",
"odd-needles-joke", "odd-needles-joke",
"odd-schools-wait", "odd-schools-wait",
"odd-shoes-cheat", "odd-shoes-cheat",
@ -120,6 +128,7 @@
"rare-pears-whisper", "rare-pears-whisper",
"real-guests-do", "real-guests-do",
"red-doors-own", "red-doors-own",
"rich-cobras-exist",
"rich-sheep-burn", "rich-sheep-burn",
"rich-tables-sing", "rich-tables-sing",
"rotten-bags-type", "rotten-bags-type",
@ -128,6 +137,7 @@
"serious-socks-cover", "serious-socks-cover",
"serious-zebras-scream", "serious-zebras-scream",
"seven-deers-jam", "seven-deers-jam",
"seven-jobs-sniff",
"seven-ravens-check", "seven-ravens-check",
"sharp-gorillas-impress", "sharp-gorillas-impress",
"sharp-kids-happen", "sharp-kids-happen",
@ -137,16 +147,21 @@
"short-buses-camp", "short-buses-camp",
"slimy-clouds-talk", "slimy-clouds-talk",
"slimy-walls-draw", "slimy-walls-draw",
"slow-beds-shave",
"slow-chefs-dream", "slow-chefs-dream",
"slow-wombats-reply",
"small-papayas-laugh", "small-papayas-laugh",
"smart-parents-swim", "smart-parents-swim",
"smart-zebras-pay", "smart-zebras-pay",
"soft-clocks-remember", "soft-clocks-remember",
"soft-geese-learn", "soft-geese-learn",
"soft-tigers-wink",
"sour-forks-stare", "sour-forks-stare",
"sour-rules-march", "sour-rules-march",
"sour-weeks-fix",
"spicy-plums-admire", "spicy-plums-admire",
"spotty-pens-agree", "spotty-pens-agree",
"spotty-spiders-compare",
"stale-books-perform", "stale-books-perform",
"stale-comics-look", "stale-comics-look",
"strong-gifts-smoke", "strong-gifts-smoke",
@ -160,9 +175,11 @@
"tall-garlics-try", "tall-garlics-try",
"tall-shrimps-worry", "tall-shrimps-worry",
"tall-tigers-wait", "tall-tigers-wait",
"tame-spies-drum",
"tasty-numbers-perform", "tasty-numbers-perform",
"ten-foxes-repeat", "ten-foxes-repeat",
"ten-peaches-sleep", "ten-peaches-sleep",
"ten-ties-repair",
"ten-worms-reflect", "ten-worms-reflect",
"thin-foxes-lick", "thin-foxes-lick",
"thirty-flowers-sit", "thirty-flowers-sit",

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: simplify event delegation logic, only delegate event attributes

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: adjust `$inspect.with` type

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: further animation transition improvements

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve how transitions are handled on mount

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve animation transition heuristics

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: adjust `parse` return type

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: prevent transition action overfiring

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve event handling compatibility with delegation

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: ensure topological order for render effects

@ -22,13 +22,6 @@ body:
placeholder: I would like to see... placeholder: I would like to see...
validations: validations:
required: true required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: "Please provide a clear and concise description of any alternative solutions or features you've considered."
validations:
required: true
- type: dropdown - type: dropdown
id: importance id: importance
attributes: attributes:

@ -0,0 +1,7 @@
{
"drips": {
"ethereum": {
"ownedBy": "0x99D414693dD65E4a0664a16D155dB66283A162D1"
}
}
}

@ -16,7 +16,7 @@
? people.filter((person) => { ? people.filter((person) => {
const name = `${person.last}, ${person.first}`; const name = `${person.last}, ${person.first}`;
return name.toLowerCase().startsWith(prefix.toLowerCase()); return name.toLowerCase().startsWith(prefix.toLowerCase());
}) })
: people; : people;
$: selected = filteredPeople[i]; $: selected = filteredPeople[i];

@ -18,8 +18,8 @@
"build:sites": "pnpm -r --filter=./sites/* build", "build:sites": "pnpm -r --filter=./sites/* build",
"preview-site": "npm run build --prefix sites/svelte-5-preview", "preview-site": "npm run build --prefix sites/svelte-5-preview",
"check": "cd packages/svelte && pnpm build && cd ../../ && pnpm -r check", "check": "cd packages/svelte && pnpm build && cd ../../ && pnpm -r check",
"format": "prettier --write --plugin prettier-plugin-svelte .", "format": "prettier --write .",
"lint": "prettier --check --plugin prettier-plugin-svelte . && eslint ./", "lint": "prettier --check . && eslint ./",
"test": "vitest run --coverage", "test": "vitest run --coverage",
"test-output": "vitest run --reporter=json --outputFile=sites/svelte-5-preview/src/routes/status/results.json", "test-output": "vitest run --reporter=json --outputFile=sites/svelte-5-preview/src/routes/status/results.json",
"changeset:version": "changeset version && pnpm -r generate:version && git add --all", "changeset:version": "changeset version && pnpm -r generate:version && git add --all",
@ -38,9 +38,14 @@
"eslint-plugin-lube": "^0.1.7", "eslint-plugin-lube": "^0.1.7",
"jsdom": "22.0.0", "jsdom": "22.0.0",
"playwright": "^1.35.1", "playwright": "^1.35.1",
"prettier": "^3.0.1", "prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.0.3", "prettier-plugin-svelte": "^3.1.2",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vitest": "^0.34.6" "vitest": "^0.34.6"
},
"pnpm": {
"overrides": {
"jimp>xml2js": "^0.6.0"
}
} }
} }

@ -1,5 +1,55 @@
# svelte # svelte
## 5.0.0-next.35
### Patch Changes
- fix: improve nested effect heuristics ([#10171](https://github.com/sveltejs/svelte/pull/10171))
- fix: simplify event delegation logic, only delegate event attributes ([#10169](https://github.com/sveltejs/svelte/pull/10169))
- fix: prevent transition action overfiring ([#10163](https://github.com/sveltejs/svelte/pull/10163))
- fix: improve event handling compatibility with delegation ([#10168](https://github.com/sveltejs/svelte/pull/10168))
- fix: ensure topological order for render effects ([#10175](https://github.com/sveltejs/svelte/pull/10175))
## 5.0.0-next.34
### Patch Changes
- fix: make `@types/estree` a dependency ([#10150](https://github.com/sveltejs/svelte/pull/10150))
- fix: improve intro transitions on dynamic mount ([#10162](https://github.com/sveltejs/svelte/pull/10162))
- fix: improve code generation ([#10156](https://github.com/sveltejs/svelte/pull/10156))
- fix: adjust `$inspect.with` type ([`c7cb90c91`](https://github.com/sveltejs/svelte/commit/c7cb90c91cd3553ad59126267c9bfddecbb290b4))
- fix: improve how transitions are handled on mount ([#10157](https://github.com/sveltejs/svelte/pull/10157))
- fix: adjust `parse` return type ([`a271878ab`](https://github.com/sveltejs/svelte/commit/a271878abe7018923839401129b18082eb2c811a))
## 5.0.0-next.33
### Patch Changes
- fix: improve ssr template code generation ([#10151](https://github.com/sveltejs/svelte/pull/10151))
- fix: improve template literal expression output generation ([#10147](https://github.com/sveltejs/svelte/pull/10147))
## 5.0.0-next.32
### Patch Changes
- fix: improve outro behavior with transitions ([#10139](https://github.com/sveltejs/svelte/pull/10139))
- chore: remove internal functions from `svelte/transition` exports ([#10132](https://github.com/sveltejs/svelte/pull/10132))
- fix: further animation transition improvements ([#10138](https://github.com/sveltejs/svelte/pull/10138))
- fix: improve animation transition heuristics ([#10119](https://github.com/sveltejs/svelte/pull/10119))
## 5.0.0-next.31 ## 5.0.0-next.31
### Patch Changes ### Patch Changes

@ -2,7 +2,7 @@
"name": "svelte", "name": "svelte",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"license": "MIT", "license": "MIT",
"version": "5.0.0-next.31", "version": "5.0.0-next.35",
"type": "module", "type": "module",
"types": "./types/index.d.ts", "types": "./types/index.d.ts",
"engines": { "engines": {
@ -107,7 +107,6 @@
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-virtual": "^3.0.2", "@rollup/plugin-virtual": "^3.0.2",
"@types/aria-query": "^5.0.3", "@types/aria-query": "^5.0.3",
"@types/estree": "^1.0.5",
"dts-buddy": "^0.4.3", "dts-buddy": "^0.4.3",
"esbuild": "^0.19.2", "esbuild": "^0.19.2",
"rollup": "^4.1.5", "rollup": "^4.1.5",
@ -117,6 +116,7 @@
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.2.1", "@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15", "@jridgewell/sourcemap-codec": "^1.4.15",
"@types/estree": "^1.0.5",
"acorn": "^8.10.0", "acorn": "^8.10.0",
"acorn-typescript": "^1.4.13", "acorn-typescript": "^1.4.13",
"aria-query": "^5.3.0", "aria-query": "^5.3.0",

@ -234,8 +234,8 @@ const attributes = {
type === 'no-each' type === 'no-each'
? `An element that uses the animate directive must be the immediate child of a keyed each block` ? `An element that uses the animate directive must be the immediate child of a keyed each block`
: type === 'each-key' : type === 'each-key'
? `An element that uses the animate directive must be used inside a keyed each block. Did you forget to add a key to your each block?` ? `An element that uses the animate directive must be used inside a keyed each block. Did you forget to add a key to your each block?`
: `An element that uses the animate directive must be the sole child of a keyed each block`, : `An element that uses the animate directive must be the sole child of a keyed each block`,
'duplicate-animation': () => `An element can only have one 'animate' directive`, 'duplicate-animation': () => `An element can only have one 'animate' directive`,
/** @param {string[] | undefined} [modifiers] */ /** @param {string[] | undefined} [modifiers] */
'invalid-event-modifier': (modifiers) => 'invalid-event-modifier': (modifiers) =>
@ -262,7 +262,7 @@ const attributes = {
? `An element can only have one '${directive1}' directive` ? `An element can only have one '${directive1}' directive`
: `An element cannot have both ${describe(directive1)} directive and ${describe( : `An element cannot have both ${describe(directive1)} directive and ${describe(
directive2 directive2
)} directive`; )} directive`;
}, },
'invalid-let-directive-placement': () => 'let directive at invalid position' 'invalid-let-directive-placement': () => 'let directive at invalid position'
}; };

@ -91,7 +91,7 @@ function handle_compile_error(error, filename, source) {
* https://svelte.dev/docs/svelte-compiler#svelte-parse * https://svelte.dev/docs/svelte-compiler#svelte-parse
* @param {string} source * @param {string} source
* @param {{ filename?: string; modern?: boolean }} [options] * @param {{ filename?: string; modern?: boolean }} [options]
* @returns {import('#compiler').SvelteNode | import('./types/legacy-nodes.js').LegacySvelteNode} * @returns {import('#compiler').Root | import('./types/legacy-nodes.js').LegacyRoot}
*/ */
export function parse(source, options = {}) { export function parse(source, options = {}) {
/** @type {import('#compiler').Root} */ /** @type {import('#compiler').Root} */
@ -108,7 +108,7 @@ export function parse(source, options = {}) {
if (options.modern) { if (options.modern) {
// remove things that we don't want to treat as public API // remove things that we don't want to treat as public API
return walk(/** @type {import('#compiler').SvelteNode} */ (ast), null, { return walk(ast, null, {
_(node, { next }) { _(node, { next }) {
// @ts-ignore // @ts-ignore
delete node.parent; delete node.parent;

@ -33,7 +33,7 @@ function remove_surrounding_whitespace_nodes(nodes) {
* Transform our nice modern AST into the monstrosity emitted by Svelte 4 * Transform our nice modern AST into the monstrosity emitted by Svelte 4
* @param {string} source * @param {string} source
* @param {import('#compiler').Root} ast * @param {import('#compiler').Root} ast
* @returns {import('./types/legacy-nodes.js').LegacySvelteNode} * @returns {import('./types/legacy-nodes.js').LegacyRoot}
*/ */
export function convert(source, ast) { export function convert(source, ast) {
const root = const root =
@ -41,7 +41,7 @@ export function convert(source, ast) {
ast ast
); );
return /** @type {import('./types/legacy-nodes.js').LegacySvelteNode} */ ( return /** @type {import('./types/legacy-nodes.js').LegacyRoot} */ (
walk(root, null, { walk(root, null, {
_(node, { next }) { _(node, { next }) {
// @ts-ignore // @ts-ignore
@ -108,7 +108,7 @@ export function convert(source, ast) {
// @ts-ignore // @ts-ignore
delete node.parent; delete node.parent;
} }
}) })
: undefined : undefined
}; };
}, },

@ -108,12 +108,12 @@ export default function tag(parser) {
const type = meta_tags.has(name) const type = meta_tags.has(name)
? meta_tags.get(name) ? meta_tags.get(name)
: regex_capital_letter.test(name[0]) || name === 'svelte:self' || name === 'svelte:component' : regex_capital_letter.test(name[0]) || name === 'svelte:self' || name === 'svelte:component'
? 'Component' ? 'Component'
: name === 'title' && parent_is_head(parser.stack) : name === 'title' && parent_is_head(parser.stack)
? 'TitleElement' ? 'TitleElement'
: name === 'slot' : name === 'slot'
? 'SlotElement' ? 'SlotElement'
: 'RegularElement'; : 'RegularElement';
/** @type {import('#compiler').ElementLike} */ /** @type {import('#compiler').ElementLike} */
// @ts-expect-error TODO can't figure out this error // @ts-expect-error TODO can't figure out this error
@ -128,11 +128,10 @@ export default function tag(parser) {
fragment: create_fragment(true), fragment: create_fragment(true),
metadata: { metadata: {
svg: false, svg: false,
has_spread: false, has_spread: false
can_delegate_events: null
}, },
parent: null parent: null
} }
: { : {
type: /** @type {import('#compiler').ElementLike['type']} */ (type), type: /** @type {import('#compiler').ElementLike['type']} */ (type),
start, start,
@ -141,7 +140,7 @@ export default function tag(parser) {
attributes: [], attributes: [],
fragment: create_fragment(true), fragment: create_fragment(true),
parent: null parent: null
}; };
parser.allow_whitespace(); parser.allow_whitespace();

@ -275,7 +275,7 @@ export function combine_sourcemaps(filename, sourcemap_list) {
sourcemap_list, sourcemap_list,
() => null, () => null,
true // skip optional field `sourcesContent` true // skip optional field `sourcesContent`
) )
: remapping( : remapping(
// use loader interface // use loader interface
sourcemap_list[0], // last map sourcemap_list[0], // last map
@ -291,7 +291,7 @@ export function combine_sourcemaps(filename, sourcemap_list) {
} }
), ),
true true
); );
if (!map.file) delete map.file; // skip optional field `file` if (!map.file) delete map.file; // skip optional field `file`

@ -59,27 +59,23 @@ function get_component_name(filename) {
} }
/** /**
* @param {Pick<import('#compiler').OnDirective, 'expression'| 'name' | 'modifiers'> & { type: string }} node * Checks if given event attribute can be delegated/hoisted and returns the corresponding info if so
* @param {string} event_name
* @param {import('estree').Expression | null} handler
* @param {import('./types').Context} context * @param {import('./types').Context} context
* @returns {null | import('#compiler').DelegatedEvent} * @returns {null | import('#compiler').DelegatedEvent}
*/ */
function get_delegated_event(node, context) { function get_delegated_event(event_name, handler, context) {
const handler = node.expression;
const event_name = node.name;
// Handle delegated event handlers. Bail-out if not a delegated event. // Handle delegated event handlers. Bail-out if not a delegated event.
if (!handler || node.modifiers.includes('capture') || !DelegatedEvents.includes(event_name)) { if (!handler || !DelegatedEvents.includes(event_name)) {
return null; return null;
} }
// If we are not working with a RegularElement, then bail-out. // If we are not working with a RegularElement, then bail-out.
const element = context.path.at(-1); const element = context.path.at(-1);
if (element?.type !== 'RegularElement') { if (element?.type !== 'RegularElement') {
return null; return null;
} }
// If element says we can't delegate because we have multiple OnDirectives of the same type, bail-out.
if (!element.metadata.can_delegate_events) {
return null;
}
/** @type {import('#compiler').DelegatedEvent} */ /** @type {import('#compiler').DelegatedEvent} */
const non_hoistable = { type: 'non-hoistable' }; const non_hoistable = { type: 'non-hoistable' };
@ -87,7 +83,7 @@ function get_delegated_event(node, context) {
let target_function = null; let target_function = null;
let binding = null; let binding = null;
if (node.type === 'Attribute' && element.metadata.has_spread) { if (element.metadata.has_spread) {
// event attribute becomes part of the dynamic spread array // event attribute becomes part of the dynamic spread array
return non_hoistable; return non_hoistable;
} }
@ -123,8 +119,7 @@ function get_delegated_event(node, context) {
if (element && event_name) { if (element && event_name) {
if ( if (
element.type !== 'RegularElement' || element.type !== 'RegularElement' ||
!determine_element_spread_and_delegatable(element).metadata.can_delegate_events || determine_element_spread(element).metadata.has_spread ||
(element.metadata.has_spread && node.type === 'Attribute') ||
!DelegatedEvents.includes(event_name) !DelegatedEvents.includes(event_name)
) { ) {
return non_hoistable; return non_hoistable;
@ -183,7 +178,8 @@ function get_delegated_event(node, context) {
) { ) {
return non_hoistable; return non_hoistable;
} }
// If we referebnce the index within an each block, then bail-out.
// If we reference the index within an each block, then bail-out.
if (binding !== null && binding.initial?.type === 'EachBlock') { if (binding !== null && binding.initial?.type === 'EachBlock') {
return non_hoistable; return non_hoistable;
} }
@ -204,6 +200,7 @@ function get_delegated_event(node, context) {
} }
visited_references.add(reference); visited_references.add(reference);
} }
return { type: 'hoistable', function: target_function }; return { type: 'hoistable', function: target_function };
} }
@ -713,12 +710,12 @@ const runes_scope_tweaker = {
rune === '$state' rune === '$state'
? 'state' ? 'state'
: rune === '$state.frozen' : rune === '$state.frozen'
? 'frozen_state' ? 'frozen_state'
: rune === '$derived' : rune === '$derived'
? 'derived' ? 'derived'
: path.is_rest : path.is_rest
? 'rest_prop' ? 'rest_prop'
: 'prop'; : 'prop';
} }
if (rune === '$props') { if (rune === '$props') {
@ -858,21 +855,9 @@ const common_visitors = {
}); });
if (is_event_attribute(node)) { if (is_event_attribute(node)) {
/** @type {string[]} */
const modifiers = [];
const expression = node.value[0].expression; const expression = node.value[0].expression;
let name = node.name.slice(2); const delegated_event = get_delegated_event(node.name.slice(2), expression, context);
if (is_capture_event(name)) {
name = name.slice(0, -7);
modifiers.push('capture');
}
const delegated_event = get_delegated_event(
{ type: node.type, name, expression, modifiers },
context
);
if (delegated_event !== null) { if (delegated_event !== null) {
if (delegated_event.type === 'hoistable') { if (delegated_event.type === 'hoistable') {
@ -1032,18 +1017,6 @@ const common_visitors = {
) )
}; };
}, },
OnDirective(node, context) {
node.metadata = { delegated: null };
context.next();
const delegated_event = get_delegated_event(node, context);
if (delegated_event !== null) {
if (delegated_event.type === 'hoistable') {
delegated_event.function.metadata.hoistable = true;
}
node.metadata.delegated = delegated_event;
}
},
ArrowFunctionExpression: function_visitor, ArrowFunctionExpression: function_visitor,
FunctionExpression: function_visitor, FunctionExpression: function_visitor,
FunctionDeclaration: function_visitor, FunctionDeclaration: function_visitor,
@ -1052,7 +1025,7 @@ const common_visitors = {
node.metadata.svg = true; node.metadata.svg = true;
} }
determine_element_spread_and_delegatable(node); determine_element_spread(node);
// Special case: Move the children of <textarea> into a value attribute if they are dynamic // Special case: Move the children of <textarea> into a value attribute if they are dynamic
if ( if (
@ -1110,51 +1083,15 @@ const common_visitors = {
}; };
/** /**
* Check if events on this element can theoretically be delegated. They can if there's no
* possibility of an OnDirective and an event attribute on the same element, and if there's
* no OnDirectives of the same type (the latter is a bit too strict because `on:click on:click on:keyup`
* means that `on:keyup` can be delegated but we gloss over this edge case).
* @param {import('#compiler').RegularElement} node * @param {import('#compiler').RegularElement} node
*/ */
function determine_element_spread_and_delegatable(node) { function determine_element_spread(node) {
if (typeof node.metadata.can_delegate_events === 'boolean') {
return node; // did this already
}
let events = new Map();
let has_spread = false; let has_spread = false;
let has_on = false;
let has_action_or_bind = false;
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
if ( if (!has_spread && attribute.type === 'SpreadAttribute') {
attribute.type === 'OnDirective' ||
(attribute.type === 'Attribute' && is_event_attribute(attribute))
) {
let event_name = attribute.name;
if (attribute.type === 'Attribute') {
event_name = get_attribute_event_name(event_name);
}
events.set(event_name, (events.get(event_name) || 0) + 1);
if (!has_on && attribute.type === 'OnDirective') {
has_on = true;
}
} else if (!has_spread && attribute.type === 'SpreadAttribute') {
has_spread = true; has_spread = true;
} else if (
!has_action_or_bind &&
((attribute.type === 'BindDirective' && attribute.name !== 'this') ||
attribute.type === 'UseDirective')
) {
has_action_or_bind = true;
} }
} }
node.metadata.can_delegate_events =
// Actions/bindings need the old on:-events to fire in order
!has_action_or_bind &&
// spreading events means we don't know if there's an event attribute with the same name as an on:-event
!(has_spread && has_on) &&
// multiple on:-events/event attributes with the same name
![...events.values()].some((count) => count > 1);
node.metadata.has_spread = has_spread; node.metadata.has_spread = has_spread;
return node; return node;

@ -201,7 +201,7 @@ export function client_component(source, analysis, options) {
b.call('$.validate_store', store_reference, b.literal(name.slice(1))), b.call('$.validate_store', store_reference, b.literal(name.slice(1))),
store_get store_get
]) ])
) )
: b.thunk(store_get) : b.thunk(store_get)
) )
); );

@ -20,11 +20,11 @@ export function get_assignment_value(node, { state, visit }) {
return operator === '=' return operator === '='
? /** @type {import('estree').Expression} */ (visit(node.right)) ? /** @type {import('estree').Expression} */ (visit(node.right))
: // turn something like x += 1 into x = x + 1 : // turn something like x += 1 into x = x + 1
b.binary( b.binary(
/** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)),
serialize_get_binding(node.left, state), serialize_get_binding(node.left, state),
/** @type {import('estree').Expression} */ (visit(node.right)) /** @type {import('estree').Expression} */ (visit(node.right))
); );
} else if ( } else if (
node.left.type === 'MemberExpression' && node.left.type === 'MemberExpression' &&
node.left.object.type === 'ThisExpression' && node.left.object.type === 'ThisExpression' &&
@ -35,11 +35,11 @@ export function get_assignment_value(node, { state, visit }) {
return operator === '=' return operator === '='
? /** @type {import('estree').Expression} */ (visit(node.right)) ? /** @type {import('estree').Expression} */ (visit(node.right))
: // turn something like x += 1 into x = x + 1 : // turn something like x += 1 into x = x + 1
b.binary( b.binary(
/** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)),
/** @type {import('estree').Expression} */ (visit(node.left)), /** @type {import('estree').Expression} */ (visit(node.left)),
/** @type {import('estree').Expression} */ (visit(node.right)) /** @type {import('estree').Expression} */ (visit(node.right))
); );
} else { } else {
return /** @type {import('estree').Expression} */ (visit(node.right)); return /** @type {import('estree').Expression} */ (visit(node.right));
} }
@ -217,9 +217,10 @@ function is_expression_async(expression) {
* @param {import('estree').AssignmentExpression} node * @param {import('estree').AssignmentExpression} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, State>} context * @param {import('zimmerframe').Context<import('#compiler').SvelteNode, State>} context
* @param {() => any} fallback * @param {() => any} fallback
* @param {{skip_proxy_and_freeze?: boolean}} [options]
* @returns {import('estree').Expression} * @returns {import('estree').Expression}
*/ */
export function serialize_set_binding(node, context, fallback) { export function serialize_set_binding(node, context, fallback, options) {
const { state, visit } = context; const { state, visit } = context;
if ( if (
@ -242,7 +243,7 @@ export function serialize_set_binding(node, context, fallback) {
const value = path.expression?.(b.id(tmp_id)); const value = path.expression?.(b.id(tmp_id));
const assignment = b.assignment('=', path.node, value); const assignment = b.assignment('=', path.node, value);
original_assignments.push(assignment); original_assignments.push(assignment);
assignments.push(serialize_set_binding(assignment, context, () => assignment)); assignments.push(serialize_set_binding(assignment, context, () => assignment, options));
} }
if (assignments.every((assignment, i) => assignment === original_assignments[i])) { if (assignments.every((assignment, i) => assignment === original_assignments[i])) {
@ -288,7 +289,11 @@ export function serialize_set_binding(node, context, fallback) {
if (private_state !== undefined) { if (private_state !== undefined) {
if (state.in_constructor) { if (state.in_constructor) {
// See if we should wrap value in $.proxy // See if we should wrap value in $.proxy
if (context.state.analysis.runes && should_proxy_or_freeze(value)) { if (
context.state.analysis.runes &&
!options?.skip_proxy_and_freeze &&
should_proxy_or_freeze(value)
) {
const assignment = fallback(); const assignment = fallback();
if (assignment.type === 'AssignmentExpression') { if (assignment.type === 'AssignmentExpression') {
assignment.right = assignment.right =
@ -302,7 +307,9 @@ export function serialize_set_binding(node, context, fallback) {
return b.call( return b.call(
'$.set', '$.set',
left, left,
context.state.analysis.runes && should_proxy_or_freeze(value) context.state.analysis.runes &&
!options?.skip_proxy_and_freeze &&
should_proxy_or_freeze(value)
? private_state.kind === 'frozen_state' ? private_state.kind === 'frozen_state'
? b.call('$.freeze', value) ? b.call('$.freeze', value)
: b.call('$.proxy', value) : b.call('$.proxy', value)
@ -321,6 +328,7 @@ export function serialize_set_binding(node, context, fallback) {
if ( if (
context.state.analysis.runes && context.state.analysis.runes &&
public_state !== undefined && public_state !== undefined &&
!options?.skip_proxy_and_freeze &&
should_proxy_or_freeze(value) should_proxy_or_freeze(value)
) { ) {
const assignment = fallback(); const assignment = fallback();
@ -387,7 +395,9 @@ export function serialize_set_binding(node, context, fallback) {
return b.call( return b.call(
'$.set', '$.set',
b.id(left_name), b.id(left_name),
context.state.analysis.runes && should_proxy_or_freeze(value) context.state.analysis.runes &&
!options?.skip_proxy_and_freeze &&
should_proxy_or_freeze(value)
? b.call('$.proxy', value) ? b.call('$.proxy', value)
: value : value
); );
@ -395,7 +405,9 @@ export function serialize_set_binding(node, context, fallback) {
return b.call( return b.call(
'$.set', '$.set',
b.id(left_name), b.id(left_name),
context.state.analysis.runes && should_proxy_or_freeze(value) context.state.analysis.runes &&
!options?.skip_proxy_and_freeze &&
should_proxy_or_freeze(value)
? b.call('$.freeze', value) ? b.call('$.freeze', value)
: value : value
); );
@ -615,6 +627,7 @@ export function should_proxy_or_freeze(node) {
if ( if (
!node || !node ||
node.type === 'Literal' || node.type === 'Literal' ||
node.type === 'TemplateLiteral' ||
node.type === 'ArrowFunctionExpression' || node.type === 'ArrowFunctionExpression' ||
node.type === 'FunctionExpression' || node.type === 'FunctionExpression' ||
node.type === 'UnaryExpression' || node.type === 'UnaryExpression' ||
@ -623,6 +636,5 @@ export function should_proxy_or_freeze(node) {
) { ) {
return false; return false;
} }
return true; return true;
} }

@ -87,8 +87,11 @@ export const javascript_visitors_runes = {
field.kind === 'state' field.kind === 'state'
? b.call('$.source', should_proxy_or_freeze(init) ? b.call('$.proxy', init) : init) ? b.call('$.source', should_proxy_or_freeze(init) ? b.call('$.proxy', init) : init)
: field.kind === 'frozen_state' : field.kind === 'frozen_state'
? b.call('$.source', should_proxy_or_freeze(init) ? b.call('$.freeze', init) : init) ? b.call(
: b.call('$.derived', b.thunk(init)); '$.source',
should_proxy_or_freeze(init) ? b.call('$.freeze', init) : init
)
: b.call('$.derived', b.thunk(init));
} else { } else {
// if no arguments, we know it's state as `$derived()` is a compile error // if no arguments, we know it's state as `$derived()` is a compile error
value = b.call('$.source'); value = b.call('$.source');

@ -655,15 +655,15 @@ function serialize_element_special_value_attribute(element, node_id, attribute,
// This ensures things stay in sync with the select binding // This ensures things stay in sync with the select binding
// in case of updates to the option value or new values appearing // in case of updates to the option value or new values appearing
b.call('$.selected', node_id) b.call('$.selected', node_id)
]) ])
: needs_option_call : needs_option_call
? b.sequence([ ? b.sequence([
inner_assignment, inner_assignment,
// This ensures a one-way street to the DOM in case it's <select {value}> // This ensures a one-way street to the DOM in case it's <select {value}>
// and not <select bind:value> // and not <select bind:value>
b.call('$.select_option', node_id, value) b.call('$.select_option', node_id, value)
]) ])
: inner_assignment : inner_assignment
); );
if (is_reactive) { if (is_reactive) {
@ -953,7 +953,7 @@ function serialize_inline_component(node, component_name, context) {
: b.call( : b.call(
'$.spread_props', '$.spread_props',
...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p)) ...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))
); );
/** @param {import('estree').Identifier} node_id */ /** @param {import('estree').Identifier} node_id */
let fn = (node_id) => let fn = (node_id) =>
b.call( b.call(
@ -1309,7 +1309,7 @@ function serialize_event_handler(node, { state, visit }) {
/** /**
* Serializes an event handler function of the `on:` directive or an attribute starting with `on` * Serializes an event handler function of the `on:` directive or an attribute starting with `on`
* @param {Pick<import('#compiler').OnDirective, 'name' | 'modifiers' | 'expression' | 'metadata'>} node * @param {{name: string; modifiers: string[]; expression: import('estree').Expression | null; delegated?: import('#compiler').DelegatedEvent | null; }} node
* @param {import('../types.js').ComponentContext} context * @param {import('../types.js').ComponentContext} context
*/ */
function serialize_event(node, context) { function serialize_event(node, context) {
@ -1318,9 +1318,9 @@ function serialize_event(node, context) {
if (node.expression) { if (node.expression) {
let handler = serialize_event_handler(node, context); let handler = serialize_event_handler(node, context);
const event_name = node.name; const event_name = node.name;
const delegated = node.metadata.delegated; const delegated = node.delegated;
if (delegated !== null) { if (delegated != null) {
let delegated_assignment; let delegated_assignment;
if (!state.events.has(event_name)) { if (!state.events.has(event_name)) {
@ -1415,7 +1415,7 @@ function serialize_event_attribute(node, context) {
name: event_name, name: event_name,
expression: node.value[0].expression, expression: node.value[0].expression,
modifiers, modifiers,
metadata: node.metadata delegated: node.metadata.delegated
}, },
context context
); );
@ -1657,6 +1657,11 @@ function serialize_template_literal(values, visit, state) {
if (node.type === 'Text') { if (node.type === 'Text') {
const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1));
last.value.raw += sanitize_template_string(node.data); last.value.raw += sanitize_template_string(node.data);
} else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') {
const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1));
if (node.expression.value != null) {
last.value.raw += sanitize_template_string(node.expression.value + '');
}
} else { } else {
if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) { if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) {
contains_call_expression = true; contains_call_expression = true;
@ -2344,7 +2349,7 @@ export const template_visitors = {
? b.arrow( ? b.arrow(
[b.id('$$anchor')], [b.id('$$anchor')],
/** @type {import('estree').BlockStatement} */ (context.visit(node.fallback)) /** @type {import('estree').BlockStatement} */ (context.visit(node.fallback))
) )
: b.literal(null); : b.literal(null);
const key_function = const key_function =
node.key && ((each_type & EACH_ITEM_REACTIVE) !== 0 || context.state.options.dev) node.key && ((each_type & EACH_ITEM_REACTIVE) !== 0 || context.state.options.dev)
@ -2355,7 +2360,7 @@ export const template_visitors = {
b.return(/** @type {import('estree').Expression} */ (context.visit(node.key))) b.return(/** @type {import('estree').Expression} */ (context.visit(node.key)))
) )
) )
) )
: b.literal(null); : b.literal(null);
if (node.index && each_node_meta.contains_group_binding) { if (node.index && each_node_meta.contains_group_binding) {
@ -2417,7 +2422,7 @@ export const template_visitors = {
? b.arrow( ? b.arrow(
[b.id('$$anchor')], [b.id('$$anchor')],
/** @type {import('estree').BlockStatement} */ (context.visit(node.alternate)) /** @type {import('estree').BlockStatement} */ (context.visit(node.alternate))
) )
: b.literal(null) : b.literal(null)
) )
) )
@ -2436,7 +2441,7 @@ export const template_visitors = {
? b.arrow( ? b.arrow(
[b.id('$$anchor')], [b.id('$$anchor')],
/** @type {import('estree').BlockStatement} */ (context.visit(node.pending)) /** @type {import('estree').BlockStatement} */ (context.visit(node.pending))
) )
: b.literal(null), : b.literal(null),
node.then node.then
? b.arrow( ? b.arrow(
@ -2444,10 +2449,10 @@ export const template_visitors = {
? [ ? [
b.id('$$anchor'), b.id('$$anchor'),
/** @type {import('estree').Pattern} */ (context.visit(node.value)) /** @type {import('estree').Pattern} */ (context.visit(node.value))
] ]
: [b.id('$$anchor')], : [b.id('$$anchor')],
/** @type {import('estree').BlockStatement} */ (context.visit(node.then)) /** @type {import('estree').BlockStatement} */ (context.visit(node.then))
) )
: b.literal(null), : b.literal(null),
node.catch node.catch
? b.arrow( ? b.arrow(
@ -2455,10 +2460,10 @@ export const template_visitors = {
? [ ? [
b.id('$$anchor'), b.id('$$anchor'),
/** @type {import('estree').Pattern} */ (context.visit(node.error)) /** @type {import('estree').Pattern} */ (context.visit(node.error))
] ]
: [b.id('$$anchor')], : [b.id('$$anchor')],
/** @type {import('estree').BlockStatement} */ (context.visit(node.catch)) /** @type {import('estree').BlockStatement} */ (context.visit(node.catch))
) )
: b.literal(null) : b.literal(null)
) )
) )
@ -2608,7 +2613,10 @@ export const template_visitors = {
serialize_set_binding( serialize_set_binding(
assignment, assignment,
context, context,
() => /** @type {import('estree').Expression} */ (context.visit(assignment)) () => /** @type {import('estree').Expression} */ (context.visit(assignment)),
{
skip_proxy_and_freeze: true
}
) )
); );
@ -2877,9 +2885,9 @@ export const template_visitors = {
/** @type {import('estree').Expression} */ (node.expression).type === /** @type {import('estree').Expression} */ (node.expression).type ===
'ObjectExpression' 'ObjectExpression'
? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine ? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.object_pattern(node.expression.properties) b.object_pattern(node.expression.properties)
: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine : // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.array_pattern(node.expression.elements), b.array_pattern(node.expression.elements),
b.member(b.id('$$slotProps'), b.id(node.name)) b.member(b.id('$$slotProps'), b.id(node.name))
), ),
b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node)))) b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
@ -2972,7 +2980,7 @@ export const template_visitors = {
: b.arrow( : b.arrow(
[b.id('$$anchor')], [b.id('$$anchor')],
b.block(create_block(node, 'fallback', node.fragment.nodes, context)) b.block(create_block(node, 'fallback', node.fragment.nodes, context))
); );
const expression = is_default const expression = is_default
? b.member(b.id('$$props'), b.id('children')) ? b.member(b.id('$$props'), b.id('children'))

@ -187,10 +187,12 @@ function process_children(nodes, parent, { visit, state }) {
const node = sequence[i]; const node = sequence[i];
if (node.type === 'Text' || node.type === 'Comment') { if (node.type === 'Text' || node.type === 'Comment') {
let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1));
last.value.raw += last.value.raw += node.type === 'Comment' ? `<!--${node.data}-->` : escape_html(node.data);
node.type === 'Comment' } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') {
? `<!--${node.data}-->` let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1));
: sanitize_template_string(escape_html(node.data)); if (node.expression.value != null) {
last.value.raw += escape_html(node.expression.value + '');
}
} else if (node.type === 'Anchor') { } else if (node.type === 'Anchor') {
expressions.push(node.id); expressions.push(node.id);
quasis.push(b.quasi('', i + 1 === sequence.length)); quasis.push(b.quasi('', i + 1 === sequence.length));
@ -349,11 +351,11 @@ function get_assignment_value(node, { state, visit }) {
return operator === '=' return operator === '='
? /** @type {import('estree').Expression} */ (visit(node.right)) ? /** @type {import('estree').Expression} */ (visit(node.right))
: // turn something like x += 1 into x = x + 1 : // turn something like x += 1 into x = x + 1
b.binary( b.binary(
/** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)),
serialize_get_binding(node.left, state), serialize_get_binding(node.left, state),
/** @type {import('estree').Expression} */ (visit(node.right)) /** @type {import('estree').Expression} */ (visit(node.right))
); );
} else { } else {
return /** @type {import('estree').Expression} */ (visit(node.right)); return /** @type {import('estree').Expression} */ (visit(node.right));
} }
@ -778,7 +780,7 @@ function serialize_element_spread_attributes(
b.id('join') b.id('join')
), ),
b.literal(' ') b.literal(' ')
) )
: b.literal(''); : b.literal('');
args.push( args.push(
b.object([ b.object([
@ -931,7 +933,7 @@ function serialize_inline_component(node, component_name, context) {
: b.call( : b.call(
'$.spread_props', '$.spread_props',
b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))) b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p)))
); );
/** @type {import('estree').Statement} */ /** @type {import('estree').Statement} */
let statement = b.stmt( let statement = b.stmt(
@ -940,7 +942,7 @@ function serialize_inline_component(node, component_name, context) {
? b.call( ? b.call(
'$.validate_component', '$.validate_component',
typeof component_name === 'string' ? b.id(component_name) : component_name typeof component_name === 'string' ? b.id(component_name) : component_name
) )
: component_name, : component_name,
b.id('$$payload'), b.id('$$payload'),
props_expression props_expression
@ -1032,7 +1034,7 @@ const javascript_visitors_legacy = {
'$.value_or_fallback', '$.value_or_fallback',
prop, prop,
/** @type {import('estree').Expression} */ (visit(declarator.init)) /** @type {import('estree').Expression} */ (visit(declarator.init))
) )
: prop; : prop;
declarations.push(b.declarator(declarator.id, init)); declarations.push(b.declarator(declarator.id, init));
@ -1170,7 +1172,7 @@ const template_visitors = {
template: [], template: [],
init: [] init: []
} }
} }
: { ...context, state }; : { ...context, state };
const { hoisted, trimmed } = clean_nodes( const { hoisted, trimmed } = clean_nodes(
@ -1494,9 +1496,9 @@ const template_visitors = {
b.let( b.let(
node.expression.type === 'ObjectExpression' node.expression.type === 'ObjectExpression'
? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine ? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.object_pattern(node.expression.properties) b.object_pattern(node.expression.properties)
: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine : // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.array_pattern(node.expression.elements), b.array_pattern(node.expression.elements),
b.member(b.id('$$slotProps'), b.id(node.name)) b.member(b.id('$$slotProps'), b.id(node.name))
), ),
b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node)))) b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
@ -1712,12 +1714,12 @@ function serialize_element_attributes(node, context) {
? b.call( ? b.call(
b.member(attribute.expression, b.id('includes')), b.member(attribute.expression, b.id('includes')),
serialize_attribute_value(value_attribute.value, context) serialize_attribute_value(value_attribute.value, context)
) )
: b.binary( : b.binary(
'===', '===',
attribute.expression, attribute.expression,
serialize_attribute_value(value_attribute.value, context) serialize_attribute_value(value_attribute.value, context)
), ),
metadata: { metadata: {
contains_call_expression: false, contains_call_expression: false,
dynamic: false dynamic: false

@ -198,7 +198,7 @@ export function infer_namespace(namespace, parent, nodes, path) {
const parent_node = const parent_node =
parent.type === 'Fragment' parent.type === 'Fragment'
? // Messy: We know that Fragment calls create_block directly, so we can do this here ? // Messy: We know that Fragment calls create_block directly, so we can do this here
path.at(-1) path.at(-1)
: parent; : parent;
if ( if (

@ -318,7 +318,7 @@ async function process_markup(process, source) {
string: processed.code, string: processed.code,
map: processed.map map: processed.map
? // TODO: can we use decode_sourcemap? ? // TODO: can we use decode_sourcemap?
typeof processed.map === 'string' typeof processed.map === 'string'
? JSON.parse(processed.map) ? JSON.parse(processed.map)
: processed.map : processed.map
: undefined, : undefined,

@ -21,6 +21,13 @@ interface BaseElement extends BaseNode {
children: Array<LegacyElementLike>; children: Array<LegacyElementLike>;
} }
export interface LegacyRoot extends BaseNode {
html: LegacySvelteNode;
css?: any;
instance?: any;
module?: any;
}
export interface LegacyAction extends BaseNode { export interface LegacyAction extends BaseNode {
type: 'Action'; type: 'Action';
/** The 'x' in `use:x` */ /** The 'x' in `use:x` */

@ -204,9 +204,6 @@ export interface OnDirective extends BaseNode {
/** The 'y' in `on:x={y}` */ /** The 'y' in `on:x={y}` */
expression: null | Expression; expression: null | Expression;
modifiers: string[]; // TODO specify modifiers: string[]; // TODO specify
metadata: {
delegated: null | DelegatedEvent;
};
} }
export type DelegatedEvent = export type DelegatedEvent =
@ -290,12 +287,6 @@ export interface RegularElement extends BaseElement {
svg: boolean; svg: boolean;
/** `true` if contains a SpreadAttribute */ /** `true` if contains a SpreadAttribute */
has_spread: boolean; has_spread: boolean;
/**
* `true` if events on this element can theoretically be delegated. This doesn't necessarily mean that
* a specific event will be delegated, as there are other factors which affect the final outcome.
* `null` only until it was determined whether this element can be delegated or not.
*/
can_delegate_events: boolean | null;
}; };
} }

@ -258,7 +258,7 @@ export function combine_sourcemaps(filename, sourcemap_list) {
sourcemap_list, sourcemap_list,
() => null, () => null,
true // skip optional field `sourcesContent` true // skip optional field `sourcesContent`
) )
: remapping( : remapping(
// use loader interface // use loader interface
sourcemap_list[0], // last map sourcemap_list[0], // last map
@ -271,7 +271,7 @@ export function combine_sourcemaps(filename, sourcemap_list) {
} }
}, },
true true
); );
if (!map.file) delete map.file; // skip optional field `file` if (!map.file) delete map.file; // skip optional field `file`
// When source maps are combined and the leading map is empty, sources is not set. // When source maps are combined and the leading map is empty, sources is not set.
// Add the filename to the empty array in this case. // Add the filename to the empty array in this case.

@ -59,10 +59,10 @@ export function bounceOut(t) {
return t < a return t < a
? 7.5625 * t2 ? 7.5625 * t2
: t < b : t < b
? 9.075 * t2 - 9.9 * t + 3.4 ? 9.075 * t2 - 9.9 * t + 3.4
: t < c : t < c
? ca * t2 - cb * t + cc ? ca * t2 - cb * t + cc
: 10.8 * t * t - 20.52 * t + 10.72; : 10.8 * t * t - 20.52 * t + 10.72;
} }
/** /**
@ -180,8 +180,8 @@ export function expoInOut(t) {
return t === 0.0 || t === 1.0 return t === 0.0 || t === 1.0
? t ? t
: t < 0.5 : t < 0.5
? +0.5 * Math.pow(2.0, 20.0 * t - 10.0) ? +0.5 * Math.pow(2.0, 20.0 * t - 10.0)
: -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0; : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0;
} }
/** /**

@ -25,7 +25,6 @@ import {
mutable_source, mutable_source,
push_destroy_fn, push_destroy_fn,
render_effect, render_effect,
schedule_task,
set_signal_value, set_signal_value,
source source
} from './runtime.js'; } from './runtime.js';
@ -134,8 +133,8 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
array = is_array(maybe_array) array = is_array(maybe_array)
? maybe_array ? maybe_array
: maybe_array == null : maybe_array == null
? [] ? []
: Array.from(maybe_array); : Array.from(maybe_array);
if (key_fn !== null) { if (key_fn !== null) {
keys = array.map(key_fn); keys = array.map(key_fn);
} else if ((flags & EACH_KEYED) === 0) { } else if ((flags & EACH_KEYED) === 0) {
@ -486,6 +485,17 @@ function reconcile_tracked_array(
key = is_computed_key ? keys[a] : item; key = is_computed_key ? keys[a] : item;
map_set(item_index, key, a); map_set(item_index, key, a);
} }
// If keys are animated, we need to do updates before actual moves
if (is_animated) {
for (b = start; b <= a_end; ++b) {
a = map_get(item_index, /** @type {V} */ (a_blocks[b].k));
if (a !== undefined) {
item = array[a];
block = a_blocks[b];
update_each_item_block(block, item, a, flags);
}
}
}
for (b = start; b <= a_end; ++b) { for (b = start; b <= a_end; ++b) {
a = map_get(item_index, /** @type {V} */ (a_blocks[b].k)); a = map_get(item_index, /** @type {V} */ (a_blocks[b].k));
block = a_blocks[b]; block = a_blocks[b];
@ -501,22 +511,9 @@ function reconcile_tracked_array(
if (pos === MOVED_BLOCK) { if (pos === MOVED_BLOCK) {
mark_lis(sources); mark_lis(sources);
} }
// If keys are animated, we need to do updates before actual moves
var should_create;
if (is_animated) {
var i = b_length;
while (i-- > 0) {
b_end = i + start;
a = sources[i];
if (pos === MOVED_BLOCK) {
block = b_blocks[b_end];
item = array[b_end];
update_each_item_block(block, item, b_end, flags);
}
}
}
var last_block; var last_block;
var last_sibling; var last_sibling;
var should_create;
while (b_length-- > 0) { while (b_length-- > 0) {
b_end = b_length + start; b_end = b_length + start;
a = sources[b_length]; a = sources[b_length];
@ -715,7 +712,7 @@ function update_each_item_block(block, item, index, type) {
// Handle each item animations // Handle each item animations
const each_animation = block.a; const each_animation = block.a;
if (transitions !== null && (type & EACH_KEYED) !== 0 && each_animation !== null) { if (transitions !== null && (type & EACH_KEYED) !== 0 && each_animation !== null) {
each_animation(block, transitions, index, index_is_reactive); each_animation(block, transitions);
} }
if (index_is_reactive) { if (index_is_reactive) {
set_signal_value(/** @type {import('./types.js').Signal<number>} */ (block.i), index); set_signal_value(/** @type {import('./types.js').Signal<number>} */ (block.i), index);
@ -780,8 +777,8 @@ function each_item_block(item, key, index, render_fn, flags) {
const item_value = each_item_not_reactive const item_value = each_item_not_reactive
? item ? item
: (flags & EACH_IS_IMMUTABLE) === 0 : (flags & EACH_IS_IMMUTABLE) === 0
? mutable_source(item) ? mutable_source(item)
: source(item); : source(item);
const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index); const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
const block = create_each_item_block(item_value, index_value, key); const block = create_each_item_block(item_value, index_value, key);

@ -319,7 +319,15 @@ export function event(event_name, dom, handler, capture, passive) {
capture, capture,
passive passive
}; };
const target_handler = handler; /**
* @this {EventTarget}
*/
function target_handler(/** @type {Event} */ event) {
handle_event_propagation(dom, event);
if (!event.cancelBubble) {
return handler.call(this, event);
}
}
dom.addEventListener(event_name, target_handler, options); dom.addEventListener(event_name, target_handler, options);
// @ts-ignore // @ts-ignore
if (dom === document.body || dom === window || dom === document) { if (dom === document.body || dom === window || dom === document) {
@ -873,8 +881,8 @@ export function bind_resize_observer(dom, type, update) {
type === 'contentRect' || type === 'contentBoxSize' type === 'contentRect' || type === 'contentBoxSize'
? resize_observer_content_box ? resize_observer_content_box
: type === 'borderBoxSize' : type === 'borderBoxSize'
? resize_observer_border_box ? resize_observer_border_box
: resize_observer_device_pixel_content_box; : resize_observer_device_pixel_content_box;
const unsub = observer.observe(dom, /** @param {any} entry */ (entry) => update(entry[type])); const unsub = observer.observe(dom, /** @param {any} entry */ (entry) => update(entry[type]));
render_effect(() => unsub); render_effect(() => unsub);
} }
@ -1375,7 +1383,10 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
/** @type {null | import('./types.js').TemplateNode | Array<import('./types.js').TemplateNode>} */ /** @type {null | import('./types.js').TemplateNode | Array<import('./types.js').TemplateNode>} */
let alternate_dom = null; let alternate_dom = null;
let has_mounted = false; let has_mounted = false;
let has_mounted_branch = false; /**
* @type {import("./types.js").EffectSignal | null}
*/
let current_branch_effect = null;
const if_effect = render_effect( const if_effect = render_effect(
() => { () => {
@ -1432,20 +1443,24 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
); );
// Managed effect // Managed effect
const consequent_effect = render_effect( const consequent_effect = render_effect(
() => { (
if (consequent_dom !== null) { /** @type {any} */ _,
/** @type {import("./types.js").EffectSignal | null} */ consequent_effect
) => {
const result = block.v;
if (!result && consequent_dom !== null) {
remove(consequent_dom); remove(consequent_dom);
consequent_dom = null; consequent_dom = null;
} }
if (block.v) { if (result && current_branch_effect !== consequent_effect) {
consequent_fn(anchor_node); consequent_fn(anchor_node);
if (!has_mounted_branch) { if (current_branch_effect === null) {
// Restore previous fragment so that Svelte continues to operate in hydration mode // Restore previous fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment(previous_hydration_fragment); set_current_hydration_fragment(previous_hydration_fragment);
has_mounted_branch = true;
} }
current_branch_effect = consequent_effect;
consequent_dom = block.d;
} }
consequent_dom = block.d;
block.d = null; block.d = null;
}, },
block, block,
@ -1454,22 +1469,26 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
block.ce = consequent_effect; block.ce = consequent_effect;
// Managed effect // Managed effect
const alternate_effect = render_effect( const alternate_effect = render_effect(
() => { (
if (alternate_dom !== null) { /** @type {any} */ _,
/** @type {import("./types.js").EffectSignal | null} */ alternate_effect
) => {
const result = block.v;
if (result && alternate_dom !== null) {
remove(alternate_dom); remove(alternate_dom);
alternate_dom = null; alternate_dom = null;
} }
if (!block.v) { if (!result && current_branch_effect !== alternate_effect) {
if (alternate_fn !== null) { if (alternate_fn !== null) {
alternate_fn(anchor_node); alternate_fn(anchor_node);
} }
if (!has_mounted_branch) { if (current_branch_effect === null) {
// Restore previous fragment so that Svelte continues to operate in hydration mode // Restore previous fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment(previous_hydration_fragment); set_current_hydration_fragment(previous_hydration_fragment);
has_mounted_branch = true;
} }
current_branch_effect = alternate_effect;
alternate_dom = block.d;
} }
alternate_dom = block.d;
block.d = null; block.d = null;
}, },
block, block,
@ -1587,8 +1606,8 @@ export function element(anchor_node, tag_fn, render_fn, is_svg = false) {
? current_hydration_fragment !== null ? current_hydration_fragment !== null
? /** @type {HTMLElement | SVGElement} */ (current_hydration_fragment[0]) ? /** @type {HTMLElement | SVGElement} */ (current_hydration_fragment[0])
: is_svg : is_svg
? document.createElementNS('http://www.w3.org/2000/svg', tag) ? document.createElementNS('http://www.w3.org/2000/svg', tag)
: document.createElement(tag) : document.createElement(tag)
: null; : null;
const prev_element = element; const prev_element = element;
if (prev_element !== null) { if (prev_element !== null) {
@ -2803,7 +2822,7 @@ export function mount(component, options) {
).c = options.context; ).c = options.context;
} }
// @ts-expect-error the public typings are not what the actual function looks like // @ts-expect-error the public typings are not what the actual function looks like
accessors = component(anchor, options.props || {}, options.events || {}); accessors = component(anchor, options.props || {});
if (options.context) { if (options.context) {
pop(); pop();
} }
@ -2850,7 +2869,7 @@ export function mount(component, options) {
PassiveDelegatedEvents.includes(event_name) PassiveDelegatedEvents.includes(event_name)
? { ? {
passive: true passive: true
} }
: undefined : undefined
); );
// The document listener ensures we catch events that originate from elements that were // The document listener ensures we catch events that originate from elements that were
@ -2861,7 +2880,7 @@ export function mount(component, options) {
PassiveDelegatedEvents.includes(event_name) PassiveDelegatedEvents.includes(event_name)
? { ? {
passive: true passive: true
} }
: undefined : undefined
); );
} }

@ -38,6 +38,7 @@ let current_scheduler_mode = FLUSH_MICROTASK;
// Used for handling scheduling // Used for handling scheduling
let is_micro_task_queued = false; let is_micro_task_queued = false;
let is_task_queued = false; let is_task_queued = false;
let is_raf_queued = false;
// Used for $inspect // Used for $inspect
export let is_batching_effect = false; export let is_batching_effect = false;
@ -52,7 +53,7 @@ let current_queued_effects = [];
/** @type {Array<() => void>} */ /** @type {Array<() => void>} */
let current_queued_tasks = []; let current_queued_tasks = [];
/** @type {Array<() => void>} */ /** @type {Array<() => void>} */
let current_queued_microtasks = []; let current_raf_tasks = [];
let flush_count = 0; let flush_count = 0;
// Handle signal reactivity tree dependencies and consumer // Handle signal reactivity tree dependencies and consumer
@ -213,6 +214,8 @@ function create_computation_signal(flags, value, block) {
f: flags, f: flags,
// init // init
i: null, i: null,
// level
l: 0,
// references // references
r: null, r: null,
// value // value
@ -237,6 +240,8 @@ function create_computation_signal(flags, value, block) {
e: null, e: null,
// flags // flags
f: flags, f: flags,
// level
l: 0,
// init // init
i: null, i: null,
// references // references
@ -341,9 +346,13 @@ function execute_signal_fn(signal) {
try { try {
let res; let res;
if (is_render_effect) { if (is_render_effect) {
res = /** @type {(block: import('./types.js').Block) => V} */ (init)( res =
/** @type {import('./types.js').Block} */ (signal.b) /** @type {(block: import('./types.js').Block, signal: import('./types.js').Signal) => V} */ (
); init
)(
/** @type {import('./types.js').Block} */ (signal.b),
/** @type {import('./types.js').Signal} */ (signal)
);
} else { } else {
res = /** @type {() => V} */ (init)(); res = /** @type {() => V} */ (init)();
} }
@ -552,7 +561,7 @@ function infinite_loop_guard() {
'ERR_SVELTE_TOO_MANY_UPDATES' + 'ERR_SVELTE_TOO_MANY_UPDATES' +
(DEV (DEV
? ': Maximum update depth exceeded. This can happen when a reactive block or effect ' + ? ': Maximum update depth exceeded. This can happen when a reactive block or effect ' +
'repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops.' 'repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops.'
: '') : '')
); );
} }
@ -586,11 +595,6 @@ function flush_queued_effects(effects) {
function process_microtask() { function process_microtask() {
is_micro_task_queued = false; is_micro_task_queued = false;
if (current_queued_microtasks.length > 0) {
const tasks = current_queued_microtasks.slice();
current_queued_microtasks = [];
run_all(tasks);
}
if (flush_count > 101) { if (flush_count > 101) {
return; return;
} }
@ -624,8 +628,53 @@ export function schedule_effect(signal, sync) {
} }
if ((flags & EFFECT) !== 0) { if ((flags & EFFECT) !== 0) {
current_queued_effects.push(signal); current_queued_effects.push(signal);
// Prevent any nested user effects from potentially triggering
// before this effect is scheduled. We know they will be destroyed
// so we can make them inert to avoid having to find them in the
// queue and remove them.
if ((flags & MANAGED) === 0) {
mark_subtree_children_inert(signal, true);
}
} else { } else {
current_queued_pre_and_render_effects.push(signal); // We need to ensure we insert the signal in the right topological order. In other words,
// we need to evaluate where to insert the signal based off its level and whether or not it's
// a pre-effect and within the same block. By checking the signals in the queue in reverse order
// we can find the right place quickly. TODO: maybe opt to use a linked list rather than an array
// for these operations.
const length = current_queued_pre_and_render_effects.length;
let should_append = length === 0;
if (!should_append) {
const target_level = signal.l;
const target_block = signal.b;
const is_pre_effect = (flags & PRE_EFFECT) !== 0;
let target_signal;
let is_target_pre_effect;
let i = length;
while (true) {
target_signal = current_queued_pre_and_render_effects[--i];
if (target_signal.l <= target_level) {
if (i + 1 === length) {
should_append = true;
} else {
is_target_pre_effect = (target_signal.f & PRE_EFFECT) !== 0;
if (target_signal.b !== target_block || (is_target_pre_effect && !is_pre_effect)) {
i++;
}
current_queued_pre_and_render_effects.splice(i, 0, signal);
}
break;
}
if (i === 0) {
current_queued_pre_and_render_effects.unshift(signal);
break;
}
}
}
if (should_append) {
current_queued_pre_and_render_effects.push(signal);
}
} }
} }
} }
@ -637,6 +686,13 @@ function process_task() {
run_all(tasks); run_all(tasks);
} }
function process_raf_task() {
is_raf_queued = false;
const tasks = current_raf_tasks.slice();
current_raf_tasks = [];
run_all(tasks);
}
/** /**
* @param {() => void} fn * @param {() => void} fn
* @returns {void} * @returns {void}
@ -653,12 +709,12 @@ export function schedule_task(fn) {
* @param {() => void} fn * @param {() => void} fn
* @returns {void} * @returns {void}
*/ */
export function schedule_microtask(fn) { export function schedule_raf_task(fn) {
if (!is_micro_task_queued) { if (!is_raf_queued) {
is_micro_task_queued = true; is_raf_queued = true;
queueMicrotask(process_microtask); requestAnimationFrame(process_raf_task);
} }
current_queued_microtasks.push(fn); current_raf_tasks.push(fn);
} }
/** /**
@ -721,8 +777,8 @@ export function flushSync(fn) {
if (current_queued_pre_and_render_effects.length > 0 || effects.length > 0) { if (current_queued_pre_and_render_effects.length > 0 || effects.length > 0) {
flushSync(); flushSync();
} }
if (is_micro_task_queued) { if (is_raf_queued) {
process_microtask(); process_raf_task();
} }
if (is_task_queued) { if (is_task_queued) {
process_task(); process_task();
@ -1010,9 +1066,26 @@ export function mutate_store(store, expression, new_value) {
/** /**
* @param {import('./types.js').ComputationSignal} signal * @param {import('./types.js').ComputationSignal} signal
* @param {boolean} inert * @param {boolean} inert
* @param {Set<import('./types.js').Block>} [visited_blocks]
* @returns {void}
*/
function mark_subtree_children_inert(signal, inert, visited_blocks) {
const references = signal.r;
if (references !== null) {
let i;
for (i = 0; i < references.length; i++) {
mark_subtree_inert(references[i], inert, visited_blocks);
}
}
}
/**
* @param {import('./types.js').ComputationSignal} signal
* @param {boolean} inert
* @param {Set<import('./types.js').Block>} [visited_blocks]
* @returns {void} * @returns {void}
*/ */
export function mark_subtree_inert(signal, inert) { export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) {
const flags = signal.f; const flags = signal.f;
const is_already_inert = (flags & INERT) !== 0; const is_already_inert = (flags & INERT) !== 0;
if (is_already_inert !== inert) { if (is_already_inert !== inert) {
@ -1022,34 +1095,33 @@ export function mark_subtree_inert(signal, inert) {
} }
// Nested if block effects // Nested if block effects
const block = signal.b; const block = signal.b;
if (block !== null) { if (block !== null && !visited_blocks.has(block)) {
visited_blocks.add(block);
const type = block.t; const type = block.t;
if (type === IF_BLOCK) { if (type === IF_BLOCK) {
const condition_effect = block.e;
if (condition_effect !== null && block !== current_block) {
mark_subtree_inert(condition_effect, inert);
}
const consequent_effect = block.ce; const consequent_effect = block.ce;
if (consequent_effect !== null) { if (consequent_effect !== null && block.v) {
mark_subtree_inert(consequent_effect, inert); mark_subtree_inert(consequent_effect, inert, visited_blocks);
} }
const alternate_effect = block.ae; const alternate_effect = block.ae;
if (alternate_effect !== null) { if (alternate_effect !== null && !block.v) {
mark_subtree_inert(alternate_effect, inert); mark_subtree_inert(alternate_effect, inert, visited_blocks);
} }
} else if (type === EACH_BLOCK) { } else if (type === EACH_BLOCK) {
const items = block.v; const items = block.v;
for (let { e: each_item_effect } of items) { for (let { e: each_item_effect } of items) {
if (each_item_effect !== null) { if (each_item_effect !== null) {
mark_subtree_inert(each_item_effect, inert); mark_subtree_inert(each_item_effect, inert, visited_blocks);
} }
} }
} }
} }
} }
const references = signal.r; mark_subtree_children_inert(signal, inert, visited_blocks);
if (references !== null) {
let i;
for (i = 0; i < references.length; i++) {
mark_subtree_inert(references[i], inert);
}
}
} }
/** /**
@ -1109,8 +1181,8 @@ export function set_signal_value(signal, value) {
'ERR_SVELTE_UNSAFE_MUTATION' + 'ERR_SVELTE_UNSAFE_MUTATION' +
(DEV (DEV
? ": Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. " + ? ": Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. " +
'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' + 'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' +
'to be reactive do not use the "$state" rune for that declaration.' 'to be reactive do not use the "$state" rune for that declaration.'
: '') : '')
); );
} }
@ -1280,12 +1352,15 @@ function internal_create_effect(type, init, sync, block, schedule) {
const signal = create_computation_signal(type | DIRTY, null, block); const signal = create_computation_signal(type | DIRTY, null, block);
signal.i = init; signal.i = init;
signal.x = current_component_context; signal.x = current_component_context;
if (current_effect !== null) {
signal.l = current_effect.l + 1;
if ((type & MANAGED) === 0) {
push_reference(current_effect, signal);
}
}
if (schedule) { if (schedule) {
schedule_effect(signal, sync); schedule_effect(signal, sync);
} }
if (current_effect !== null && (type & MANAGED) === 0) {
push_reference(current_effect, signal);
}
return signal; return signal;
} }
@ -1621,7 +1696,7 @@ export function safe_not_equal(a, b) {
// eslint-disable-next-line eqeqeq // eslint-disable-next-line eqeqeq
return a != a return a != a
? // eslint-disable-next-line eqeqeq ? // eslint-disable-next-line eqeqeq
b == b b == b
: a !== b || (a !== null && typeof a === 'object') || typeof a === 'function'; : a !== b || (a !== null && typeof a === 'object') || typeof a === 'function';
} }

@ -21,7 +21,7 @@ import {
managed_effect, managed_effect,
managed_pre_effect, managed_pre_effect,
mark_subtree_inert, mark_subtree_inert,
schedule_microtask, schedule_raf_task,
untrack untrack
} from './runtime.js'; } from './runtime.js';
import { raf } from './timing.js'; import { raf } from './timing.js';
@ -369,6 +369,47 @@ function create_transition(dom, init, direction, effect) {
}, },
// out // out
o() { o() {
// @ts-ignore
const has_keyed_transition = dom.__animate;
// If we're outroing an element that has an animation, then we need to fix
// its position to ensure it behaves nicely without causing layout shift.
if (has_keyed_transition) {
const style = getComputedStyle(dom);
const position = style.position;
if (position !== 'absolute' && position !== 'fixed') {
const { width, height } = style;
const a = dom.getBoundingClientRect();
dom.style.position = 'absolute';
dom.style.width = width;
dom.style.height = height;
const b = dom.getBoundingClientRect();
if (a.left !== b.left || a.top !== b.top) {
const translate = `translate(${a.left - b.left}px, ${a.top - b.top}px)`;
const existing_transform = style.transform;
if (existing_transform === 'none') {
dom.style.transform = translate;
} else {
// Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However,
// because we're now using Web Animations, we can't do that as it won't work properly if the
// animation is also making use of the same transformations. So instead, we apply an
// instantaneous animation and pause it on the first frame, just applying the same behavior.
// We also need to take into consideration matrix transforms and how they might combine with
// an existing behavior that is already in progress (such as scale).
// > Follow the white rabbit.
const transform = existing_transform.startsWith('matrix(1,')
? translate
: `matrix(1,0,0,1,0,0)`;
const frame = {
transform
};
const animation = dom.animate([frame, frame], { duration: 1 });
animation.pause();
}
}
}
}
const needs_reverse = direction === 'both' && curr_direction !== 'out'; const needs_reverse = direction === 'both' && curr_direction !== 'out';
curr_direction = 'out'; curr_direction = 'out';
if (animation === null || cancelled) { if (animation === null || cancelled) {
@ -432,13 +473,18 @@ function is_transition_block(block) {
export function bind_transition(dom, get_transition_fn, props_fn, direction, global) { export function bind_transition(dom, get_transition_fn, props_fn, direction, global) {
const transition_effect = /** @type {import('./types.js').EffectSignal} */ (current_effect); const transition_effect = /** @type {import('./types.js').EffectSignal} */ (current_effect);
const block = current_block; const block = current_block;
const is_keyed_transition = direction === 'key';
let can_show_intro_on_mount = true; let can_show_intro_on_mount = true;
let can_apply_lazy_transitions = false; let can_apply_lazy_transitions = false;
if (is_keyed_transition) {
// @ts-ignore
dom.__animate = true;
}
/** @type {import('./types.js').Block | null} */ /** @type {import('./types.js').Block | null} */
let transition_block = block; let transition_block = block;
while (transition_block !== null) { main: while (transition_block !== null) {
if (is_transition_block(transition_block)) { if (is_transition_block(transition_block)) {
if (transition_block.t === EACH_ITEM_BLOCK) { if (transition_block.t === EACH_ITEM_BLOCK) {
// Lazily apply the each block transition // Lazily apply the each block transition
@ -446,22 +492,31 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
transition_block.a = each_item_animate; transition_block.a = each_item_animate;
transition_block = transition_block.p; transition_block = transition_block.p;
} else if (transition_block.t === AWAIT_BLOCK && transition_block.n /* pending */) { } else if (transition_block.t === AWAIT_BLOCK && transition_block.n /* pending */) {
can_show_intro_on_mount = false; can_show_intro_on_mount = true;
} else if (transition_block.t === IF_BLOCK) { } else if (transition_block.t === IF_BLOCK) {
transition_block.r = if_block_transition; transition_block.r = if_block_transition;
if (can_show_intro_on_mount) {
/** @type {import('./types.js').Block | null} */
let if_block = transition_block;
while (if_block.t === IF_BLOCK) {
// If we have an if block parent that is currently falsy then
// we can show the intro on mount as long as that block is mounted
if (if_block.e !== null && !if_block.v) {
can_show_intro_on_mount = true;
break main;
}
if_block = if_block.p;
}
}
} }
if (!can_apply_lazy_transitions && can_show_intro_on_mount) { if (!can_apply_lazy_transitions && can_show_intro_on_mount) {
can_show_intro_on_mount = transition_block.e === null; can_show_intro_on_mount = transition_block.e !== null;
} }
if (!can_show_intro_on_mount || !global) { if (can_show_intro_on_mount || !global) {
can_apply_lazy_transitions = true; can_apply_lazy_transitions = true;
} }
} else if ( } else if (transition_block.t === ROOT_BLOCK && !can_apply_lazy_transitions) {
!can_apply_lazy_transitions && can_show_intro_on_mount = transition_block.e !== null || transition_block.i;
transition_block.t === ROOT_BLOCK &&
(transition_block.e !== null || transition_block.i)
) {
can_show_intro_on_mount = false;
} }
transition_block = transition_block.p; transition_block = transition_block.p;
} }
@ -470,7 +525,9 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
let transition; let transition;
effect(() => { effect(() => {
let already_mounted = false;
if (transition !== undefined) { if (transition !== undefined) {
already_mounted = true;
// Destroy any existing transitions first // Destroy any existing transitions first
transition.x(); transition.x();
} }
@ -479,23 +536,23 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
const init = (from) => const init = (from) =>
untrack(() => { untrack(() => {
const props = props_fn === null ? {} : props_fn(); const props = props_fn === null ? {} : props_fn();
return direction === 'key' return is_keyed_transition
? /** @type {import('./types.js').AnimateFn<any>} */ (transition_fn)( ? /** @type {import('./types.js').AnimateFn<any>} */ (transition_fn)(
dom, dom,
{ from: /** @type {DOMRect} */ (from), to: dom.getBoundingClientRect() }, { from: /** @type {DOMRect} */ (from), to: dom.getBoundingClientRect() },
props, props,
{} {}
) )
: /** @type {import('./types.js').TransitionFn<any>} */ (transition_fn)(dom, props, { : /** @type {import('./types.js').TransitionFn<any>} */ (transition_fn)(dom, props, {
direction direction
}); });
}); });
transition = create_transition(dom, init, direction, transition_effect); transition = create_transition(dom, init, direction, transition_effect);
const is_intro = direction === 'in'; const is_intro = direction === 'in';
const show_intro = !can_show_intro_on_mount && (is_intro || direction === 'both'); const show_intro = can_show_intro_on_mount && (is_intro || direction === 'both');
if (show_intro) { if (show_intro && !already_mounted) {
transition.p = transition.i(); transition.p = transition.i();
} }
@ -503,7 +560,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
destroy_signal(effect); destroy_signal(effect);
dom.inert = false; dom.inert = false;
if (show_intro) { if (show_intro && !already_mounted) {
transition.in(); transition.in();
} }
@ -547,6 +604,7 @@ export function trigger_transitions(transitions, target_direction, from) {
const outros = []; const outros = [];
for (const transition of transitions) { for (const transition of transitions) {
const direction = transition.r; const direction = transition.r;
const effect = transition.e;
if (target_direction === 'in') { if (target_direction === 'in') {
if (direction === 'in' || direction === 'both') { if (direction === 'in' || direction === 'both') {
transition.in(); transition.in();
@ -554,7 +612,7 @@ export function trigger_transitions(transitions, target_direction, from) {
transition.c(); transition.c();
} }
transition.d.inert = false; transition.d.inert = false;
mark_subtree_inert(transition.e, false); mark_subtree_inert(effect, false);
} else if (target_direction === 'key') { } else if (target_direction === 'key') {
if (direction === 'key') { if (direction === 'key') {
transition.p = transition.i(/** @type {DOMRect} */ (from)); transition.p = transition.i(/** @type {DOMRect} */ (from));
@ -566,7 +624,7 @@ export function trigger_transitions(transitions, target_direction, from) {
outros.push(transition.o); outros.push(transition.o);
} }
transition.d.inert = true; transition.d.inert = true;
mark_subtree_inert(transition.e, true); mark_subtree_inert(effect, true);
} }
} }
if (outros.length > 0) { if (outros.length > 0) {
@ -599,7 +657,8 @@ function if_block_transition(transition) {
const c = /** @type {Set<import('./types.js').Transition>} */ (consequent_transitions); const c = /** @type {Set<import('./types.js').Transition>} */ (consequent_transitions);
c.delete(transition); c.delete(transition);
if (c.size === 0) { if (c.size === 0) {
execute_effect(/** @type {import('./types.js').EffectSignal} */ (block.ce)); const consequent_effect = block.ce;
execute_effect(/** @type {import('./types.js').EffectSignal} */ (consequent_effect));
} }
}); });
} else { } else {
@ -612,7 +671,8 @@ function if_block_transition(transition) {
const a = /** @type {Set<import('./types.js').Transition>} */ (alternate_transitions); const a = /** @type {Set<import('./types.js').Transition>} */ (alternate_transitions);
a.delete(transition); a.delete(transition);
if (a.size === 0) { if (a.size === 0) {
execute_effect(/** @type {import('./types.js').EffectSignal} */ (block.ae)); const alternate_effect = block.ae;
execute_effect(/** @type {import('./types.js').EffectSignal} */ (alternate_effect));
} }
}); });
} }
@ -646,7 +706,8 @@ function each_item_transition(transition) {
transitions.delete(transition); transitions.delete(transition);
if (transition.r !== 'key') { if (transition.r !== 'key') {
for (let other of transitions) { for (let other of transitions) {
if (other.r === 'key' || other.r === 'in') { const type = other.r;
if (type === 'key' || type === 'in') {
transitions.delete(other); transitions.delete(other);
} }
} }
@ -664,26 +725,18 @@ function each_item_transition(transition) {
* *
* @param {import('./types.js').EachItemBlock} block * @param {import('./types.js').EachItemBlock} block
* @param {Set<import('./types.js').Transition>} transitions * @param {Set<import('./types.js').Transition>} transitions
* @param {number} index
* @param {boolean} index_is_reactive
*/ */
function each_item_animate(block, transitions, index, index_is_reactive) { function each_item_animate(block, transitions) {
let prev_index = block.i; const from_dom = /** @type {Element} */ (get_first_element(block));
if (index_is_reactive) { const from = from_dom.getBoundingClientRect();
prev_index = /** @type {import('./types.js').Signal<number>} */ (prev_index).v; // Cancel any existing key transitions
} for (const transition of transitions) {
const items = block.p.v; const type = transition.r;
if (prev_index !== index && /** @type {number} */ (index) < items.length) { if (type === 'key') {
const from_dom = /** @type {Element} */ (get_first_element(block)); transition.c();
const from = from_dom.getBoundingClientRect();
// Cancel any existing key transitions
for (const transition of transitions) {
if (transition.r === 'key') {
transition.c();
}
} }
schedule_microtask(() => {
trigger_transitions(transitions, 'key', from);
});
} }
schedule_raf_task(() => {
trigger_transitions(transitions, 'key', from);
});
} }

@ -99,11 +99,17 @@ export type ComputationSignal<V = unknown> = {
/** The types that the signal represent, as a bitwise value */ /** The types that the signal represent, as a bitwise value */
f: SignalFlags; f: SignalFlags;
/** init: The function that we invoke for effects and computeds */ /** init: The function that we invoke for effects and computeds */
i: null | (() => V) | (() => void | (() => void)) | ((b: Block) => void | (() => void)); i:
| null
| (() => V)
| (() => void | (() => void))
| ((b: Block, s: Signal) => void | (() => void));
/** references: Anything that a signal owns */ /** references: Anything that a signal owns */
r: null | ComputationSignal[]; r: null | ComputationSignal[];
/** value: The latest value for this signal, doubles as the teardown for effects */ /** value: The latest value for this signal, doubles as the teardown for effects */
v: V; v: V;
/** level: the depth from the root signal, used for ordering render/pre-effects topologically **/
l: number;
}; };
export type Signal<V = unknown> = SourceSignal<V> | ComputationSignal<V>; export type Signal<V = unknown> = SourceSignal<V> | ComputationSignal<V>;
@ -288,14 +294,7 @@ export type EachBlock = {
export type EachItemBlock = { export type EachItemBlock = {
/** transition */ /** transition */
a: a: null | ((block: EachItemBlock, transitions: Set<Transition>) => void);
| null
| ((
block: EachItemBlock,
transitions: Set<Transition>,
index: number,
index_is_reactive: boolean
) => void);
/** dom */ /** dom */
d: null | TemplateNode | Array<TemplateNode>; d: null | TemplateNode | Array<TemplateNode>;
/** effect */ /** effect */

@ -75,8 +75,8 @@ export function validate_each_keys(collection, key_fn) {
const array = is_array(maybe_array) const array = is_array(maybe_array)
? maybe_array ? maybe_array
: maybe_array == null : maybe_array == null
? [] ? []
: Array.from(maybe_array); : Array.from(maybe_array);
const length = array.length; const length = array.length;
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const key = key_fn(array[i], i); const key = key_fn(array[i], i);

@ -178,4 +178,4 @@ declare function $props<T>(): T;
*/ */
declare function $inspect<T extends any[]>( declare function $inspect<T extends any[]>(
...values: T ...values: T
): { with: (type: 'init' | 'update', ...values: T) => void }; ): { with: (fn: (type: 'init' | 'update', ...values: T) => void) => void };

@ -214,8 +214,8 @@ export interface EventDispatcher<EventMap extends Record<string, any>> {
...args: null extends EventMap[Type] ...args: null extends EventMap[Type]
? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]
: undefined extends EventMap[Type] : undefined extends EventMap[Type]
? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]
: [type: Type, parameter: EventMap[Type], options?: DispatchOptions] : [type: Type, parameter: EventMap[Type], options?: DispatchOptions]
): boolean; ): boolean;
} }

@ -33,7 +33,7 @@ export function safe_not_equal(a, b) {
// eslint-disable-next-line eqeqeq // eslint-disable-next-line eqeqeq
return a != a return a != a
? // eslint-disable-next-line eqeqeq ? // eslint-disable-next-line eqeqeq
b == b b == b
: a !== b || (a && typeof a === 'object') || typeof a === 'function'; : a !== b || (a && typeof a === 'object') || typeof a === 'function';
} }

@ -12,14 +12,14 @@ function cubic_out(t) {
* @param {number} t * @param {number} t
* @returns {number} * @returns {number}
*/ */
export function cubic_in_out(t) { function cubic_in_out(t) {
return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0; return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0;
} }
/** @param {number | string} value /** @param {number | string} value
* @returns {[number, string]} * @returns {[number, string]}
*/ */
export function split_css_unit(value) { function split_css_unit(value) {
const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/); const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/);
return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px']; return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px'];
} }

@ -6,5 +6,5 @@
* https://svelte.dev/docs/svelte-compiler#svelte-version * https://svelte.dev/docs/svelte-compiler#svelte-version
* @type {string} * @type {string}
*/ */
export const VERSION = '5.0.0-next.31'; export const VERSION = '5.0.0-next.35';
export const PUBLIC_VERSION = '5'; export const PUBLIC_VERSION = '5';

@ -37,8 +37,8 @@ declare global {
): Key extends keyof ElementTagNameMap ): Key extends keyof ElementTagNameMap
? ElementTagNameMap[Key] ? ElementTagNameMap[Key]
: Key extends keyof SVGElementTagNameMap : Key extends keyof SVGElementTagNameMap
? SVGElementTagNameMap[Key] ? SVGElementTagNameMap[Key]
: any; : any;
function createElement<Elements extends IntrinsicElements, Key extends keyof Elements, T>( function createElement<Elements extends IntrinsicElements, Key extends keyof Elements, T>(
// "undefined | null" because of <svelte:element> // "undefined | null" because of <svelte:element>
element: Key | undefined | null, element: Key | undefined | null,
@ -47,8 +47,8 @@ declare global {
): Key extends keyof ElementTagNameMap ): Key extends keyof ElementTagNameMap
? ElementTagNameMap[Key] ? ElementTagNameMap[Key]
: Key extends keyof SVGElementTagNameMap : Key extends keyof SVGElementTagNameMap
? SVGElementTagNameMap[Key] ? SVGElementTagNameMap[Key]
: any; : any;
// For backwards-compatibility and ease-of-use, in case someone enhanced the typings from import('svelte/elements').HTMLAttributes/SVGAttributes // For backwards-compatibility and ease-of-use, in case someone enhanced the typings from import('svelte/elements').HTMLAttributes/SVGAttributes
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars

@ -53,9 +53,9 @@ export default test({
divs = target.querySelectorAll('div'); divs = target.querySelectorAll('div');
assert.ok(~divs[0].style.transform); assert.ok(~divs[0].style.transform);
assert.equal(divs[1].style.transform, ''); assert.equal(divs[1].style.transform, 'translate(1px, 0px)');
assert.equal(divs[2].style.transform, ''); assert.equal(divs[2].style.transform, 'translate(1px, 0px)');
assert.equal(divs[3].style.transform, ''); assert.equal(divs[3].style.transform, 'translate(1px, 0px)');
assert.ok(~divs[4].style.transform); assert.ok(~divs[4].style.transform);
raf.tick(100); raf.tick(100);

@ -20,6 +20,12 @@ export default test({
b2.click(); b2.click();
}); });
assert.deepEqual(log, ['transition 2', 'transition 1', 'transition 1']); assert.deepEqual(log, ['transition 2']);
flushSync(() => {
b1.click();
});
assert.deepEqual(log, ['transition 2', 'transition 1']);
} }
}); });

@ -0,0 +1,20 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
import { log } from './log.js';
export default test({
before_test() {
log.length = 0;
},
async test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
flushSync(() => {
b1?.click();
});
await Promise.resolve();
assert.deepEqual(log, ['clicked button']);
}
});

@ -0,0 +1,8 @@
<script>
import { log } from './log.js';
</script>
<div on:click={(e) => { log.push('clicked div') }}>
<button onclick={(e) => { log.push('clicked button'); e.stopPropagation() }}>
Button
</button>
</div>

@ -0,0 +1,20 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
import { log } from './log.js';
export default test({
before_test() {
log.length = 0;
},
async test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
flushSync(() => {
b1?.click();
});
await Promise.resolve();
assert.deepEqual(log, ['clicked button']);
}
});

@ -0,0 +1,13 @@
<script>
import { log } from './log.js';
const action = () => {}
</script>
<div use:action onclick={() => log.push('clicked container')} onkeydown={() => {}}>
<div use:action onclick={(e) => { log.push('clicked div 1') }}>
<div onclick={(e) => { log.push('clicked div 2') }}>
<button onclick={(e) => { log.push('clicked button'); e.stopPropagation() }}>
Button
</button>
</div>
</div>
</div>

@ -0,0 +1,25 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
import { log } from './log.js';
export default test({
before_test() {
log.length = 0;
},
async test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
flushSync(() => {
b1?.click();
});
await Promise.resolve();
assert.deepEqual(log, [
'clicked button',
'clicked div 2',
'clicked div 1',
'clicked container'
]);
}
});

@ -0,0 +1,13 @@
<script>
import { log } from './log.js';
const action = () => {}
</script>
<div use:action onclick={() => log.push('clicked container')} onkeydown={() => {}}>
<div use:action onclick={(e) => { log.push('clicked div 1') }}>
<div onclick={(e) => { log.push('clicked div 2') }}>
<button onclick={(e) => { log.push('clicked button') }}>
Button
</button>
</div>
</div>
</div>

@ -0,0 +1,16 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>Click</button><p>expires in 1 click</p>`,
async test({ assert, target }) {
const [btn1] = target.querySelectorAll('button');
flushSync(() => {
btn1.click();
});
assert.htmlEqual(target.innerHTML, ``);
}
});

@ -0,0 +1,13 @@
<script>
let data = $state({ num: 1 });
function expire() {
data.num = data.num - 1;
if (data.num <= 0) data = undefined;
}
</script>
{#if data}
<button onclick={expire}>Click</button>
<p>expires in {data.num} click</p>
{/if}

@ -31,9 +31,7 @@ export default test({
b2.click(); b2.click();
await Promise.resolve(); await Promise.resolve();
assert.ok( assert.ok(log[0].stack.startsWith('Error:') && log[0].stack.includes('HTMLButtonElement.'));
log[0].stack.startsWith('Error:') && log[0].stack.includes('HTMLButtonElement.on_click')
);
assert.deepEqual(log[1], 1); assert.deepEqual(log[1], 1);
} }
}); });

@ -0,0 +1,28 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
import { log } from './log.js';
export default test({
before_test() {
log.length = 0;
},
async test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
flushSync(() => {
b1?.click();
});
await Promise.resolve();
assert.deepEqual(log, [
'top level',
'inner',
0,
'destroy inner',
undefined,
'destroy outer',
undefined
]);
}
});

@ -0,0 +1,2 @@
/** @type {any[]} */
export const log = [];

@ -0,0 +1,21 @@
<script>
import { log } from './log.js';
let c = $state({ a: 0 });
$effect(() => {
log.push('top level')
$effect(() => {
if (c) {
$effect(() => {
log.push('inner',c.a);
return () => log.push('destroy inner', c?.a);
});
}
return () => log.push('destroy outer', c?.a);
})
});
</script>
<button onclick={() => {
c.a = 1; c = null
}}>toggle</button>

@ -45,4 +45,4 @@ export default function Main($$anchor, $$props) {
$.close_frag($$anchor, fragment); $.close_frag($$anchor, fragment);
$.pop(); $.pop();
} }

@ -0,0 +1,3 @@
import { test } from '../../test';
export default test({});

@ -0,0 +1,30 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import "svelte/internal/disclose-version";
import * as $ from "svelte/internal";
export default function Each_string_template($$anchor, $$props) {
$.push($$props, false);
/* Init */
var fragment = $.comment($$anchor);
var node = $.child_frag(fragment);
$.each_indexed(
node,
() => ['foo', 'bar', 'baz'],
1,
($$anchor, thing, $$index) => {
/* Init */
var node_1 = $.space($$anchor);
/* Update */
$.text_effect(node_1, () => `${$.stringify($.unwrap(thing))}, `);
$.close($$anchor, node_1);
},
null
);
$.close_frag($$anchor, fragment);
$.pop();
}

@ -0,0 +1,22 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import * as $ from "svelte/internal/server";
export default function Each_string_template($$payload, $$props) {
$.push(false);
const anchor = $.create_anchor($$payload);
const each_array = $.ensure_array_like(['foo', 'bar', 'baz']);
$$payload.out += `${anchor}`;
for (let $$index = 0; $$index < each_array.length; $$index++) {
const thing = each_array[$$index];
const anchor_1 = $.create_anchor($$payload);
$$payload.out += `${anchor_1}${$.escape(thing)}, ${anchor_1}`;
}
$$payload.out += `${anchor}`;
$.pop();
}

@ -0,0 +1,3 @@
{#each ['foo', 'bar', 'baz'] as thing}
{thing},{' '}
{/each}

@ -0,0 +1,3 @@
import { test } from '../../test';
export default test({});

@ -0,0 +1,39 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import "svelte/internal/disclose-version";
import * as $ from "svelte/internal";
function reset(_, str, tpl) {
$.set(str, '');
$.set(str, ``);
$.set(tpl, '');
$.set(tpl, ``);
}
var frag = $.template(`<input> <input> <button>reset</button>`, true);
export default function State_proxy_literal($$anchor, $$props) {
$.push($$props, true);
let str = $.source('');
let tpl = $.source(``);
/* Init */
var fragment = $.open_frag($$anchor, true, frag);
var node = $.child_frag(fragment);
$.remove_input_attr_defaults(node);
var input = $.sibling($.sibling(node));
$.remove_input_attr_defaults(input);
var button = $.sibling($.sibling(input));
$.bind_value(node, () => $.get(str), ($$value) => $.set(str, $$value));
$.bind_value(input, () => $.get(tpl), ($$value) => $.set(tpl, $$value));
button.__click = [reset, str, tpl];
$.close_frag($$anchor, fragment);
$.pop();
}
$.delegate(["click"]);

@ -0,0 +1,20 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import * as $ from "svelte/internal/server";
export default function State_proxy_literal($$payload, $$props) {
$.push(true);
let str = '';
let tpl = ``;
function reset() {
str = '';
str = ``;
tpl = '';
tpl = ``;
}
$$payload.out += `<input${$.attr("value", str, false)}> <input${$.attr("value", tpl, false)}> <button>reset</button>`;
$.pop();
}

@ -0,0 +1,17 @@
<script>
let str = $state('');
let tpl = $state(``);
function reset() {
str = '';
str = ``;
tpl = '';
tpl = ``;
}
</script>
<input bind:value={str} />
<input bind:value={tpl} />
<button onclick={reset}>reset</button>

@ -14,4 +14,4 @@ export default function Svelte_element($$anchor, $$props) {
$.element(node, tag); $.element(node, tag);
$.close_frag($$anchor, fragment); $.close_frag($$anchor, fragment);
$.pop(); $.pop();
} }

@ -17,7 +17,7 @@ const filter = process.env.FILTER
process.env.FILTER.startsWith('/') process.env.FILTER.startsWith('/')
? process.env.FILTER.slice(1, -1).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') ? process.env.FILTER.slice(1, -1).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
: `^${process.env.FILTER.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}$` : `^${process.env.FILTER.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}$`
) )
: /./; : /./;
export function suite<Test extends BaseTest>(fn: (config: Test, test_dir: string) => void) { export function suite<Test extends BaseTest>(fn: (config: Test, test_dir: string) => void) {

@ -215,8 +215,8 @@ declare module 'svelte' {
...args: null extends EventMap[Type] ...args: null extends EventMap[Type]
? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]
: undefined extends EventMap[Type] : undefined extends EventMap[Type]
? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]
: [type: Type, parameter: EventMap[Type], options?: DispatchOptions] : [type: Type, parameter: EventMap[Type], options?: DispatchOptions]
): boolean; ): boolean;
} }
/** /**
@ -515,7 +515,7 @@ declare module 'svelte/compiler' {
export function parse(source: string, options?: { export function parse(source: string, options?: {
filename?: string | undefined; filename?: string | undefined;
modern?: boolean | undefined; modern?: boolean | undefined;
} | undefined): SvelteNode | LegacySvelteNode; } | undefined): Root | LegacyRoot;
/** /**
* @deprecated Replace this with `import { walk } from 'estree-walker'` * @deprecated Replace this with `import { walk } from 'estree-walker'`
* */ * */
@ -780,6 +780,13 @@ declare module 'svelte/compiler' {
children: Array<LegacyElementLike>; children: Array<LegacyElementLike>;
} }
interface LegacyRoot extends BaseNode_1 {
html: LegacySvelteNode;
css?: any;
instance?: any;
module?: any;
}
interface LegacyAction extends BaseNode_1 { interface LegacyAction extends BaseNode_1 {
type: 'Action'; type: 'Action';
/** The 'x' in `use:x` */ /** The 'x' in `use:x` */
@ -1235,9 +1242,6 @@ declare module 'svelte/compiler' {
/** The 'y' in `on:x={y}` */ /** The 'y' in `on:x={y}` */
expression: null | Expression; expression: null | Expression;
modifiers: string[]; // TODO specify modifiers: string[]; // TODO specify
metadata: {
delegated: null | DelegatedEvent;
};
} }
type DelegatedEvent = type DelegatedEvent =
@ -1321,12 +1325,6 @@ declare module 'svelte/compiler' {
svg: boolean; svg: boolean;
/** `true` if contains a SpreadAttribute */ /** `true` if contains a SpreadAttribute */
has_spread: boolean; has_spread: boolean;
/**
* `true` if events on this element can theoretically be delegated. This doesn't necessarily mean that
* a specific event will be delegated, as there are other factors which affect the final outcome.
* `null` only until it was determined whether this element can be delegated or not.
*/
can_delegate_events: boolean | null;
}; };
} }
@ -2082,12 +2080,6 @@ declare module 'svelte/transition' {
duration?: number | ((len: number) => number); duration?: number | ((len: number) => number);
easing?: EasingFunction; easing?: EasingFunction;
} }
/**
* https://svelte.dev/docs/svelte-easing
* */
export function cubic_in_out(t: number): number;
export function split_css_unit(value: number | string): [number, string];
/** /**
* Animates a `blur` filter alongside an element's opacity. * Animates a `blur` filter alongside an element's opacity.
* *
@ -2574,6 +2566,6 @@ declare function $props<T>(): T;
*/ */
declare function $inspect<T extends any[]>( declare function $inspect<T extends any[]>(
...values: T ...values: T
): { with: (type: 'init' | 'update', ...values: T) => void }; ): { with: (fn: (type: 'init' | 'update', ...values: T) => void) => void };
//# sourceMappingURL=index.d.ts.map //# sourceMappingURL=index.d.ts.map

@ -4,6 +4,9 @@ settings:
autoInstallPeers: true autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides:
jimp>xml2js: ^0.6.0
importers: importers:
.: .:
@ -45,11 +48,11 @@ importers:
specifier: ^1.35.1 specifier: ^1.35.1
version: 1.39.0 version: 1.39.0
prettier: prettier:
specifier: ^3.0.1 specifier: ^3.1.1
version: 3.0.3 version: 3.1.1
prettier-plugin-svelte: prettier-plugin-svelte:
specifier: ^3.0.3 specifier: ^3.1.2
version: 3.1.0(prettier@3.0.3)(svelte@packages+svelte) version: 3.1.2(prettier@3.1.1)(svelte@packages+svelte)
typescript: typescript:
specifier: ^5.2.2 specifier: ^5.2.2
version: 5.2.2 version: 5.2.2
@ -65,6 +68,9 @@ importers:
'@jridgewell/sourcemap-codec': '@jridgewell/sourcemap-codec':
specifier: ^1.4.15 specifier: ^1.4.15
version: 1.4.15 version: 1.4.15
'@types/estree':
specifier: ^1.0.5
version: 1.0.5
acorn: acorn:
specifier: ^8.10.0 specifier: ^8.10.0
version: 8.11.2 version: 8.11.2
@ -117,9 +123,6 @@ importers:
'@types/aria-query': '@types/aria-query':
specifier: ^5.0.3 specifier: ^5.0.3
version: 5.0.4 version: 5.0.4
'@types/estree':
specifier: ^1.0.5
version: 1.0.5
dts-buddy: dts-buddy:
specifier: ^0.4.3 specifier: ^0.4.3
version: 0.4.3(typescript@5.2.2) version: 0.4.3(typescript@5.2.2)
@ -357,11 +360,11 @@ importers:
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.1.6 version: 9.1.6
prettier: prettier:
specifier: ^3.1.0 specifier: ^3.1.1
version: 3.1.0 version: 3.1.1
prettier-plugin-svelte: prettier-plugin-svelte:
specifier: ^3.1.2 specifier: ^3.1.2
version: 3.1.2(prettier@3.1.0)(svelte@4.2.3) version: 3.1.2(prettier@3.1.1)(svelte@4.2.3)
sass: sass:
specifier: ^1.67.0 specifier: ^1.67.0
version: 1.69.5 version: 1.69.5
@ -6073,24 +6076,24 @@ packages:
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
dev: true dev: true
/prettier-plugin-svelte@3.1.0(prettier@3.0.3)(svelte@packages+svelte): /prettier-plugin-svelte@3.1.2(prettier@3.1.1)(svelte@4.2.3):
resolution: {integrity: sha512-96+AZxs2ESqIFA9j+o+DHqY+BsUglezfl553LQd6VOtTyJq5GPuBEb3ElxF2cerFzKlYKttlH/VcVmRNj5oc3A==} resolution: {integrity: sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==}
peerDependencies: peerDependencies:
prettier: ^3.0.0 prettier: ^3.0.0
svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0
dependencies: dependencies:
prettier: 3.0.3 prettier: 3.1.1
svelte: link:packages/svelte svelte: 4.2.3
dev: true dev: true
/prettier-plugin-svelte@3.1.2(prettier@3.1.0)(svelte@4.2.3): /prettier-plugin-svelte@3.1.2(prettier@3.1.1)(svelte@packages+svelte):
resolution: {integrity: sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==} resolution: {integrity: sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==}
peerDependencies: peerDependencies:
prettier: ^3.0.0 prettier: ^3.0.0
svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0
dependencies: dependencies:
prettier: 3.1.0 prettier: 3.1.1
svelte: 4.2.3 svelte: link:packages/svelte
dev: true dev: true
/prettier@2.8.8: /prettier@2.8.8:
@ -6099,14 +6102,8 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/prettier@3.0.3: /prettier@3.1.1:
resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==}
engines: {node: '>=14'}
hasBin: true
dev: true
/prettier@3.1.0:
resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
dev: true dev: true

@ -298,7 +298,7 @@
status = message; status = message;
} }
}) })
: null; : null;
/** /**

@ -535,7 +535,7 @@ async function bundle({ uid, files }) {
exports: 'named' exports: 'named'
// sourcemap: 'inline' // sourcemap: 'inline'
}) })
)?.output?.[0] )?.output?.[0]
: null; : null;
return { return {

@ -41,7 +41,7 @@
"lightningcss": "^1.21.8", "lightningcss": "^1.21.8",
"magic-string": "^0.30.3", "magic-string": "^0.30.3",
"marked": "^9.0.0", "marked": "^9.0.0",
"prettier": "^3.1.0", "prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2", "prettier-plugin-svelte": "^3.1.2",
"sass": "^1.67.0", "sass": "^1.67.0",
"satori": "^0.10.4", "satori": "^0.10.4",

@ -12,7 +12,7 @@ export const GET = client_id
}).toString(); }).toString();
throw redirect(302, Location); throw redirect(302, Location);
} }
: () => : () =>
new Response( new Response(
` `

Loading…
Cancel
Save