pull/15579/head
Rich Harris 1 year ago
commit 954eb8d839

@ -1,5 +0,0 @@
---
'svelte': minor
---
feat: allow state created in deriveds/effects to be written/read locally without self-invalidation

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: simplify set calls for proxyable values

@ -1,5 +1,21 @@
# svelte
## 5.24.0
### Minor Changes
- feat: allow state created in deriveds/effects to be written/read locally without self-invalidation ([#15553](https://github.com/sveltejs/svelte/pull/15553))
### Patch Changes
- fix: check if DOM prototypes are extensible ([#15569](https://github.com/sveltejs/svelte/pull/15569))
- Keep inlined trailing JSDoc comments of properties when running svelte-migrate ([#15567](https://github.com/sveltejs/svelte/pull/15567))
- fix: simplify set calls for proxyable values ([#15548](https://github.com/sveltejs/svelte/pull/15548))
- fix: don't depend on deriveds created inside the current reaction ([#15564](https://github.com/sveltejs/svelte/pull/15564))
## 5.23.2
### Patch Changes

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

@ -1592,7 +1592,6 @@ function extract_type_and_comment(declarator, state, path) {
const comment_start = /** @type {any} */ (comment_node)?.start;
const comment_end = /** @type {any} */ (comment_node)?.end;
let comment = comment_node && str.original.substring(comment_start, comment_end);
if (comment_node) {
str.update(comment_start, comment_end, '');
}
@ -1673,6 +1672,11 @@ function extract_type_and_comment(declarator, state, path) {
state.has_type_or_fallback = true;
const match = /@type {(.+)}/.exec(comment_node.value);
if (match) {
// try to find JSDoc comments after a hyphen `-`
const jsdocComment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value);
if (jsdocComment) {
cleaned_comment += jsdocComment[1]?.trim();
}
return {
type: match[1],
comment: cleaned_comment,
@ -1693,7 +1697,6 @@ function extract_type_and_comment(declarator, state, path) {
};
}
}
return {
type: 'any',
comment: state.uses_ts ? comment : cleaned_comment,

@ -2,7 +2,7 @@
import { hydrate_node, hydrating, set_hydrate_node } from './hydration.js';
import { DEV } from 'esm-env';
import { init_array_prototype_warnings } from '../dev/equality.js';
import { get_descriptor } from '../../shared/utils.js';
import { get_descriptor, is_extensible } from '../../shared/utils.js';
// export these for reference in the compiled code, making global name deduplication unnecessary
/** @type {Window} */
@ -34,26 +34,31 @@ export function init_operations() {
var element_prototype = Element.prototype;
var node_prototype = Node.prototype;
var text_prototype = Text.prototype;
// @ts-ignore
first_child_getter = get_descriptor(node_prototype, 'firstChild').get;
// @ts-ignore
next_sibling_getter = get_descriptor(node_prototype, 'nextSibling').get;
// the following assignments improve perf of lookups on DOM nodes
// @ts-expect-error
element_prototype.__click = undefined;
// @ts-expect-error
element_prototype.__className = undefined;
// @ts-expect-error
element_prototype.__attributes = null;
// @ts-expect-error
element_prototype.__style = undefined;
// @ts-expect-error
element_prototype.__e = undefined;
// @ts-expect-error
Text.prototype.__t = undefined;
if (is_extensible(element_prototype)) {
// the following assignments improve perf of lookups on DOM nodes
// @ts-expect-error
element_prototype.__click = undefined;
// @ts-expect-error
element_prototype.__className = undefined;
// @ts-expect-error
element_prototype.__attributes = null;
// @ts-expect-error
element_prototype.__style = undefined;
// @ts-expect-error
element_prototype.__e = undefined;
}
if (is_extensible(text_prototype)) {
// @ts-expect-error
text_prototype.__t = undefined;
}
if (DEV) {
// @ts-expect-error

@ -101,7 +101,7 @@ export {
text,
props_id
} from './dom/template.js';
export { derived, derived_safe_equal } from './reactivity/deriveds.js';
export { user_derived as derived, derived_safe_equal } from './reactivity/deriveds.js';
export {
effect_tracking,
effect_root,
@ -117,7 +117,7 @@ export {
mutable_source,
mutate,
set,
source as state,
state,
update,
update_pre,
get_options

@ -8,7 +8,8 @@ import {
skip_reaction,
update_reaction,
increment_write_version,
set_active_effect
set_active_effect,
push_reaction_value
} from '../runtime.js';
import { equals, safe_equals } from './equality.js';
import * as e from '../errors.js';
@ -61,6 +62,19 @@ export function derived(fn) {
return signal;
}
/**
* @template V
* @param {() => V} fn
* @returns {Derived<V>}
*/
export function user_derived(fn) {
const d = derived(fn);
push_reaction_value(d);
return d;
}
/**
* @template V
* @param {() => V} fn

@ -15,7 +15,8 @@ import {
set_reaction_sources,
check_dirtiness,
untracking,
is_destroying_effect
is_destroying_effect,
push_reaction_value
} from '../runtime.js';
import { equals, safe_equals } from './equality.js';
import {
@ -93,14 +94,6 @@ export function source(v, o, stack) {
o
};
if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) {
if (reaction_sources === null) {
set_reaction_sources([signal]);
} else {
reaction_sources.push(signal);
}
}
if (DEV && tracing_mode_flag) {
signal.created = stack ?? get_stack('CreatedAt');
signal.debug = null;
@ -109,8 +102,6 @@ export function source(v, o, stack) {
return signal;
}
export { source as state };
/**
* @param {Source} source
* @returns {ValueOptions | undefined}
@ -119,6 +110,20 @@ export function get_options(source) {
return source.o;
}
/**
* @template V
* @param {V} v
* @param {ValueOptions} [o]
* @param {Error | null} [stack]
*/
export function state(v, o, stack) {
const s = source(v, o, stack);
push_reaction_value(s);
return s;
}
/**
* @template V
* @param {V} initial_value

@ -101,6 +101,17 @@ export function set_reaction_sources(sources) {
reaction_sources = sources;
}
/** @param {Value} value */
export function push_reaction_value(value) {
if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) {
if (reaction_sources === null) {
set_reaction_sources([value]);
} else {
reaction_sources.push(value);
}
}
}
/**
* The dependencies of the reaction that is currently being executed. In many cases,
* the dependencies are unchanged between runs, and so this will be `null` unless
@ -875,21 +886,23 @@ export function get(signal) {
// Register the dependency on the current reaction signal.
if (active_reaction !== null && !untracking) {
var deps = active_reaction.deps;
if (signal.rv < read_version) {
signal.rv = read_version;
// If the signal is accessing the same dependencies in the same
// order as it did last time, increment `skipped_deps`
// rather than updating `new_deps`, which creates GC cost
if (new_deps === null && deps !== null && deps[skipped_deps] === signal) {
skipped_deps++;
} else if (new_deps === null) {
new_deps = [signal];
} else if (!skip_reaction || !new_deps.includes(signal)) {
// Normally we can push duplicated dependencies to `new_deps`, but if we're inside
// an unowned derived because skip_reaction is true, then we need to ensure that
// we don't have duplicates
new_deps.push(signal);
if (!reaction_sources?.includes(signal)) {
var deps = active_reaction.deps;
if (signal.rv < read_version) {
signal.rv = read_version;
// If the signal is accessing the same dependencies in the same
// order as it did last time, increment `skipped_deps`
// rather than updating `new_deps`, which creates GC cost
if (new_deps === null && deps !== null && deps[skipped_deps] === signal) {
skipped_deps++;
} else if (new_deps === null) {
new_deps = [signal];
} else if (!skip_reaction || !new_deps.includes(signal)) {
// Normally we can push duplicated dependencies to `new_deps`, but if we're inside
// an unowned derived because skip_reaction is true, then we need to ensure that
// we don't have duplicates
new_deps.push(signal);
}
}
}
} else if (

@ -10,6 +10,7 @@ export var get_descriptors = Object.getOwnPropertyDescriptors;
export var object_prototype = Object.prototype;
export var array_prototype = Array.prototype;
export var get_prototype_of = Object.getPrototypeOf;
export var is_extensible = Object.isExtensible;
/**
* @param {any} thing

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

@ -21,6 +21,9 @@
*/
export let type_no_comment;
/** @type {boolean} type_with_comment - One-line declaration with comment */
export let type_with_comment;
/**
* This is optional
*/
@ -40,4 +43,10 @@
export let inline_multiline_trailing_comment = 'world'; /*
* this is a same-line trailing multiline comment
**/
/** @type {number} [default_value=1] */
export let default_value = 1;
/** @type {number} [comment_default_value=1] - This has a comment and an optional value. */
export let comment_default_value = 1;
</script>

@ -9,12 +9,18 @@
/**
* @typedef {Object} Props
* @property {string} comment - My wonderful comment
@ -22,11 +28,14 @@
* @property {any} one_line - one line comment
* @property {any} no_comment
* @property {boolean} type_no_comment
* @property {boolean} type_with_comment - One-line declaration with comment
* @property {any} [optional] - This is optional
* @property {any} inline_commented - this should stay a comment
* @property {any} inline_commented_merged - This comment should be merged - with this inline comment
* @property {string} [inline_multiline_leading_comment] - this is a same-line leading multiline comment
* @property {string} [inline_multiline_trailing_comment] - this is a same-line trailing multiline comment
* @property {number} [default_value]
* @property {number} [comment_default_value] - This has a comment and an optional value.
*/
/** @type {Props} */
@ -36,10 +45,13 @@
one_line,
no_comment,
type_no_comment,
type_with_comment,
optional = {stuff: true},
inline_commented,
inline_commented_merged,
inline_multiline_leading_comment = 'world',
inline_multiline_trailing_comment = 'world'
inline_multiline_trailing_comment = 'world',
default_value = 1,
comment_default_value = 1
} = $props();
</script>

@ -10,6 +10,6 @@ export default test({
flushSync(() => {
b1.click();
});
assert.deepEqual(logs, ['init 0', 'cleanup 2', null, 'init 2', 'cleanup 4', null, 'init 4']);
assert.deepEqual(logs, ['init 0']);
}
});

@ -0,0 +1,20 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
test({ assert, target, logs }) {
const button = target.querySelector('button');
flushSync(() => button?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>increment</button>
<p>1/2</p
`
);
assert.deepEqual(logs, [0, 0]);
}
});

@ -0,0 +1,26 @@
<script>
class Foo {
value = $state(0);
double = $derived(this.value * 2);
constructor() {
console.log(this.value, this.double);
}
increment() {
this.value++;
}
}
let foo = $state();
$effect(() => {
foo = new Foo();
});
</script>
<button onclick={() => foo.increment()}>increment</button>
{#if foo}
<p>{foo.value}/{foo.double}</p>
{/if}

@ -8,12 +8,7 @@ import {
render_effect,
user_effect
} from '../../src/internal/client/reactivity/effects';
import {
source as state,
set,
update,
update_pre
} from '../../src/internal/client/reactivity/sources';
import { state, set, update, update_pre } from '../../src/internal/client/reactivity/sources';
import type { Derived, Effect, Value } from '../../src/internal/client/types';
import { proxy } from '../../src/internal/client/proxy';
import { derived } from '../../src/internal/client/reactivity/deriveds';

Loading…
Cancel
Save