defer-effects-in-pending-boundary
Rich Harris 5 months ago
commit e3fcf9d617

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: use symbols for encapsulated event delegation

@ -1,5 +1,33 @@
# svelte
## 5.51.3
### Patch Changes
- fix: prevent event delegation logic conflicting between svelte instances ([#17728](https://github.com/sveltejs/svelte/pull/17728))
- fix: treat CSS attribute selectors as case-insensitive for HTML enumerated attributes ([#17712](https://github.com/sveltejs/svelte/pull/17712))
- fix: locate Rollup annontaion friendly to JS downgraders ([#17724](https://github.com/sveltejs/svelte/pull/17724))
- fix: run effects in pending snippets ([#17719](https://github.com/sveltejs/svelte/pull/17719))
## 5.51.2
### Patch Changes
- fix: take async into consideration for dev delegated handlers ([#17710](https://github.com/sveltejs/svelte/pull/17710))
- fix: emit state_referenced_locally warning for non-destructured props ([#17708](https://github.com/sveltejs/svelte/pull/17708))
## 5.51.1
### Patch Changes
- fix: don't crash on undefined `document.contentType` ([#17707](https://github.com/sveltejs/svelte/pull/17707))
- fix: use symbols for encapsulated event delegation ([#17703](https://github.com/sveltejs/svelte/pull/17703))
## 5.51.0
### Minor Changes

@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.51.0",
"version": "5.51.3",
"type": "module",
"types": "./types/index.d.ts",
"engines": {

@ -22,6 +22,50 @@ const whitelist_attribute_selector = new Map([
['dialog', ['open']]
]);
/**
* HTML attributes whose enumerated values are case-insensitive per the HTML spec.
* CSS attribute selectors match these values case-insensitively in HTML documents.
* @see {@link https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors HTML spec}
*/
const case_insensitive_attributes = new Set([
'accept-charset',
'autocapitalize',
'autocomplete',
'behavior',
'charset',
'crossorigin',
'decoding',
'dir',
'direction',
'draggable',
'enctype',
'enterkeyhint',
'fetchpriority',
'formenctype',
'formmethod',
'formtarget',
'hidden',
'http-equiv',
'inputmode',
'kind',
'loading',
'method',
'preload',
'referrerpolicy',
'rel',
'rev',
'role',
'rules',
'scope',
'shape',
'spellcheck',
'target',
'translate',
'type',
'valign',
'wrap'
]);
/** @type {Compiler.AST.CSS.Combinator} */
const descendant_combinator = {
type: 'Combinator',
@ -523,7 +567,9 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
selector.name,
selector.value && unquote(selector.value),
selector.matcher,
selector.flags?.includes('i') ?? false
(selector.flags?.includes('i') ?? false) ||
(!selector.flags?.includes('s') &&
case_insensitive_attributes.has(selector.name.toLowerCase()))
)
) {
return false;

@ -115,7 +115,8 @@ export function Identifier(node, context) {
!should_proxy(binding.initial.arguments[0], context.state.scope)))) ||
binding.kind === 'raw_state' ||
binding.kind === 'derived' ||
binding.kind === 'prop') &&
binding.kind === 'prop' ||
binding.kind === 'rest_prop') &&
// We're only concerned with reads here
(parent.type !== 'AssignmentExpression' || parent.left !== node) &&
parent.type !== 'UpdateExpression'

@ -70,7 +70,8 @@ export function build_event(context, event_name, handler, capture, passive, dele
fn = b.function(
b.id(name),
handler.params,
handler.body.type === 'BlockStatement' ? handler.body : b.block([b.return(handler.body)])
handler.body.type === 'BlockStatement' ? handler.body : b.block([b.return(handler.body)]),
handler.async
);
}

@ -67,7 +67,10 @@ export const STALE_REACTION = new (class StaleReactionError extends Error {
message = 'The reaction that called `getAbortSignal()` was re-run or destroyed';
})();
export const IS_XHTML = /* @__PURE__ */ globalThis.document?.contentType.includes('xml') ?? false;
export const IS_XHTML =
// We gotta write it like this because after downleveling the pure comment may end up in the wrong location
!!globalThis.document?.contentType &&
/* @__PURE__ */ globalThis.document.contentType.includes('xml');
export const ELEMENT_NODE = 1;
export const TEXT_NODE = 3;
export const COMMENT_NODE = 8;

@ -1,6 +1,5 @@
/** @import { Effect, Source, TemplateNode, } from '#client' */
import {
BLOCK_EFFECT,
BOUNDARY_EFFECT,
COMMENT_NODE,
DIRTY,
@ -53,7 +52,7 @@ import { set_signal_status } from '../../reactivity/status.js';
* }} BoundaryProps
*/
var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED | BOUNDARY_EFFECT;
var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED;
/**
* @param {TemplateNode} node
@ -69,6 +68,10 @@ export class Boundary {
/** @type {Boundary | null} */
parent;
/**
* True if the boundary has a `pending` snippet, is not yet resolved, and either
* a) is mounting or b) is hydrating a server-rendered `pending` snippet
*/
is_pending = false;
/** @type {TemplateNode} */
@ -98,15 +101,10 @@ export class Boundary {
/** @type {DocumentFragment | null} */
#offscreen_fragment = null;
/** @type {TemplateNode | null} */
#pending_anchor = null;
#local_pending_count = 0;
#pending_count = 0;
#pending_count_update_queued = false;
#is_creating_fallback = false;
/** @type {Set<Effect>} */
#dirty_effects = new Set();
@ -142,51 +140,31 @@ export class Boundary {
constructor(node, props, children) {
this.#anchor = node;
this.#props = props;
this.#children = children;
this.parent = /** @type {Effect} */ (active_effect).b;
this.#children = (anchor) => {
var effect = /** @type {Effect} */ (active_effect);
this.is_pending = !!this.#props.pending;
effect.b = this;
effect.f |= BOUNDARY_EFFECT;
this.#effect = block(() => {
/** @type {Effect} */ (active_effect).b = this;
children(anchor);
};
this.parent = /** @type {Effect} */ (active_effect).b;
this.#effect = block(() => {
if (hydrating) {
const comment = this.#hydrate_open;
const comment = /** @type {Comment} */ (this.#hydrate_open);
hydrate_next();
const server_rendered_pending =
/** @type {Comment} */ (comment).nodeType === COMMENT_NODE &&
/** @type {Comment} */ (comment).data === HYDRATION_START_ELSE;
if (server_rendered_pending) {
if (comment.data === HYDRATION_START_ELSE) {
this.#hydrate_pending_content();
} else {
this.#hydrate_resolved_content();
if (this.#pending_count === 0) {
this.is_pending = false;
}
}
} else {
var anchor = this.#get_anchor();
try {
this.#main_effect = branch(() => children(anchor));
} catch (error) {
this.error(error);
}
if (this.#pending_count > 0) {
this.#show_pending_snippet();
} else {
this.is_pending = false;
}
this.#render();
}
return () => {
this.#pending_anchor?.remove();
};
}, flags);
if (hydrating) {
@ -206,19 +184,24 @@ export class Boundary {
const pending = this.#props.pending;
if (!pending) return;
this.is_pending = true;
this.#pending_effect = branch(() => pending(this.#anchor));
queue_micro_task(() => {
var anchor = this.#get_anchor();
var fragment = (this.#offscreen_fragment = document.createDocumentFragment());
var anchor = create_text();
fragment.append(anchor);
this.#main_effect = this.#run(() => {
Batch.ensure();
return branch(() => this.#children(anchor));
});
if (this.#pending_count > 0) {
this.#show_pending_snippet();
} else {
if (this.#pending_count === 0) {
this.#anchor.before(fragment);
this.#offscreen_fragment = null;
pause_effect(/** @type {Effect} */ (this.#pending_effect), () => {
this.#pending_effect = null;
});
@ -228,17 +211,28 @@ export class Boundary {
});
}
#get_anchor() {
var anchor = this.#anchor;
#render() {
try {
this.is_pending = this.has_pending_snippet();
this.#pending_count = 0;
this.#local_pending_count = 0;
this.#main_effect = branch(() => {
this.#children(this.#anchor);
});
if (this.is_pending) {
this.#pending_anchor = create_text();
this.#anchor.before(this.#pending_anchor);
if (this.#pending_count > 0) {
var fragment = (this.#offscreen_fragment = document.createDocumentFragment());
move_effect(this.#main_effect, fragment);
anchor = this.#pending_anchor;
const pending = /** @type {(anchor: Node) => void} */ (this.#props.pending);
this.#pending_effect = branch(() => pending(this.#anchor));
} else {
this.is_pending = false;
}
} catch (error) {
this.error(error);
}
return anchor;
}
/**
@ -262,7 +256,8 @@ export class Boundary {
}
/**
* @param {() => Effect | null} fn
* @template T
* @param {() => T} fn
*/
#run(fn) {
var previous_effect = active_effect;
@ -285,20 +280,6 @@ export class Boundary {
}
}
#show_pending_snippet() {
const pending = /** @type {(anchor: Node) => void} */ (this.#props.pending);
if (this.#main_effect !== null) {
this.#offscreen_fragment = document.createDocumentFragment();
this.#offscreen_fragment.append(/** @type {TemplateNode} */ (this.#pending_anchor));
move_effect(this.#main_effect, this.#offscreen_fragment);
}
if (this.#pending_effect === null) {
this.#pending_effect = branch(() => pending(this.#anchor));
}
}
#reschedule_deferred_effects() {
this.is_pending = false;
@ -387,7 +368,7 @@ export class Boundary {
// If we have nothing to capture the error, or if we hit an error while
// rendering the fallback, re-throw for another boundary to handle
if (this.#is_creating_fallback || (!onerror && !failed)) {
if (!onerror && !failed) {
throw error;
}
@ -427,31 +408,18 @@ export class Boundary {
e.svelte_boundary_reset_onerror();
}
// If the failure happened while flushing effects, current_batch can be null
Batch.ensure();
this.#local_pending_count = 0;
if (this.#failed_effect !== null) {
pause_effect(this.#failed_effect, () => {
this.#failed_effect = null;
});
}
// we intentionally do not try to find the nearest pending boundary. If this boundary has one, we'll render it on reset
// but it would be really weird to show the parent's boundary on a child reset.
this.is_pending = this.has_pending_snippet();
this.#run(() => {
// If the failure happened while flushing effects, current_batch can be null
Batch.ensure();
this.#main_effect = this.#run(() => {
this.#is_creating_fallback = false;
return branch(() => this.#children(this.#anchor));
this.#render();
});
if (this.#pending_count > 0) {
this.#show_pending_snippet();
} else {
this.is_pending = false;
}
};
queue_micro_task(() => {
@ -466,10 +434,16 @@ export class Boundary {
if (failed) {
this.#failed_effect = this.#run(() => {
Batch.ensure();
this.#is_creating_fallback = true;
try {
return branch(() => {
// errors in `failed` snippets cause the boundary to error again
// TODO Svelte 6: revisit this decision, most likely better to go to parent boundary instead
var effect = /** @type {Effect} */ (active_effect);
effect.b = this;
effect.f |= BOUNDARY_EFFECT;
failed(
this.#anchor,
() => error,
@ -479,8 +453,6 @@ export class Boundary {
} catch (error) {
invoke_error_boundary(error, /** @type {Effect} */ (this.#effect.parent));
return null;
} finally {
this.#is_creating_fallback = false;
}
});
}

@ -11,8 +11,11 @@ import {
set_active_reaction
} from '../../runtime.js';
import { without_reactive_context } from './bindings/shared.js';
import { can_delegate_event } from '../../../../utils.js';
/**
* Used on elements, as a map of event type -> event handler,
* and on events themselves to track which element handled an event
*/
export const event_symbol = Symbol('events');
/** @type {Set<string>} */
@ -177,8 +180,8 @@ export function handle_event_propagation(event) {
last_propagated_event = event;
// composedPath contains list of nodes the event has propagated through.
// We check __root to skip all nodes below it in case this is a
// parent of the __root node, which indicates that there's nested
// We check `event_symbol` to skip all nodes below it in case this is a
// parent of the `event_symbol` node, which indicates that there's nested
// mounted apps. In this case we don't want to trigger events multiple times.
var path_idx = 0;
@ -186,7 +189,7 @@ export function handle_event_propagation(event) {
// without it the variable will be DCE'd and things will
// fail mysteriously in Firefox
// @ts-expect-error is added below
var handled_at = last_propagated_event === event && event.__root;
var handled_at = last_propagated_event === event && event[event_symbol];
if (handled_at) {
var at_idx = path.indexOf(handled_at);
@ -198,7 +201,7 @@ export function handle_event_propagation(event) {
// -> ignore, but set handle_at to document/window so that we're resetting the event
// chain in case someone manually dispatches the same event object again.
// @ts-expect-error
event.__root = handler_element;
event[event_symbol] = handler_element;
return;
}
@ -298,7 +301,7 @@ export function handle_event_propagation(event) {
}
} finally {
// @ts-expect-error is used above
event.__root = handler_element;
event[event_symbol] = handler_element;
// @ts-ignore remove proxy on currentTarget
delete event.currentTarget;
set_active_reaction(previous_reaction);

@ -2,15 +2,15 @@
import { create_element } from './operations.js';
const policy = /* @__PURE__ */ globalThis?.window?.trustedTypes?.createPolicy(
'svelte-trusted-html',
{
const policy =
// We gotta write it like this because after downleveling the pure comment may end up in the wrong location
globalThis?.window?.trustedTypes &&
/* @__PURE__ */ globalThis.window.trustedTypes.createPolicy('svelte-trusted-html', {
/** @param {string} html */
createHTML: (html) => {
return html;
}
}
);
});
/** @param {string} html */
function create_trusted_html(html) {

@ -293,16 +293,19 @@ export class Batch {
}
}
var parent = effect.parent;
effect = effect.next;
while (effect === null && parent !== null) {
if (parent === pending_boundary) {
while (effect !== null) {
if (effect === pending_boundary) {
pending_boundary = null;
}
effect = parent.next;
parent = parent.parent;
var next = effect.next;
if (next !== null) {
effect = next;
break;
}
effect = effect.parent;
}
}
}

@ -161,48 +161,6 @@ const listeners = new Map();
function _mount(Component, { target, anchor, props = {}, events, context, intro = true }) {
init_operations();
/** @type {Set<string>} */
var registered_events = new Set();
/** @param {Array<string>} events */
var event_handle = (events) => {
for (var i = 0; i < events.length; i++) {
var event_name = events[i];
if (registered_events.has(event_name)) continue;
registered_events.add(event_name);
var passive = is_passive_event(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.
//
// The document listener ensures we catch events that originate from elements that were
// manually moved outside of the container (e.g. via manual portals).
for (const node of [target, document]) {
var counts = listeners.get(node);
if (counts === undefined) {
counts = new Map();
listeners.set(node, counts);
}
var count = counts.get(event_name);
if (count === undefined) {
node.addEventListener(event_name, handle_event_propagation, { passive });
counts.set(event_name, 1);
} else {
counts.set(event_name, count + 1);
}
}
}
};
event_handle(array_from(all_registered_events));
root_event_handles.add(event_handle);
/** @type {Exports} */
// @ts-expect-error will be defined because the render effect runs synchronously
var component = undefined;
@ -251,6 +209,49 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
}
);
// Setup event delegation _after_ component is mounted - if an error would happen during mount, it would otherwise not be cleaned up
/** @type {Set<string>} */
var registered_events = new Set();
/** @param {Array<string>} events */
var event_handle = (events) => {
for (var i = 0; i < events.length; i++) {
var event_name = events[i];
if (registered_events.has(event_name)) continue;
registered_events.add(event_name);
var passive = is_passive_event(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.
//
// The document listener ensures we catch events that originate from elements that were
// manually moved outside of the container (e.g. via manual portals).
for (const node of [target, document]) {
var counts = listeners.get(node);
if (counts === undefined) {
counts = new Map();
listeners.set(node, counts);
}
var count = counts.get(event_name);
if (count === undefined) {
node.addEventListener(event_name, handle_event_propagation, { passive });
counts.set(event_name, 1);
} else {
counts.set(event_name, count + 1);
}
}
}
};
event_handle(array_from(all_registered_events));
root_event_handles.add(event_handle);
return () => {
for (var event_name of registered_events) {
for (const node of [target, document]) {

@ -4,5 +4,5 @@
* The current version, as set in package.json.
* @type {string}
*/
export const VERSION = '5.51.0';
export const VERSION = '5.51.3';
export const PUBLIC_VERSION = '5';

@ -0,0 +1,11 @@
form[method="get"].svelte-xyz h1:where(.svelte-xyz) {
color: red;
}
form[method="post"].svelte-xyz h1:where(.svelte-xyz) {
color: blue;
}
input[type="text"].svelte-xyz {
color: green;
}

@ -0,0 +1 @@
<form class="svelte-xyz" method="GET"><h1 class="svelte-xyz">Hello</h1></form> <form class="svelte-xyz" method="POST"><h1 class="svelte-xyz">World</h1></form> <input class="svelte-xyz" type="Text" />

@ -0,0 +1,23 @@
<form method="GET">
<h1>Hello</h1>
</form>
<form method="POST">
<h1>World</h1>
</form>
<input type="Text" />
<style>
form[method="get"] h1 {
color: red;
}
form[method="post"] h1 {
color: blue;
}
input[type="text"] {
color: green;
}
</style>

@ -0,0 +1,9 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
compileOptions: {
dev: true
},
async test() {}
});

@ -0,0 +1,8 @@
<button
type="button"
onclick={async () => {
await Promise.resolve();
}}
>
Button
</button>

@ -0,0 +1,142 @@
[
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 3,
"column": 1,
"character": 33
},
"end": {
"line": 3,
"column": 6,
"character": 38
},
"position": [
33,
38
],
"frame": "1: <script>\n2: let props = $props();\n3: props.a;\n ^\n4: props[a];\n5: props.a.b;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 4,
"column": 1,
"character": 43
},
"end": {
"line": 4,
"column": 6,
"character": 48
},
"position": [
43,
48
],
"frame": "2: let props = $props();\n3: props.a;\n4: props[a];\n ^\n5: props.a.b;\n6: props.a.b = true;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 5,
"column": 1,
"character": 54
},
"end": {
"line": 5,
"column": 6,
"character": 59
},
"position": [
54,
59
],
"frame": "3: props.a;\n4: props[a];\n5: props.a.b;\n ^\n6: props.a.b = true;\n7: props.a = true;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 6,
"column": 1,
"character": 66
},
"end": {
"line": 6,
"column": 6,
"character": 71
},
"position": [
66,
71
],
"frame": "4: props[a];\n5: props.a.b;\n6: props.a.b = true;\n ^\n7: props.a = true;\n8: props[a] = true;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 7,
"column": 1,
"character": 85
},
"end": {
"line": 7,
"column": 6,
"character": 90
},
"position": [
85,
90
],
"frame": " 5: props.a.b;\n 6: props.a.b = true;\n 7: props.a = true;\n ^\n 8: props[a] = true;\n 9: props;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 8,
"column": 1,
"character": 102
},
"end": {
"line": 8,
"column": 6,
"character": 107
},
"position": [
102,
107
],
"frame": " 6: props.a.b = true;\n 7: props.a = true;\n 8: props[a] = true;\n ^\n 9: props;\n10: </script>"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 9,
"column": 1,
"character": 120
},
"end": {
"line": 9,
"column": 6,
"character": 125
},
"position": [
120,
125
],
"frame": " 7: props.a = true;\n 8: props[a] = true;\n 9: props;\n ^\n10: </script>\n11: "
}
]

@ -0,0 +1,142 @@
[
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 3,
"column": 1,
"character": 33
},
"end": {
"line": 3,
"column": 6,
"character": 38
},
"position": [
33,
38
],
"frame": "1: <script>\n2: let props = $props();\n3: props.a;\n ^\n4: props[a];\n5: props.a.b;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 4,
"column": 1,
"character": 43
},
"end": {
"line": 4,
"column": 6,
"character": 48
},
"position": [
43,
48
],
"frame": "2: let props = $props();\n3: props.a;\n4: props[a];\n ^\n5: props.a.b;\n6: props.a.b = true;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 5,
"column": 1,
"character": 54
},
"end": {
"line": 5,
"column": 6,
"character": 59
},
"position": [
54,
59
],
"frame": "3: props.a;\n4: props[a];\n5: props.a.b;\n ^\n6: props.a.b = true;\n7: props.a = true;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 6,
"column": 1,
"character": 66
},
"end": {
"line": 6,
"column": 6,
"character": 71
},
"position": [
66,
71
],
"frame": "4: props[a];\n5: props.a.b;\n6: props.a.b = true;\n ^\n7: props.a = true;\n8: props[a] = true;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 7,
"column": 1,
"character": 85
},
"end": {
"line": 7,
"column": 6,
"character": 90
},
"position": [
85,
90
],
"frame": " 5: props.a.b;\n 6: props.a.b = true;\n 7: props.a = true;\n ^\n 8: props[a] = true;\n 9: props;"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 8,
"column": 1,
"character": 102
},
"end": {
"line": 8,
"column": 6,
"character": 107
},
"position": [
102,
107
],
"frame": " 6: props.a.b = true;\n 7: props.a = true;\n 8: props[a] = true;\n ^\n 9: props;\n10: </script>"
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
"filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
"start": {
"line": 9,
"column": 1,
"character": 120
},
"end": {
"line": 9,
"column": 6,
"character": 125
},
"position": [
120,
125
],
"frame": " 7: props.a = true;\n 8: props[a] = true;\n 9: props;\n ^\n10: </script>\n11: "
}
]

@ -0,0 +1,6 @@
<script>
const props = $props();
const { model } = props;
const value = props.model.value;
console.log(model, value);
</script>

@ -0,0 +1,26 @@
[
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?",
"start": {
"line": 3,
"column": 19
},
"end": {
"line": 3,
"column": 24
}
},
{
"code": "state_referenced_locally",
"message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?",
"start": {
"line": 4,
"column": 15
},
"end": {
"line": 4,
"column": 20
}
}
]
Loading…
Cancel
Save