From 5dc98303db89bcb1e2d1d925a2de54aee18459c8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 5 Dec 2023 14:52:02 -0500 Subject: [PATCH 1/8] WIP --- packages/svelte/src/internal/client/runtime.js | 5 +++++ packages/svelte/tests/runtime-legacy/shared.ts | 5 +++++ .../proxy-prop-default-readonly/Counter.svelte | 8 ++++++++ .../proxy-prop-default-readonly/_config.js | 18 ++++++++++++++++++ .../proxy-prop-default-readonly/main.svelte | 5 +++++ 5 files changed, 41 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/Counter.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/main.svelte diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 462372f83f97..4c3f82c66745 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -3,6 +3,7 @@ import { subscribe_to_store } from '../../store/utils.js'; import { EMPTY_FUNC, run_all } from '../common.js'; import { get_descriptor, get_descriptors, is_array } from './utils.js'; import { PROPS_CALL_DEFAULT_VALUE, PROPS_IS_IMMUTABLE, PROPS_IS_RUNES } from '../../constants.js'; +import { readonly } from './proxy/readonly.js'; export const SOURCE = 1; export const DERIVED = 1 << 1; @@ -1437,6 +1438,10 @@ export function prop_source(props_obj, key, flags, default_value) { value = // @ts-expect-error would need a cumbersome method overload to type this call_default_value ? default_value() : default_value; + + if (DEV && immutable) { + value = readonly(/** @type {any} */ (value)); + } } const source_signal = immutable ? source(value) : mutable_source(value); diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index a1944d47e5f3..63c4ca10c9bd 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -305,6 +305,11 @@ async function run_test_variant( compileOptions }); } + + if (config.runtime_error) { + unintended_error = true; + assert.fail('Expected a runtime error'); + } } finally { instance.$destroy(); assert_html_equal( diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/Counter.svelte new file mode 100644 index 000000000000..91ccc2af8188 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/Counter.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js new file mode 100644 index 000000000000..5e0eb0e2da60 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js @@ -0,0 +1,18 @@ +import { test } from '../../test'; + +export default test({ + html: ``, + + compileOptions: { + dev: true + }, + + async test({ assert, target }) { + const btn = target.querySelector('button'); + await btn?.click(); + + assert.htmlEqual(target.innerHTML, ``); + }, + + runtime_error: 'Props are read-only, unless used with `bind:`' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/main.svelte b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/main.svelte new file mode 100644 index 000000000000..391afc4b232e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/main.svelte @@ -0,0 +1,5 @@ + + + From c263119408521a68170c56f098057f354758474f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 5 Dec 2023 15:16:52 -0500 Subject: [PATCH 2/8] update tests --- .../class-state-derived-unowned/_config.js | 15 ++++++++++----- .../samples/class-state-derived-unowned/log.js | 1 + .../class-state-derived-unowned/main.svelte | 14 +++++++------- .../samples/class-state-init-eager-2/_config.js | 9 +++++++-- .../samples/class-state-init-eager-2/log.js | 1 + .../samples/class-state-init-eager-2/main.svelte | 2 +- .../samples/class-state-init-eager-3/_config.js | 9 +++++++-- .../samples/class-state-init-eager-3/log.js | 1 + .../samples/class-state-init-eager-3/main.svelte | 2 +- .../samples/class-state-init-eager/_config.js | 9 +++++++-- .../samples/class-state-init-eager/log.js | 1 + .../samples/class-state-init-eager/main.svelte | 2 +- .../samples/event-attribute-template/_config.js | 9 +++++++-- .../samples/event-attribute-template/log.js | 1 + .../samples/event-attribute-template/main.svelte | 2 +- 15 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-state-init-eager-2/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-state-init-eager-3/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-state-init-eager/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-template/log.js diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/_config.js index d73f3351a30c..e0f3a58f3ff2 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/_config.js @@ -1,36 +1,41 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; +import { log } from './log.js'; export default test({ // The component context class instance gets shared between tests, strangely, causing hydration to fail? skip_if_hydrate: 'permanent', - async test({ assert, target, component }) { + before_test() { + log.length = 0; + }, + + async test({ assert, target }) { const btn = target.querySelector('button'); flushSync(() => { btn?.click(); }); - assert.deepEqual(component.log, [0, 'class trigger false', 'local trigger false', 1]); + assert.deepEqual(log, [0, 'class trigger false', 'local trigger false', 1]); flushSync(() => { btn?.click(); }); - assert.deepEqual(component.log, [0, 'class trigger false', 'local trigger false', 1, 2]); + assert.deepEqual(log, [0, 'class trigger false', 'local trigger false', 1, 2]); flushSync(() => { btn?.click(); }); - assert.deepEqual(component.log, [0, 'class trigger false', 'local trigger false', 1, 2, 3]); + assert.deepEqual(log, [0, 'class trigger false', 'local trigger false', 1, 2, 3]); flushSync(() => { btn?.click(); }); - assert.deepEqual(component.log, [ + assert.deepEqual(log, [ 0, 'class trigger false', 'local trigger false', diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/log.js b/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/log.js new file mode 100644 index 000000000000..0abf1fa9159e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/log.js @@ -0,0 +1 @@ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/main.svelte index 58b96457dbb6..6f1c26db048e 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/class-state-derived-unowned/main.svelte @@ -1,17 +1,17 @@ + + diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly-reassigned/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly-reassigned/_config.js new file mode 100644 index 000000000000..9787b1ca9271 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly-reassigned/_config.js @@ -0,0 +1,19 @@ +import { test } from '../../test'; + +export default test({ + html: ``, + + compileOptions: { + dev: true + }, + + async test({ assert, target }) { + const btn = target.querySelector('button'); + + await btn?.click(); + assert.htmlEqual(target.innerHTML, ``); + + await btn?.click(); + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly-reassigned/main.svelte b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly-reassigned/main.svelte new file mode 100644 index 000000000000..391afc4b232e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly-reassigned/main.svelte @@ -0,0 +1,5 @@ + + + From 23064b1d21fba923905163881f107719f084b784 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 5 Dec 2023 15:55:38 -0500 Subject: [PATCH 8/8] tweak message --- packages/svelte/src/internal/client/proxy/readonly.js | 2 +- .../samples/proxy-prop-default-readonly/_config.js | 2 +- .../tests/runtime-runes/samples/proxy-prop-readonly/_config.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/internal/client/proxy/readonly.js b/packages/svelte/src/internal/client/proxy/readonly.js index fa698c3b0830..f0dbea76a194 100644 --- a/packages/svelte/src/internal/client/proxy/readonly.js +++ b/packages/svelte/src/internal/client/proxy/readonly.js @@ -44,7 +44,7 @@ export function readonly(value) { /** @returns {never} */ const readonly_error = () => { - throw new Error(`Props are read-only, unless used with \`bind:\``); + throw new Error(`Props cannot be mutated, unless used with \`bind:\``); }; /** @type {ProxyHandler>} */ diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js index 5e0eb0e2da60..65d89b5f46c7 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/proxy-prop-default-readonly/_config.js @@ -14,5 +14,5 @@ export default test({ assert.htmlEqual(target.innerHTML, ``); }, - runtime_error: 'Props are read-only, unless used with `bind:`' + runtime_error: 'Props cannot be mutated, unless used with `bind:`' }); diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-prop-readonly/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-prop-readonly/_config.js index 5e0eb0e2da60..65d89b5f46c7 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-prop-readonly/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/proxy-prop-readonly/_config.js @@ -14,5 +14,5 @@ export default test({ assert.htmlEqual(target.innerHTML, ``); }, - runtime_error: 'Props are read-only, unless used with `bind:`' + runtime_error: 'Props cannot be mutated, unless used with `bind:`' });