diff --git a/CHANGELOG.md b/CHANGELOG.md index d1e76c1705..3ad9a97b43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,11 @@ * **breaking** Stricter types for `onMount` - now throws a type error when returning a function asynchronously to catch potential mistakes around callback functions (see PR for migration instructions) ([#8136](https://github.com/sveltejs/svelte/pull/8136)) * **breaking** Overhaul and drastically improve creating custom elements with Svelte (see PR for list of changes and migration instructions) ([#8457](https://github.com/sveltejs/svelte/pull/8457)) * **breaking** Deprecate `SvelteComponentTyped`, use `SvelteComponent` instead ([#8512](https://github.com/sveltejs/svelte/pull/8512)) +* **breaking** Error on falsy values instead of stores passed to `derived` ([#7947](https://github.com/sveltejs/svelte/pull/7947)) * Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391)) * Add `a11y-no-static-element-interactions`rule ([#8251](https://github.com/sveltejs/svelte/pull/8251)) * Bind `null` option and input values consistently ([#8312](https://github.com/sveltejs/svelte/issues/8312)) +* Allow `$store` to be used with changing values including nullish values ([#7555](https://github.com/sveltejs/svelte/issues/7555)) ## Unreleased (3.0) diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 1507bad541..9d48378603 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -68,6 +68,9 @@ export function validate_store(store, name) { export function subscribe(store, ...callbacks) { if (store == null) { + for (const callback of callbacks) { + callback(undefined); + } return noop; } const unsub = store.subscribe(...callbacks); diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index 09e5b10bd2..319f5a4d2e 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -164,9 +164,10 @@ export function derived( export function derived(stores: Stores, fn: Function, initial_value?: T): Readable { const single = !Array.isArray(stores); - const stores_array: Array> = single - ? [stores as Readable] - : stores as Array>; + const stores_array: Array> = single ? [stores as Readable] : stores as Array>; + if (!stores_array.every(Boolean)) { + throw new Error('derived() expects stores as input, got a falsy value'); + } const auto = fn.length < 2; diff --git a/test/runtime/samples/store-auto-subscribe-removed-store/_config.js b/test/runtime/samples/store-auto-subscribe-removed-store/_config.js new file mode 100644 index 0000000000..12297a3b8a --- /dev/null +++ b/test/runtime/samples/store-auto-subscribe-removed-store/_config.js @@ -0,0 +1,21 @@ +import { writable } from '../../../../store'; + +export default { + html: ` +

undefined

+ `, + async test({ assert, component, target }) { + component.store = writable('foo'); + assert.htmlEqual(target.innerHTML, ` +

foo

+ `); + component.store = undefined; + assert.htmlEqual(target.innerHTML, ` +

undefined

+ `); + component.store = writable('bar'); + assert.htmlEqual(target.innerHTML, ` +

bar

+ `); + } +}; diff --git a/test/runtime/samples/store-auto-subscribe-removed-store/main.svelte b/test/runtime/samples/store-auto-subscribe-removed-store/main.svelte new file mode 100644 index 0000000000..9c1ed4a560 --- /dev/null +++ b/test/runtime/samples/store-auto-subscribe-removed-store/main.svelte @@ -0,0 +1,5 @@ + + +

{$store}

diff --git a/test/store/index.js b/test/store/index.js index 29d233495c..7487903ed4 100644 --- a/test/store/index.js +++ b/test/store/index.js @@ -428,6 +428,21 @@ describe('store', () => { a.set(false); assert.equal(b_started, false); }); + + it('errors on undefined stores #1', () => { + assert.throws(() => { + derived(null, (n) => n); + }); + }); + + it('errors on undefined stores #2', () => { + assert.throws(() => { + const a = writable(1); + derived([a, null, undefined], ([n]) => { + return n * 2; + }); + }); + }); }); describe('get', () => {