feat: disallow fallback values with bindings in runes mode (#9784)

* disallow fallback values with bindings in runes mode

* on second thoughts

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/9791/head
Rich Harris 1 year ago committed by GitHub
parent c7e626ebbb
commit 24777c335a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
feat: disallow fallback values with bindings in runes mode

@ -1,7 +1,11 @@
import * as b from '../../../utils/builders.js';
import { extract_paths, is_simple_expression } from '../../../utils/ast.js';
import { error } from '../../../errors.js';
import { PROPS_CALL_DEFAULT_VALUE, PROPS_IS_IMMUTABLE } from '../../../../constants.js';
import {
PROPS_CALL_DEFAULT_VALUE,
PROPS_IS_IMMUTABLE,
PROPS_IS_RUNES
} from '../../../../constants.js';
/**
* @template {import('./types').ClientTransformState} State
@ -369,6 +373,10 @@ export function get_props_method(binding, state, name, default_value) {
flags |= PROPS_IS_IMMUTABLE;
}
if (state.analysis.runes) {
flags |= PROPS_IS_RUNES;
}
if (default_value) {
// To avoid eagerly evaluating the right-hand-side, we wrap it in a thunk if necessary
if (is_simple_expression(default_value)) {

@ -6,7 +6,8 @@ export const EACH_IS_ANIMATED = 1 << 4;
export const EACH_IS_IMMUTABLE = 1 << 6;
export const PROPS_IS_IMMUTABLE = 1;
export const PROPS_CALL_DEFAULT_VALUE = 1 << 1;
export const PROPS_IS_RUNES = 1 << 1;
export const PROPS_CALL_DEFAULT_VALUE = 1 << 2;
/** List of Element events that will be delegated */
export const DelegatedEvents = [

@ -2,7 +2,7 @@ import { DEV } from 'esm-env';
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 } from '../../constants.js';
import { PROPS_CALL_DEFAULT_VALUE, PROPS_IS_IMMUTABLE, PROPS_IS_RUNES } from '../../constants.js';
export const SOURCE = 1;
export const DERIVED = 1 << 1;
@ -1428,6 +1428,11 @@ export function prop_source(props_obj, key, flags, default_value) {
let value = props[key];
const should_set_default_value = value === undefined && default_value !== undefined;
if (update_bound_prop && default_value !== undefined && (flags & PROPS_IS_RUNES) !== 0) {
// TODO consolidate all these random runtime errors
throw new Error('Cannot use fallback values with bind:');
}
if (should_set_default_value) {
value =
// @ts-expect-error would need a cumbersome method overload to type this

@ -12,5 +12,7 @@ export default test({
await btn?.click();
assert.htmlEqual(target.innerHTML, `<button>1</button><span>1</span>`);
}
},
error: `Cannot use fallback values with bind:`
});

@ -10,7 +10,6 @@ export default test({
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault: 1
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
@ -20,27 +19,24 @@ export default test({
readonly: 0
readonlyWithDefault: 0
binding: 0
bindingWithDefault: 0
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault: 1
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding: 0
bindingWithDefault: 0
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
@ -50,19 +46,13 @@ export default test({
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined: 0
readonlyWithDefault_defined: 0
binding_defined: 0
bindingWithDefault_defined: 0
bind_readonly_undefined:
bind_readonlyWithDefault_undefined: 1
bind_binding_undefined:
bind_bindingWithDefault_undefined: 1
bind_readonly_defined: 0
bind_readonlyWithDefault_defined: 0
bind_binding_defined: 0
bind_bindingWithDefault_defined: 0
</p>
<button>set everything to 10</button>
@ -93,64 +83,54 @@ export default test({
`
<p>props undefined:</p>
<p>
readonly:
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault: 1
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined: 0
readonlyWithDefault_defined: 0
binding_defined: 0
bindingWithDefault_defined: 0
bind_readonly_undefined:
bind_readonlyWithDefault_undefined: 1
bind_binding_undefined:
bind_bindingWithDefault_undefined:
bind_readonly_defined: 0
bind_readonlyWithDefault_defined: 0
bind_binding_defined:
bind_bindingWithDefault_defined:
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`
@ -169,61 +149,51 @@ export default test({
readonly:
readonlyWithDefault: 1
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault: 1
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 0
readonlyWithDefault: 0
binding: 5
bindingWithDefault: 5
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined: 0
readonlyWithDefault_defined: 0
binding_defined: 0
bindingWithDefault_defined: 0
bind_readonly_undefined:
bind_readonlyWithDefault_undefined: 1
bind_binding_undefined: 5
bind_bindingWithDefault_undefined: 5
bind_readonly_defined: 0
bind_readonlyWithDefault_defined: 0
bind_binding_defined: 5
bind_bindingWithDefault_defined: 5
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`
@ -239,61 +209,51 @@ export default test({
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly: 10
readonlyWithDefault: 10
binding: 10
bindingWithDefault: 10
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined: 10
readonlyWithDefault_undefined: 10
binding_undefined: 10
bindingWithDefault_undefined: 10
readonly_defined: 10
readonlyWithDefault_defined: 10
binding_defined: 10
bindingWithDefault_defined: 10
bind_readonly_undefined: 10
bind_readonlyWithDefault_undefined: 10
bind_binding_undefined: 10
bind_bindingWithDefault_undefined: 10
bind_readonly_defined: 10
bind_readonlyWithDefault_defined: 10
bind_binding_defined: 10
bind_bindingWithDefault_defined: 10
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`
@ -309,61 +269,51 @@ export default test({
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>props defined:</p>
<p>
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings undefined:</p>
<p>
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>bindings defined:</p>
<p>
readonly:
readonlyWithDefault:
binding:
bindingWithDefault:
</p>
<button>set bindings to 5</button>
<button>set bindings to undefined</button>
<p>
Main:
readonly_undefined:
readonlyWithDefault_undefined:
binding_undefined:
bindingWithDefault_undefined:
readonly_defined:
readonlyWithDefault_defined:
binding_defined:
bindingWithDefault_defined:
bind_readonly_undefined:
bind_readonlyWithDefault_undefined:
bind_binding_undefined:
bind_bindingWithDefault_undefined:
bind_readonly_defined:
bind_readonlyWithDefault_defined:
bind_binding_defined:
bind_bindingWithDefault_defined:
</p>
<button>set everything to 10</button>
<button>set everything to undefined</button>
`

@ -1,20 +1,17 @@
<script>
let { readonly, readonlyWithDefault = 1, binding, bindingWithDefault = 1 } = $props();
let { readonly, readonlyWithDefault = 1, binding } = $props();
</script>
<p>
readonly: {readonly}
readonlyWithDefault: {readonlyWithDefault}
binding: {binding}
bindingWithDefault: {bindingWithDefault}
</p>
<button on:click={() => {
binding = 5;
bindingWithDefault = 5;
}}>set bindings to 5</button>
<button on:click={() => {
binding = undefined;
bindingWithDefault = undefined;
}}>set bindings to undefined</button>

@ -4,19 +4,13 @@
let readonly_undefined = $state();
let readonlyWithDefault_undefined = $state();
let binding_undefined = $state();
let bindingWithDefault_undefined = $state();
let readonly_defined = $state(0);
let readonlyWithDefault_defined = $state(0);
let binding_defined = $state(0);
let bindingWithDefault_defined = $state(0);
let bind_readonly_undefined = $state();
let bind_readonlyWithDefault_undefined = $state();
let bind_binding_undefined = $state();
let bind_bindingWithDefault_undefined = $state();
let bind_readonly_defined = $state(0);
let bind_readonlyWithDefault_defined = $state(0);
let bind_binding_defined = $state(0);
let bind_bindingWithDefault_defined = $state(0);
</script>
<p>props undefined:</p>
@ -24,7 +18,6 @@
readonly={readonly_undefined}
readonlyWithDefault={readonlyWithDefault_undefined}
binding={binding_undefined}
bindingWithDefault={bindingWithDefault_undefined}
/>
<p>props defined:</p>
@ -32,23 +25,20 @@
readonly={readonly_defined}
readonlyWithDefault={readonlyWithDefault_defined}
binding={binding_defined}
bindingWithDefault={bindingWithDefault_defined}
/>
<p>bindings undefined:</p>
<Inner
bind:readonly={bind_readonly_undefined}
bind:readonlyWithDefault={bind_readonlyWithDefault_undefined}
readonlyWithDefault={readonlyWithDefault_undefined}
bind:binding={bind_binding_undefined}
bind:bindingWithDefault={bind_bindingWithDefault_undefined}
/>
<p>bindings defined:</p>
<Inner
bind:readonly={bind_readonly_defined}
bind:readonlyWithDefault={bind_readonlyWithDefault_defined}
readonlyWithDefault={readonlyWithDefault_defined}
bind:binding={bind_binding_defined}
bind:bindingWithDefault={bind_bindingWithDefault_defined}
/>
<p>
@ -56,55 +46,37 @@
readonly_undefined: {readonly_undefined}
readonlyWithDefault_undefined: {readonlyWithDefault_undefined}
binding_undefined: {binding_undefined}
bindingWithDefault_undefined: {bindingWithDefault_undefined}
readonly_defined: {readonly_defined}
readonlyWithDefault_defined: {readonlyWithDefault_defined}
binding_defined: {binding_defined}
bindingWithDefault_defined: {bindingWithDefault_defined}
bind_readonly_undefined: {bind_readonly_undefined}
bind_readonlyWithDefault_undefined: {bind_readonlyWithDefault_undefined}
bind_binding_undefined: {bind_binding_undefined}
bind_bindingWithDefault_undefined: {bind_bindingWithDefault_undefined}
bind_readonly_defined: {bind_readonly_defined}
bind_readonlyWithDefault_defined: {bind_readonlyWithDefault_defined}
bind_binding_defined: {bind_binding_defined}
bind_bindingWithDefault_defined: {bind_bindingWithDefault_defined}
</p>
<button on:click={() => {
readonly_undefined = 10;
readonlyWithDefault_undefined = 10;
binding_undefined = 10;
bindingWithDefault_undefined = 10;
readonly_defined = 10;
readonlyWithDefault_defined = 10;
binding_defined = 10;
bindingWithDefault_defined = 10;
bind_readonly_undefined = 10;
bind_readonlyWithDefault_undefined = 10;
bind_binding_undefined = 10;
bind_bindingWithDefault_undefined = 10;
bind_readonly_defined = 10;
bind_readonlyWithDefault_defined = 10;
bind_binding_defined = 10;
bind_bindingWithDefault_defined = 10;
}}>set everything to 10</button>
<button on:click={() => {
readonly_undefined = undefined;
readonlyWithDefault_undefined = undefined;
binding_undefined = undefined;
bindingWithDefault_undefined = undefined;
readonly_defined = undefined;
readonlyWithDefault_defined = undefined;
binding_defined = undefined;
bindingWithDefault_defined = undefined;
bind_readonly_undefined = undefined;
bind_readonlyWithDefault_undefined = undefined;
bind_binding_undefined = undefined;
bind_bindingWithDefault_undefined = undefined;
bind_readonly_defined = undefined;
bind_readonlyWithDefault_defined = undefined;
bind_binding_defined = undefined;
bind_bindingWithDefault_defined = undefined;
}}>set everything to undefined</button>

@ -6,7 +6,7 @@ import * as $ from "svelte/internal";
export default function Svelte_element($$anchor, $$props) {
$.push($$props, true);
let tag = $.prop_source($$props, "tag", 1, 'hr');
let tag = $.prop_source($$props, "tag", 3, 'hr');
/* Init */
var fragment = $.comment($$anchor);
var node = $.child_frag(fragment);

Loading…
Cancel
Save