better default value handling

proxied-state-each-blocks
Rich Harris 1 year ago
parent 0df903eef4
commit 7fc251d620

@ -1,5 +1,5 @@
import * as b from '../../../utils/builders.js';
import { extract_paths } from '../../../utils/ast.js';
import { extract_paths, is_simple_expression } from '../../../utils/ast.js';
import { error } from '../../../errors.js';
/**
@ -353,9 +353,9 @@ export function get_props_method(binding, state, name, default_value) {
// - accessors/mutated: needs to be able to set the prop value from within
// - default value: we set the fallback value only initially, and it's not possible to know this timing in $.prop
const needs_source =
(state.analysis.immutable ? binding.reassigned : binding.mutated) ||
default_value ||
state.analysis.accessors;
state.analysis.accessors ||
(state.analysis.immutable ? binding.reassigned : binding.mutated);
if (needs_source) {
args.push(b.literal(state.analysis.immutable));
@ -363,12 +363,20 @@ export function get_props_method(binding, state, name, default_value) {
if (default_value) {
// To avoid eagerly evaluating the right-hand-side, we wrap it in a thunk if necessary
if (default_value.type !== 'Literal' && default_value.type !== 'Identifier') {
args.push(b.thunk(default_value));
args.push(b.true);
} else {
if (is_simple_expression(default_value)) {
args.push(default_value);
args.push(b.false);
} else {
if (
default_value.type === 'CallExpression' &&
default_value.callee.type === 'Identifier' &&
default_value.arguments.length === 0
) {
args.push(default_value.callee);
} else {
args.push(b.thunk(default_value));
}
args.push(b.true);
}
}

@ -304,3 +304,36 @@ export function get_parent(path, at) {
}
return /** @type {T} */ (node);
}
/**
* Returns `true` if the expression is an identifier, a literal, a function expression,
* or a logical expression that only contains simple expressions. Used to determine whether
* something needs to be treated as though accessing it could have side-effects (i.e.
* reading signals prematurely)
* @param {import('estree').Expression} node
* @returns {boolean}
*/
export function is_simple_expression(node) {
if (
node.type === 'Literal' ||
node.type === 'Identifier' ||
node.type === 'ArrowFunctionExpression' ||
node.type === 'FunctionExpression'
) {
return true;
}
if (node.type === 'ConditionalExpression') {
return (
is_simple_expression(node.test) &&
is_simple_expression(node.consequent) &&
is_simple_expression(node.alternate)
);
}
if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {
return is_simple_expression(node.left) && is_simple_expression(node.right);
}
return false;
}

@ -1520,7 +1520,6 @@ export function prop_source(props_obj, key, immutable, default_value, call_defau
/**
* If the prop is readonly and has no fallback value, we can use this function, else we need to use `prop_source`.
* @template V
* @param {import('./types.js').MaybeSignal<Record<string, unknown>>} props_obj
* @param {string} key
* @returns {any}

@ -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", true, 'hr', false);
let tag = $.prop_source($$props, "tag", true, 'hr');
/* Init */
var fragment = $.comment($$anchor);
var node = $.child_frag(fragment);

Loading…
Cancel
Save