hoist-unmodified-var
Ben McCann 7 months ago
commit 8e17fa741f

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: handle delegated events of elements moved outside the container

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: support TypeScript's `satisfies` operator

@ -25,6 +25,8 @@
"cool-ants-leave",
"cuddly-pianos-drop",
"curly-lizards-dream",
"curvy-ties-shout",
"dirty-bats-punch",
"dirty-garlics-design",
"dirty-tips-add",
"dry-clocks-grow",
@ -54,6 +56,7 @@
"happy-suits-film",
"healthy-planes-vanish",
"heavy-ears-rule",
"hip-balloons-begin",
"honest-icons-change",
"hungry-dots-fry",
"hungry-tips-unite",
@ -80,6 +83,7 @@
"lucky-schools-hang",
"moody-frogs-exist",
"moody-owls-cry",
"nasty-lions-double",
"neat-dingos-clap",
"new-boats-wait",
"ninety-dingos-walk",
@ -88,12 +92,14 @@
"odd-shoes-cheat",
"old-flies-jog",
"old-mails-sneeze",
"orange-dingos-poke",
"polite-dolphins-care",
"polite-pumpkins-guess",
"polite-ravens-study",
"poor-eggs-enjoy",
"poor-seahorses-flash",
"popular-mangos-rest",
"pretty-ties-help",
"purple-dragons-peel",
"quiet-camels-mate",
"rare-pears-whisper",
@ -125,10 +131,12 @@
"strong-gifts-smoke",
"strong-lemons-provide",
"sweet-mangos-beg",
"sweet-pens-sniff",
"swift-donkeys-perform",
"swift-ravens-hunt",
"swift-seahorses-deliver",
"tall-books-grin",
"tall-garlics-try",
"tall-shrimps-worry",
"tall-tigers-wait",
"tasty-numbers-perform",
@ -140,12 +148,14 @@
"thirty-ghosts-fix",
"thirty-impalas-repair",
"thirty-wombats-relax",
"three-suits-grin",
"tiny-kings-whisper",
"twelve-dragons-join",
"twelve-onions-juggle",
"two-dragons-yell",
"two-falcons-buy",
"unlucky-boxes-obey",
"unlucky-trees-lick",
"wet-games-fly",
"wicked-clouds-exercise",
"wicked-doors-train",

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: bail-out event handler referencing each index

@ -1,5 +1,29 @@
# svelte
## 5.0.0-next.28
### Patch Changes
- fix: deeply unstate objects passed to inspect ([#10056](https://github.com/sveltejs/svelte/pull/10056))
- fix: handle delegated events of elements moved outside the container ([#10060](https://github.com/sveltejs/svelte/pull/10060))
- fix: improve script `lang` attribute detection ([#10046](https://github.com/sveltejs/svelte/pull/10046))
- fix: improve pseudo class parsing ([#10055](https://github.com/sveltejs/svelte/pull/10055))
- fix: add types for popover attributes and events ([#10041](https://github.com/sveltejs/svelte/pull/10041))
- fix: skip generating $.proxy() calls for unary and binary expressions ([#9979](https://github.com/sveltejs/svelte/pull/9979))
- fix: allow pseudo classes after `:global(..)` ([#10055](https://github.com/sveltejs/svelte/pull/10055))
- fix: bail-out event handler referencing each index ([#10063](https://github.com/sveltejs/svelte/pull/10063))
- fix: parse `:nth-of-type(xn+y)` correctly ([#9970](https://github.com/sveltejs/svelte/pull/9970))
- fix: ensure if block is executed in correct order ([#10053](https://github.com/sveltejs/svelte/pull/10053))
## 5.0.0-next.27
### Patch Changes

@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.0.0-next.27",
"version": "5.0.0-next.28",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
@ -117,7 +117,7 @@
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
"acorn": "^8.10.0",
"acorn-typescript": "^1.4.11",
"acorn-typescript": "^1.4.13",
"aria-query": "^5.3.0",
"axobject-query": "^4.0.0",
"esm-env": "^1.0.0",

@ -2,7 +2,7 @@ import * as acorn from 'acorn';
import { walk } from 'zimmerframe';
import { tsPlugin } from 'acorn-typescript';
const ParserWithTS = acorn.Parser.extend(tsPlugin());
const ParserWithTS = acorn.Parser.extend(tsPlugin({ allowSatisfies: true }));
/**
* @param {string} source

@ -183,6 +183,10 @@ function get_delegated_event(node, context) {
) {
return non_hoistable;
}
// If we referebnce the index within an each block, then bail-out.
if (binding !== null && binding.initial?.type === 'EachBlock') {
return non_hoistable;
}
if (
binding !== null &&

@ -41,13 +41,16 @@ export const remove_types = {
TSAsExpression(node, context) {
return context.visit(node.expression);
},
TSSatisfiesExpression(node, context) {
return context.visit(node.expression);
},
TSNonNullExpression(node, context) {
return context.visit(node.expression);
},
TSInterfaceDeclaration(node, context) {
TSInterfaceDeclaration() {
return b.empty;
},
TSTypeAliasDeclaration(node, context) {
TSTypeAliasDeclaration() {
return b.empty;
}
};

@ -73,7 +73,7 @@ export class Scope {
* @param {import('estree').Identifier} node
* @param {import('#compiler').Binding['kind']} kind
* @param {import('#compiler').DeclarationKind} declaration_kind
* @param {null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration} initial
* @param {null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration | import('../types/template.js').EachBlock} initial
* @returns {import('#compiler').Binding}
*/
declare(node, kind, declaration_kind, initial = null) {
@ -551,7 +551,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
const is_keyed =
node.key &&
(node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index);
scope.declare(b.id(node.index), is_keyed ? 'derived' : 'normal', 'const');
scope.declare(b.id(node.index), is_keyed ? 'derived' : 'normal', 'const', node);
}
if (node.key) visit(node.key, { scope });
@ -708,7 +708,7 @@ export function set_scope(scopes) {
/**
* Returns the name of the rune if the given expression is a `CallExpression` using a rune.
* @param {import('estree').Node | null | undefined} node
* @param {import('estree').Node | import('../types/template.js').EachBlock | null | undefined} node
* @param {Scope} scope
* @returns {Runes[number] | null}
*/

@ -11,7 +11,7 @@ import type { SourceMap } from 'magic-string';
import type { Context } from 'zimmerframe';
import type { Scope } from '../phases/scope.js';
import * as Css from './css.js';
import type { Namespace, SvelteNode } from './template.js';
import type { EachBlock, Namespace, SvelteNode } from './template.js';
/** The return value of `compile` from `svelte/compiler` */
export interface CompileResult {
@ -269,7 +269,13 @@ export interface Binding {
* What the value was initialized with.
* For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()`
*/
initial: null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration;
initial:
| null
| Expression
| FunctionDeclaration
| ClassDeclaration
| ImportDeclaration
| EachBlock;
is_called: boolean;
references: { node: Identifier; path: SvelteNode[] }[];
mutated: boolean;

@ -1280,6 +1280,10 @@ function handle_event_propagation(root_element, event) {
const handled_at = event.__root;
if (handled_at) {
const at_idx = path.indexOf(handled_at);
if (at_idx !== -1 && root_element === document) {
// This is the fallback document listener but the event was already handled -> ignore
return;
}
if (at_idx < path.indexOf(root_element)) {
path_idx = at_idx;
}
@ -2778,6 +2782,7 @@ export function mount(component, options) {
set_current_hydration_fragment(previous_hydration_fragment);
}
const bound_event_listener = handle_event_propagation.bind(null, container);
const bound_document_event_listener = handle_event_propagation.bind(null, document);
/** @param {Array<string>} events */
const event_handle = (events) => {
@ -2785,6 +2790,9 @@ export function mount(component, options) {
const event_name = events[i];
if (!registered_events.has(event_name)) {
registered_events.add(event_name);
// Add the event listener to both the container and the document.
// The container listener ensures we catch events from within in case
// the outer content stops propagation of the event.
container.addEventListener(
event_name,
bound_event_listener,
@ -2794,6 +2802,17 @@ export function mount(component, options) {
}
: undefined
);
// The document listener ensures we catch events that originate from elements that were
// manually moved outside of the container (e.g. via manual portals).
document.addEventListener(
event_name,
bound_document_event_listener,
PassiveDelegatedEvents.includes(event_name)
? {
passive: true
}
: undefined
);
}
}
};

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

@ -0,0 +1,23 @@
import { test } from '../../test';
// Tests that event delegation still works when the element with the event listener is moved outside the container
export default test({
async test({ assert, target }) {
const btn1 = target.parentElement?.querySelector('button');
const btn2 = target.querySelector('button');
btn1?.click();
await Promise.resolve();
assert.htmlEqual(
target.parentElement?.innerHTML ?? '',
'<main><div><button>clicks: 1</button></div></main><button>clicks: 1</button>'
);
btn2?.click();
await Promise.resolve();
assert.htmlEqual(
target.parentElement?.innerHTML ?? '',
'<main><div><button>clicks: 2</button></div></main><button>clicks: 2</button>'
);
}
});

@ -0,0 +1,12 @@
<script>
let count = $state(0);
let el;
$effect(() => {
document.getElementsByTagName('body')[0].appendChild(el);
})
</script>
<div>
<button bind:this={el} onclick={() => count++}>clicks: {count}</button>
<button onclick={() => count++}>clicks: {count}</button>
</div>

@ -10,6 +10,9 @@
import { type Bar, type Baz } from './types';
let count = $state(0);
const person = {
message: 'goodbye'
} satisfies Goodbye;
</script>
<button

@ -746,7 +746,13 @@ declare module 'svelte/compiler' {
* What the value was initialized with.
* For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()`
*/
initial: null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration;
initial:
| null
| Expression
| FunctionDeclaration
| ClassDeclaration
| ImportDeclaration
| EachBlock;
is_called: boolean;
references: { node: Identifier; path: SvelteNode[] }[];
mutated: boolean;
@ -1026,7 +1032,7 @@ declare module 'svelte/compiler' {
*/
function_depth: number;
declare(node: import('estree').Identifier, kind: Binding['kind'], declaration_kind: DeclarationKind, initial?: null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration): Binding;
declare(node: import('estree').Identifier, kind: Binding['kind'], declaration_kind: DeclarationKind, initial?: null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration | EachBlock): Binding;
has_parent(): boolean;

@ -69,8 +69,8 @@ importers:
specifier: ^8.10.0
version: 8.11.2
acorn-typescript:
specifier: ^1.4.11
version: 1.4.11(acorn@8.11.2)
specifier: ^1.4.13
version: 1.4.13(acorn@8.11.2)
aria-query:
specifier: ^5.3.0
version: 5.3.0
@ -2748,8 +2748,8 @@ packages:
acorn: 8.11.2
dev: true
/acorn-typescript@1.4.11(acorn@8.11.2):
resolution: {integrity: sha512-cRGgp+4HMxMZAiMS61ZmQ3iuU/+A4g4ZYZsyLZdmvrEVN/TOwfJ40rPWcLqi3H5ut75SYAdOOJj6QGCcrkK57w==}
/acorn-typescript@1.4.13(acorn@8.11.2):
resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==}
peerDependencies:
acorn: '>=8.9.0'
dependencies:

Loading…
Cancel
Save