svelte 5.55.9 → 5.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/index.js +1 -1
- package/package.json +8 -5
- package/src/compiler/errors.js +18 -0
- package/src/compiler/legacy.js +4 -0
- package/src/compiler/phases/1-parse/acorn.js +44 -1
- package/src/compiler/phases/1-parse/index.js +4 -1
- package/src/compiler/phases/1-parse/state/tag.js +91 -3
- package/src/compiler/phases/2-analyze/index.js +5 -0
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +1 -2
- package/src/compiler/phases/2-analyze/visitors/CallExpression.js +3 -0
- package/src/compiler/phases/2-analyze/visitors/ConstTag.js +2 -25
- package/src/compiler/phases/2-analyze/visitors/DeclarationTag.js +58 -0
- package/src/compiler/phases/2-analyze/visitors/Identifier.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +1 -1
- package/src/compiler/phases/3-transform/client/transform-client.js +5 -15
- package/src/compiler/phases/3-transform/client/transform-template/index.js +40 -3
- package/src/compiler/phases/3-transform/client/utils.js +21 -0
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +13 -24
- package/src/compiler/phases/3-transform/client/visitors/DeclarationTag.js +87 -0
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +2 -5
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +32 -10
- package/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +7 -2
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +14 -11
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +1 -1
- package/src/compiler/phases/3-transform/server/transform-server.js +2 -0
- package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +9 -24
- package/src/compiler/phases/3-transform/server/visitors/DeclarationTag.js +85 -0
- package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +24 -7
- package/src/compiler/phases/3-transform/utils.js +1 -0
- package/src/compiler/phases/nodes.js +3 -2
- package/src/compiler/print/index.js +42 -0
- package/src/compiler/utils/builders.js +2 -1
- package/src/compiler/warnings.js +6 -5
- package/src/internal/client/dom/blocks/boundary.js +1 -1
- package/src/internal/client/dom/blocks/branches.js +2 -0
- package/src/internal/client/dom/blocks/svelte-element.js +5 -3
- package/src/internal/client/dom/elements/events.js +5 -10
- package/src/internal/client/dom/operations.js +12 -2
- package/src/internal/client/reactivity/async.js +4 -4
- package/src/internal/client/reactivity/batch.js +70 -78
- package/src/internal/client/reactivity/deriveds.js +7 -4
- package/src/internal/client/reactivity/effects.js +5 -2
- package/src/internal/client/reactivity/props.js +6 -6
- package/src/internal/client/reactivity/sources.js +1 -2
- package/src/internal/client/runtime.js +4 -8
- package/src/utils.js +1 -1
- package/src/version.js +1 -1
- package/types/index.d.ts +7 -0
- package/types/index.d.ts.map +1 -1
|
@@ -603,6 +603,48 @@ const svelte_visitors = (comments) => ({
|
|
|
603
603
|
context.write('}');
|
|
604
604
|
},
|
|
605
605
|
|
|
606
|
+
DeclarationTag(node, context) {
|
|
607
|
+
context.write('{');
|
|
608
|
+
|
|
609
|
+
// This is duplicated from esrap's handling of VariableDeclaration,
|
|
610
|
+
// which we need to do in order to omit the trailing semicolon that esrap would add.
|
|
611
|
+
const open = context.new();
|
|
612
|
+
const join = context.new();
|
|
613
|
+
const child_context = context.new();
|
|
614
|
+
|
|
615
|
+
context.append(child_context);
|
|
616
|
+
|
|
617
|
+
child_context.write(`${node.declaration.kind} `);
|
|
618
|
+
child_context.append(open);
|
|
619
|
+
|
|
620
|
+
const declarations = node.declaration.declarations;
|
|
621
|
+
let first = true;
|
|
622
|
+
|
|
623
|
+
for (const d of declarations) {
|
|
624
|
+
if (!first) child_context.append(join);
|
|
625
|
+
first = false;
|
|
626
|
+
|
|
627
|
+
child_context.visit(d);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const length = child_context.measure() + 2 * (declarations.length - 1);
|
|
631
|
+
|
|
632
|
+
const multiline = child_context.multiline || (declarations.length > 1 && length > 50);
|
|
633
|
+
|
|
634
|
+
if (multiline) {
|
|
635
|
+
context.multiline = true;
|
|
636
|
+
|
|
637
|
+
if (declarations.length > 1) open.indent();
|
|
638
|
+
join.write(',');
|
|
639
|
+
join.newline();
|
|
640
|
+
if (declarations.length > 1) context.dedent();
|
|
641
|
+
} else {
|
|
642
|
+
join.write(', ');
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
context.write('}');
|
|
646
|
+
},
|
|
647
|
+
|
|
606
648
|
DebugTag(node, context) {
|
|
607
649
|
context.write('{@debug ');
|
|
608
650
|
let started = false;
|
package/src/compiler/warnings.js
CHANGED
|
@@ -166,11 +166,12 @@ export function a11y_autofocus(node) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* Visible, non-interactive
|
|
169
|
+
* Visible, non-interactive element `<%element%>` with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate
|
|
170
170
|
* @param {null | NodeLike} node
|
|
171
|
+
* @param {string} element
|
|
171
172
|
*/
|
|
172
|
-
export function a11y_click_events_have_key_events(node) {
|
|
173
|
-
w(node, 'a11y_click_events_have_key_events', `Visible, non-interactive
|
|
173
|
+
export function a11y_click_events_have_key_events(node, element) {
|
|
174
|
+
w(node, 'a11y_click_events_have_key_events', `Visible, non-interactive element \`<${element}>\` with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate\nhttps://svelte.dev/e/a11y_click_events_have_key_events`);
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
/**
|
|
@@ -803,11 +804,11 @@ export function script_context_deprecated(node) {
|
|
|
803
804
|
}
|
|
804
805
|
|
|
805
806
|
/**
|
|
806
|
-
*
|
|
807
|
+
* Unrecognised attribute — should be one of `generics`, `lang` or `module`. If this exists for a preprocessor, ensure that the preprocessor removes it
|
|
807
808
|
* @param {null | NodeLike} node
|
|
808
809
|
*/
|
|
809
810
|
export function script_unknown_attribute(node) {
|
|
810
|
-
w(node, 'script_unknown_attribute', `
|
|
811
|
+
w(node, 'script_unknown_attribute', `Unrecognised attribute — should be one of \`generics\`, \`lang\` or \`module\`. If this exists for a preprocessor, ensure that the preprocessor removes it\nhttps://svelte.dev/e/script_unknown_attribute`);
|
|
811
812
|
}
|
|
812
813
|
|
|
813
814
|
/**
|
|
@@ -396,7 +396,7 @@ export class Boundary {
|
|
|
396
396
|
if (this.#pending_effect) current_batch.skip_effect(this.#pending_effect);
|
|
397
397
|
if (this.#failed_effect) current_batch.skip_effect(this.#failed_effect);
|
|
398
398
|
|
|
399
|
-
current_batch.
|
|
399
|
+
current_batch.oncommit(() => {
|
|
400
400
|
this.#handle_error(error);
|
|
401
401
|
});
|
|
402
402
|
} else {
|
|
@@ -90,6 +90,8 @@ export class BranchManager {
|
|
|
90
90
|
var offscreen = this.#offscreen.get(key);
|
|
91
91
|
|
|
92
92
|
if (offscreen) {
|
|
93
|
+
// effect could have been outro'ed before through a prior batch — resume if necessary
|
|
94
|
+
resume_effect(offscreen.effect);
|
|
93
95
|
this.#onscreen.set(key, offscreen.effect);
|
|
94
96
|
this.#offscreen.delete(key);
|
|
95
97
|
|
|
@@ -88,9 +88,11 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
|
|
|
88
88
|
assign_nodes(element, element);
|
|
89
89
|
|
|
90
90
|
if (render_fn) {
|
|
91
|
+
var tmp_comment = null;
|
|
92
|
+
|
|
91
93
|
if (hydrating && is_raw_text_element(next_tag)) {
|
|
92
|
-
// prevent hydration glitches
|
|
93
|
-
element.append(document.createComment(''));
|
|
94
|
+
// prevent hydration glitches (code just below expects an anchor)
|
|
95
|
+
element.append((tmp_comment = document.createComment('')));
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
// If hydrating, use the existing ssr comment as the anchor so that the
|
|
@@ -114,7 +116,7 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
|
|
|
114
116
|
// contains children, it's a user error (which is warned on elsewhere)
|
|
115
117
|
// and the DOM will be silently discarded
|
|
116
118
|
render_fn(element, child_anchor);
|
|
117
|
-
|
|
119
|
+
tmp_comment?.remove();
|
|
118
120
|
set_animation_effect_override(null);
|
|
119
121
|
}
|
|
120
122
|
|
|
@@ -257,12 +257,7 @@ export function handle_event_propagation(event) {
|
|
|
257
257
|
var other_errors = [];
|
|
258
258
|
|
|
259
259
|
while (current_target !== null) {
|
|
260
|
-
|
|
261
|
-
var parent_element =
|
|
262
|
-
current_target.assignedSlot ||
|
|
263
|
-
current_target.parentNode ||
|
|
264
|
-
/** @type {any} */ (current_target).host ||
|
|
265
|
-
null;
|
|
260
|
+
if (current_target === handler_element) break;
|
|
266
261
|
|
|
267
262
|
try {
|
|
268
263
|
// @ts-expect-error
|
|
@@ -284,10 +279,10 @@ export function handle_event_propagation(event) {
|
|
|
284
279
|
throw_error = error;
|
|
285
280
|
}
|
|
286
281
|
}
|
|
287
|
-
if (event.cancelBubble
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
current_target =
|
|
282
|
+
if (event.cancelBubble) break;
|
|
283
|
+
|
|
284
|
+
path_idx++;
|
|
285
|
+
current_target = path_idx < path.length ? /** @type {Element} */ (path[path_idx]) : null;
|
|
291
286
|
}
|
|
292
287
|
|
|
293
288
|
if (throw_error) {
|
|
@@ -233,6 +233,12 @@ export function should_defer_append() {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
/**
|
|
236
|
+
* Branching here is intentional and load-bearing for perf. `createElement(tag)`
|
|
237
|
+
* hits a fast path in Blink that `createElementNS(NAMESPACE_HTML, tag)` doesn't,
|
|
238
|
+
* and passing an explicit `undefined` as the trailing options arg measurably
|
|
239
|
+
* slows both APIs. Funnelling every case through a single `createElementNS(ns,
|
|
240
|
+
* tag, options)` call would be smaller but slower on the HTML path.
|
|
241
|
+
*
|
|
236
242
|
* @template {keyof HTMLElementTagNameMap | string} T
|
|
237
243
|
* @param {T} tag
|
|
238
244
|
* @param {string} [namespace]
|
|
@@ -240,9 +246,13 @@ export function should_defer_append() {
|
|
|
240
246
|
* @returns {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element}
|
|
241
247
|
*/
|
|
242
248
|
export function create_element(tag, namespace, is) {
|
|
243
|
-
|
|
249
|
+
if (namespace == null || namespace === NAMESPACE_HTML) {
|
|
250
|
+
return /** @type {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */ (
|
|
251
|
+
is ? document.createElement(tag, { is }) : document.createElement(tag)
|
|
252
|
+
);
|
|
253
|
+
}
|
|
244
254
|
return /** @type {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */ (
|
|
245
|
-
document.createElementNS(namespace
|
|
255
|
+
is ? document.createElementNS(namespace, tag, { is }) : document.createElementNS(namespace, tag)
|
|
246
256
|
);
|
|
247
257
|
}
|
|
248
258
|
|
|
@@ -352,15 +352,15 @@ export function wait(blockers) {
|
|
|
352
352
|
*/
|
|
353
353
|
export function increment_pending() {
|
|
354
354
|
var effect = /** @type {Effect} */ (active_effect);
|
|
355
|
-
var boundary =
|
|
355
|
+
var boundary = effect.b; // undefined if called outside the render tree, e.g. a standalone $effect.root
|
|
356
356
|
var batch = /** @type {Batch} */ (current_batch);
|
|
357
|
-
var blocking = boundary
|
|
357
|
+
var blocking = !!boundary?.is_rendered();
|
|
358
358
|
|
|
359
|
-
boundary
|
|
359
|
+
boundary?.update_pending_count(1, batch);
|
|
360
360
|
batch.increment(blocking, effect);
|
|
361
361
|
|
|
362
362
|
return () => {
|
|
363
|
-
boundary
|
|
363
|
+
boundary?.update_pending_count(-1, batch);
|
|
364
364
|
batch.decrement(blocking, effect);
|
|
365
365
|
};
|
|
366
366
|
}
|
|
@@ -127,13 +127,6 @@ export class Batch {
|
|
|
127
127
|
*/
|
|
128
128
|
previous = new Map();
|
|
129
129
|
|
|
130
|
-
/**
|
|
131
|
-
* Async effects which this batch doesn't take into account anymore when calculating blockers,
|
|
132
|
-
* as it has a value for it already.
|
|
133
|
-
* @type {Set<Effect>}
|
|
134
|
-
*/
|
|
135
|
-
unblocked = new Set();
|
|
136
|
-
|
|
137
130
|
/**
|
|
138
131
|
* When the batch is committed (and the DOM is updated), we need to remove old branches
|
|
139
132
|
* and append new ones by calling the functions added inside (if/each/key/etc) blocks
|
|
@@ -147,12 +140,6 @@ export class Batch {
|
|
|
147
140
|
*/
|
|
148
141
|
#discard_callbacks = new Set();
|
|
149
142
|
|
|
150
|
-
/**
|
|
151
|
-
* Callbacks that should run only when a fork is committed.
|
|
152
|
-
* @type {Set<(batch: Batch) => void>}
|
|
153
|
-
*/
|
|
154
|
-
#fork_commit_callbacks = new Set();
|
|
155
|
-
|
|
156
143
|
/**
|
|
157
144
|
* The number of async effects that are currently in flight
|
|
158
145
|
*/
|
|
@@ -214,6 +201,18 @@ export class Batch {
|
|
|
214
201
|
|
|
215
202
|
#decrement_queued = false;
|
|
216
203
|
|
|
204
|
+
constructor() {
|
|
205
|
+
// link batch
|
|
206
|
+
if (last_batch === null) {
|
|
207
|
+
first_batch = last_batch = this;
|
|
208
|
+
} else {
|
|
209
|
+
last_batch.#next = this;
|
|
210
|
+
this.#prev = last_batch;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
last_batch = this;
|
|
214
|
+
}
|
|
215
|
+
|
|
217
216
|
#is_deferred() {
|
|
218
217
|
if (this.is_fork) return true;
|
|
219
218
|
|
|
@@ -289,19 +288,19 @@ export class Batch {
|
|
|
289
288
|
}
|
|
290
289
|
}
|
|
291
290
|
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
if
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
291
|
+
// We always reschedule previously-deferred effects, not just when
|
|
292
|
+
// #is_deferred() is true, because traversing the tree could make
|
|
293
|
+
// an if block that contains the last blocking pending effect falsy,
|
|
294
|
+
// causing the block to no longer be deferred.
|
|
295
|
+
for (const e of this.#dirty_effects) {
|
|
296
|
+
this.#maybe_dirty_effects.delete(e);
|
|
297
|
+
set_signal_status(e, DIRTY);
|
|
298
|
+
this.schedule(e);
|
|
299
|
+
}
|
|
300
300
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
301
|
+
for (const e of this.#maybe_dirty_effects) {
|
|
302
|
+
set_signal_status(e, MAYBE_DIRTY);
|
|
303
|
+
this.schedule(e);
|
|
305
304
|
}
|
|
306
305
|
|
|
307
306
|
const roots = this.#roots;
|
|
@@ -326,6 +325,12 @@ export class Batch {
|
|
|
326
325
|
this.#traverse(root, effects, render_effects);
|
|
327
326
|
} catch (e) {
|
|
328
327
|
reset_all(root);
|
|
328
|
+
// If there's no async work left, this branch is now dead and needs
|
|
329
|
+
// to be discarded to not become a zombie that is never cleaned up.
|
|
330
|
+
// See https://github.com/sveltejs/svelte/issues/18221#issuecomment-4497918414
|
|
331
|
+
// for a (non-minimal) reproduction that demonstrates a case where this is necessary
|
|
332
|
+
// to not get follow-up false-positives via "batch has scheduled roots" invariant errors.
|
|
333
|
+
if (!this.#is_deferred()) this.discard();
|
|
329
334
|
throw e;
|
|
330
335
|
}
|
|
331
336
|
}
|
|
@@ -362,6 +367,10 @@ export class Batch {
|
|
|
362
367
|
const earlier_batch = this.#find_earlier_batch();
|
|
363
368
|
|
|
364
369
|
if (earlier_batch) {
|
|
370
|
+
// If this batch collected deferred effects during traversal, they still need
|
|
371
|
+
// to run after being merged into the earlier batch.
|
|
372
|
+
this.#defer_effects(render_effects);
|
|
373
|
+
this.#defer_effects(effects);
|
|
365
374
|
earlier_batch.#merge(this);
|
|
366
375
|
return;
|
|
367
376
|
}
|
|
@@ -383,31 +392,30 @@ export class Batch {
|
|
|
383
392
|
|
|
384
393
|
var next_batch = /** @type {Batch | null} */ (/** @type {unknown} */ (current_batch));
|
|
385
394
|
|
|
386
|
-
if (this
|
|
395
|
+
if (this.#pending === 0 && (this.#roots.length === 0 || next_batch !== null)) {
|
|
387
396
|
this.#unlink();
|
|
388
|
-
}
|
|
389
397
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
+
// Order matters here - we need to commit and THEN continue flushing new batches, not the other way around,
|
|
399
|
+
// else we could start flushing a new batch and then, if it has pending work, rebase it right afterwards, which is wrong.
|
|
400
|
+
// In sync mode flushSync can cause #commit to wrongfully think that there needs to be a rebase, so we only do it in async mode
|
|
401
|
+
// TODO fix the underlying cause, otherwise this will likely regress when non-async mode is removed
|
|
402
|
+
if (async_mode_flag) {
|
|
403
|
+
this.#commit();
|
|
404
|
+
// Rebases can activate other batches or null it out, therefore restore the new one here
|
|
405
|
+
current_batch = next_batch;
|
|
406
|
+
}
|
|
398
407
|
}
|
|
399
408
|
|
|
400
409
|
// Edge case: During traversal new branches might create effects that run immediately and set state,
|
|
401
410
|
// causing an effect and therefore a root to be scheduled again. We need to traverse the current batch
|
|
402
411
|
// once more in that case - most of the time this will just clean up dirty branches.
|
|
403
412
|
if (this.#roots.length > 0) {
|
|
404
|
-
if (next_batch
|
|
413
|
+
if (next_batch !== null) {
|
|
414
|
+
const batch = next_batch;
|
|
415
|
+
batch.#roots.push(...this.#roots.filter((r) => !batch.#roots.includes(r)));
|
|
416
|
+
} else {
|
|
405
417
|
next_batch = this;
|
|
406
|
-
this.#link();
|
|
407
418
|
}
|
|
408
|
-
|
|
409
|
-
const batch = next_batch;
|
|
410
|
-
batch.#roots.push(...this.#roots.filter((r) => !batch.#roots.includes(r)));
|
|
411
419
|
}
|
|
412
420
|
|
|
413
421
|
if (next_batch !== null) {
|
|
@@ -500,9 +508,12 @@ export class Batch {
|
|
|
500
508
|
|
|
501
509
|
for (const [effect, deferred] of batch.async_deriveds) {
|
|
502
510
|
const d = this.async_deriveds.get(effect);
|
|
503
|
-
if (d) deferred.promise.then(d.resolve);
|
|
511
|
+
if (d) deferred.promise.then(d.resolve).catch(d.reject);
|
|
504
512
|
}
|
|
505
513
|
|
|
514
|
+
// Mark is not guaranteed not touch these, so we transfer them
|
|
515
|
+
this.transfer_effects(batch.#dirty_effects, batch.#maybe_dirty_effects);
|
|
516
|
+
|
|
506
517
|
/**
|
|
507
518
|
* mark all effects that depend on `batch.current`, except the
|
|
508
519
|
* async effects that we just resolved (TODO unless they depend
|
|
@@ -617,9 +628,9 @@ export class Batch {
|
|
|
617
628
|
discard() {
|
|
618
629
|
for (const fn of this.#discard_callbacks) fn(this);
|
|
619
630
|
this.#discard_callbacks.clear();
|
|
620
|
-
this.#fork_commit_callbacks.clear();
|
|
621
631
|
|
|
622
632
|
this.#unlink();
|
|
633
|
+
this.#deferred?.resolve();
|
|
623
634
|
}
|
|
624
635
|
|
|
625
636
|
/**
|
|
@@ -630,8 +641,6 @@ export class Batch {
|
|
|
630
641
|
}
|
|
631
642
|
|
|
632
643
|
#commit() {
|
|
633
|
-
this.#unlink();
|
|
634
|
-
|
|
635
644
|
// If there are other pending batches, they now need to be 'rebased' —
|
|
636
645
|
// in other words, we re-run block/async effects with the newly
|
|
637
646
|
// committed state, unless the batch in question has a more
|
|
@@ -664,14 +673,16 @@ export class Batch {
|
|
|
664
673
|
// immediately resolving them? Likely not because of how this.apply() works.
|
|
665
674
|
for (const [effect, deferred] of this.async_deriveds) {
|
|
666
675
|
const d = batch.async_deriveds.get(effect);
|
|
667
|
-
if (d) deferred.promise.then(d.resolve);
|
|
676
|
+
if (d) deferred.promise.then(d.resolve).catch(d.reject);
|
|
668
677
|
}
|
|
669
678
|
}
|
|
670
679
|
|
|
671
680
|
if (!batch.#started) continue;
|
|
672
681
|
|
|
673
|
-
// Re-run async/block effects that depend on distinct values changed in both batches
|
|
674
|
-
var others = [...batch.current.keys()].filter(
|
|
682
|
+
// Re-run async/block effects that depend on distinct values changed in both batches (ignoring deriveds)
|
|
683
|
+
var others = [...batch.current.keys()].filter(
|
|
684
|
+
(s) => !(/** @type {[any, boolean]} */ (batch.current.get(s))[1]) && !this.current.has(s)
|
|
685
|
+
);
|
|
675
686
|
|
|
676
687
|
if (others.length === 0) {
|
|
677
688
|
if (is_earlier) {
|
|
@@ -711,11 +722,14 @@ export class Batch {
|
|
|
711
722
|
}
|
|
712
723
|
|
|
713
724
|
checked = new Map();
|
|
714
|
-
var current_unequal = [...batch.current
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
725
|
+
var current_unequal = [...batch.current]
|
|
726
|
+
.filter(([c, v1]) => {
|
|
727
|
+
const v2 = this.current.get(c);
|
|
728
|
+
if (!v2) return true;
|
|
729
|
+
// Either their values are different or one is a derived but not the other
|
|
730
|
+
return v2[0] !== v1[0] || v2[1] !== v1[1];
|
|
731
|
+
})
|
|
732
|
+
.map(([c]) => c);
|
|
719
733
|
|
|
720
734
|
if (current_unequal.length > 0) {
|
|
721
735
|
for (const effect of this.#new_effects) {
|
|
@@ -819,16 +833,6 @@ export class Batch {
|
|
|
819
833
|
this.#discard_callbacks.add(fn);
|
|
820
834
|
}
|
|
821
835
|
|
|
822
|
-
/** @param {(batch: Batch) => void} fn */
|
|
823
|
-
on_fork_commit(fn) {
|
|
824
|
-
this.#fork_commit_callbacks.add(fn);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
run_fork_commit_callbacks() {
|
|
828
|
-
for (const fn of this.#fork_commit_callbacks) fn(this);
|
|
829
|
-
this.#fork_commit_callbacks.clear();
|
|
830
|
-
}
|
|
831
|
-
|
|
832
836
|
settled() {
|
|
833
837
|
return (this.#deferred ??= deferred()).promise;
|
|
834
838
|
}
|
|
@@ -836,7 +840,6 @@ export class Batch {
|
|
|
836
840
|
static ensure() {
|
|
837
841
|
if (current_batch === null) {
|
|
838
842
|
const batch = (current_batch = new Batch());
|
|
839
|
-
batch.#link();
|
|
840
843
|
|
|
841
844
|
if (!is_processing && !is_flushing_sync) {
|
|
842
845
|
queue_micro_task(() => {
|
|
@@ -956,18 +959,11 @@ export class Batch {
|
|
|
956
959
|
this.#roots.push(e);
|
|
957
960
|
}
|
|
958
961
|
|
|
959
|
-
#link() {
|
|
960
|
-
if (last_batch === null) {
|
|
961
|
-
first_batch = last_batch = this;
|
|
962
|
-
} else {
|
|
963
|
-
last_batch.#next = this;
|
|
964
|
-
this.#prev = last_batch;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
last_batch = this;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
962
|
#unlink() {
|
|
963
|
+
// #merge calls #unlink, discard later on does it again - prevent
|
|
964
|
+
// running it multiple times to not corrupt the linked list
|
|
965
|
+
if (!this.linked) return;
|
|
966
|
+
|
|
971
967
|
var prev = this.#prev;
|
|
972
968
|
var next = this.#next;
|
|
973
969
|
|
|
@@ -1397,10 +1393,6 @@ export function fork(fn) {
|
|
|
1397
1393
|
source.wv = increment_write_version();
|
|
1398
1394
|
}
|
|
1399
1395
|
|
|
1400
|
-
batch.activate();
|
|
1401
|
-
batch.run_fork_commit_callbacks();
|
|
1402
|
-
batch.deactivate();
|
|
1403
|
-
|
|
1404
1396
|
// trigger any `$state.eager(...)` expressions with the new state.
|
|
1405
1397
|
// eager effects don't get scheduled like other effects, so we
|
|
1406
1398
|
// can't just encounter them during traversal, we need to
|
|
@@ -187,7 +187,10 @@ export function async_derived(fn, label, location) {
|
|
|
187
187
|
var decrement_pending = increment_pending();
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
if (
|
|
190
|
+
if (
|
|
191
|
+
// boundary can be null if the async derived is inside an $effect.root not connected to the component render tree
|
|
192
|
+
parent.b?.is_rendered()
|
|
193
|
+
) {
|
|
191
194
|
batch.async_deriveds.get(effect)?.reject(OBSOLETE);
|
|
192
195
|
} else {
|
|
193
196
|
// While the boundary is still showing pending, a new run supersedes all older in-flight runs
|
|
@@ -227,9 +230,7 @@ export function async_derived(fn, label, location) {
|
|
|
227
230
|
signal.f ^= ERROR_VALUE;
|
|
228
231
|
}
|
|
229
232
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (DEV && location !== undefined) {
|
|
233
|
+
if (DEV && location !== undefined && !signal.equals(value)) {
|
|
233
234
|
recent_async_deriveds.add(signal);
|
|
234
235
|
|
|
235
236
|
setTimeout(() => {
|
|
@@ -239,6 +240,8 @@ export function async_derived(fn, label, location) {
|
|
|
239
240
|
}
|
|
240
241
|
});
|
|
241
242
|
}
|
|
243
|
+
|
|
244
|
+
internal_set(signal, value);
|
|
242
245
|
}
|
|
243
246
|
|
|
244
247
|
batch.deactivate();
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
EFFECT,
|
|
21
21
|
DESTROYED,
|
|
22
22
|
INERT,
|
|
23
|
-
REACTION_RAN,
|
|
24
23
|
BLOCK_EFFECT,
|
|
25
24
|
ROOT_EFFECT,
|
|
26
25
|
EFFECT_TRANSPARENT,
|
|
@@ -213,7 +212,11 @@ export function user_effect(fn) {
|
|
|
213
212
|
// Non-nested `$effect(...)` in a component should be deferred
|
|
214
213
|
// until the component is mounted
|
|
215
214
|
var flags = /** @type {Effect} */ (active_effect).f;
|
|
216
|
-
var defer =
|
|
215
|
+
var defer =
|
|
216
|
+
!active_reaction &&
|
|
217
|
+
(flags & BRANCH_EFFECT) !== 0 &&
|
|
218
|
+
component_context !== null &&
|
|
219
|
+
!component_context.i;
|
|
217
220
|
|
|
218
221
|
if (defer) {
|
|
219
222
|
// Top-level `$effect(...)` in an unmounted component — defer until mount
|
|
@@ -49,11 +49,11 @@ export function update_pre_prop(fn, d = 1) {
|
|
|
49
49
|
/**
|
|
50
50
|
* The proxy handler for rest props (i.e. `const { x, ...rest } = $props()`).
|
|
51
51
|
* Is passed the full `$$props` object and excludes the named props.
|
|
52
|
-
* @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude:
|
|
52
|
+
* @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Set<string | symbol>, name?: string }>}}
|
|
53
53
|
*/
|
|
54
54
|
const rest_props_handler = {
|
|
55
55
|
get(target, key) {
|
|
56
|
-
if (target.exclude.
|
|
56
|
+
if (target.exclude.has(key)) return;
|
|
57
57
|
return target.props[key];
|
|
58
58
|
},
|
|
59
59
|
set(target, key) {
|
|
@@ -65,7 +65,7 @@ const rest_props_handler = {
|
|
|
65
65
|
return false;
|
|
66
66
|
},
|
|
67
67
|
getOwnPropertyDescriptor(target, key) {
|
|
68
|
-
if (target.exclude.
|
|
68
|
+
if (target.exclude.has(key)) return;
|
|
69
69
|
if (key in target.props) {
|
|
70
70
|
return {
|
|
71
71
|
enumerable: true,
|
|
@@ -75,17 +75,17 @@ const rest_props_handler = {
|
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
has(target, key) {
|
|
78
|
-
if (target.exclude.
|
|
78
|
+
if (target.exclude.has(key)) return false;
|
|
79
79
|
return key in target.props;
|
|
80
80
|
},
|
|
81
81
|
ownKeys(target) {
|
|
82
|
-
return Reflect.ownKeys(target.props).filter((key) => !target.exclude.
|
|
82
|
+
return Reflect.ownKeys(target.props).filter((key) => !target.exclude.has(key));
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* @param {Record<string, unknown>} props
|
|
88
|
-
* @param {string
|
|
88
|
+
* @param {Set<string>} exclude
|
|
89
89
|
* @param {string} [name]
|
|
90
90
|
* @returns {Record<string, unknown>}
|
|
91
91
|
*/
|
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
} from '#client/constants';
|
|
33
33
|
import * as e from '../errors.js';
|
|
34
34
|
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
|
|
35
|
-
import { includes } from '../../shared/utils.js';
|
|
36
35
|
import { tag_proxy } from '../dev/tracing.js';
|
|
37
36
|
import { get_error } from '../../shared/dev.js';
|
|
38
37
|
import { component_context, is_runes } from '../context.js';
|
|
@@ -158,7 +157,7 @@ export function set(source, value, should_proxy = false) {
|
|
|
158
157
|
(!untracking || (active_reaction.f & EAGER_EFFECT) !== 0) &&
|
|
159
158
|
is_runes() &&
|
|
160
159
|
(active_reaction.f & (DERIVED | BLOCK_EFFECT | ASYNC | EAGER_EFFECT)) !== 0 &&
|
|
161
|
-
(current_sources === null || !
|
|
160
|
+
(current_sources === null || !current_sources.has(source))
|
|
162
161
|
) {
|
|
163
162
|
e.state_unsafe_mutation();
|
|
164
163
|
}
|
|
@@ -90,18 +90,14 @@ export function set_active_effect(effect) {
|
|
|
90
90
|
/**
|
|
91
91
|
* When sources are created within a reaction, reading and writing
|
|
92
92
|
* them within that reaction should not cause a re-run
|
|
93
|
-
* @type {null | Source
|
|
93
|
+
* @type {null | Set<Source>}
|
|
94
94
|
*/
|
|
95
95
|
export let current_sources = null;
|
|
96
96
|
|
|
97
97
|
/** @param {Value} value */
|
|
98
98
|
export function push_reaction_value(value) {
|
|
99
99
|
if (active_reaction !== null && (!async_mode_flag || (active_reaction.f & DERIVED) !== 0)) {
|
|
100
|
-
|
|
101
|
-
current_sources = [value];
|
|
102
|
-
} else {
|
|
103
|
-
current_sources.push(value);
|
|
104
|
-
}
|
|
100
|
+
(current_sources ??= new Set()).add(value);
|
|
105
101
|
}
|
|
106
102
|
}
|
|
107
103
|
|
|
@@ -202,7 +198,7 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true)
|
|
|
202
198
|
var reactions = signal.reactions;
|
|
203
199
|
if (reactions === null) return;
|
|
204
200
|
|
|
205
|
-
if (!async_mode_flag && current_sources !== null &&
|
|
201
|
+
if (!async_mode_flag && current_sources !== null && current_sources.has(signal)) {
|
|
206
202
|
return;
|
|
207
203
|
}
|
|
208
204
|
|
|
@@ -540,7 +536,7 @@ export function get(signal) {
|
|
|
540
536
|
// we don't add the dependency, because that would create a memory leak
|
|
541
537
|
var destroyed = active_effect !== null && (active_effect.f & DESTROYED) !== 0;
|
|
542
538
|
|
|
543
|
-
if (!destroyed && (current_sources === null || !
|
|
539
|
+
if (!destroyed && (current_sources === null || !current_sources.has(signal))) {
|
|
544
540
|
var deps = active_reaction.deps;
|
|
545
541
|
|
|
546
542
|
if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) {
|
package/src/utils.js
CHANGED
package/src/version.js
CHANGED