chore: remove `exposable` (#9783)

* use flags for prop_source, this will be useful later

* remove exposable/expose stuff

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/9784/head
Rich Harris 7 months ago committed by GitHub
parent 25abca78b2
commit 10aacfa603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,7 @@
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';
/**
* @template {import('./types').ClientTransformState} State
@ -359,29 +360,43 @@ export function get_props_method(binding, state, name, default_value) {
(state.analysis.immutable ? binding.reassigned : binding.mutated);
if (needs_source) {
args.push(b.literal(state.analysis.immutable));
}
let flags = 0;
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)) {
args.push(default_value);
} else {
if (
default_value.type === 'CallExpression' &&
default_value.callee.type === 'Identifier' &&
default_value.arguments.length === 0
) {
args.push(default_value.callee);
/** @type {import('estree').Expression | undefined} */
let arg;
if (state.analysis.immutable) {
flags |= PROPS_IS_IMMUTABLE;
}
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)) {
arg = default_value;
} else {
args.push(b.thunk(default_value));
if (
default_value.type === 'CallExpression' &&
default_value.callee.type === 'Identifier' &&
default_value.arguments.length === 0
) {
arg = default_value.callee;
} else {
arg = b.thunk(default_value);
}
flags |= PROPS_CALL_DEFAULT_VALUE;
}
}
args.push(b.true);
if (flags || arg) {
args.push(b.literal(flags));
if (arg) args.push(arg);
}
return b.call('$.prop_source', ...args);
}
return b.call(needs_source ? '$.prop_source' : '$.prop', ...args);
return b.call('$.prop', ...args);
}
/**

@ -795,32 +795,17 @@ function serialize_inline_component(node, component_name, context) {
push_prop(
b.get(attribute.name, [
b.return(
b.call(
'$.exposable',
b.thunk(
/** @type {import('estree').Expression} */ (context.visit(attribute.expression))
)
)
/** @type {import('estree').Expression} */ (context.visit(attribute.expression))
)
])
);
// If the binding is just a reference to a top level state variable
// we don't need a setter as the inner component can write to the signal directly
const binding =
attribute.expression.type !== 'Identifier'
? null
: context.state.scope.get(attribute.expression.name);
if (
binding === null ||
(binding.kind !== 'state' && binding.kind !== 'prop' && binding.kind !== 'rest_prop')
) {
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
push_prop(
b.set(attribute.name, [
b.stmt(serialize_set_binding(assignment, context, () => context.visit(assignment)))
])
);
}
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
push_prop(
b.set(attribute.name, [
b.stmt(serialize_set_binding(assignment, context, () => context.visit(assignment)))
])
);
}
}
}

@ -5,6 +5,9 @@ export const EACH_IS_CONTROLLED = 1 << 3;
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;
/** List of Element events that will be delegated */
export const DelegatedEvents = [
'beforeinput',

@ -34,7 +34,6 @@ import {
untrack,
effect,
flushSync,
expose,
safe_not_equal,
current_block,
source,
@ -1193,10 +1192,7 @@ export function bind_prop(props, prop, value) {
/** @param {V | null} value */
const update = (value) => {
const current_props = unwrap(props);
const signal = expose(() => current_props[prop]);
if (is_signal(signal)) {
set(signal, value);
} else if (Object.getOwnPropertyDescriptor(current_props, prop)?.set !== undefined) {
if (get_descriptor(current_props, prop)?.set !== undefined) {
current_props[prop] = value;
}
};

@ -1,7 +1,8 @@
import { DEV } from 'esm-env';
import { subscribe_to_store } from '../../store/utils.js';
import { EMPTY_FUNC, run_all } from '../common.js';
import { get_descriptors, is_array } from './utils.js';
import { get_descriptor, get_descriptors, is_array } from './utils.js';
import { PROPS_CALL_DEFAULT_VALUE, PROPS_IS_IMMUTABLE } from '../../constants.js';
export const SOURCE = 1;
export const DERIVED = 1 << 1;
@ -30,8 +31,7 @@ let current_scheduler_mode = FLUSH_MICROTASK;
// Used for handling scheduling
let is_micro_task_queued = false;
let is_task_queued = false;
// Used for exposing signals
let is_signal_exposed = false;
// Handle effect queues
/** @type {import('./types.js').EffectSignal[]} */
@ -63,8 +63,6 @@ export let current_untracking = false;
/** Exists to opt out of the mutation validation for stores which may be set for the first time during a derivation */
let ignore_mutation_validation = false;
/** @type {null | import('./types.js').Signal} */
let current_captured_signal = null;
// If we are working with a get() chain that has no active container,
// to prevent memory leaks, we skip adding the consumer.
let current_skip_consumer = false;
@ -800,23 +798,6 @@ export function unsubscribe_on_destroy(stores) {
});
}
/**
* Wraps a function and marks execution context so that the last signal read from can be captured
* using the `expose` function.
* @template V
* @param {() => V} fn
* @returns {V}
*/
export function exposable(fn) {
const previous_is_signal_exposed = is_signal_exposed;
try {
is_signal_exposed = true;
return fn();
} finally {
is_signal_exposed = previous_is_signal_exposed;
}
}
/**
* @template V
* @param {import('./types.js').Signal<V>} signal
@ -836,10 +817,6 @@ export function get(signal) {
return signal.v;
}
if (is_signal_exposed && current_should_capture_signal) {
current_captured_signal = signal;
}
if (is_signals_recorded) {
captured_signals.add(signal);
}
@ -906,31 +883,6 @@ export function set_sync(signal, value) {
flushSync(() => set(signal, value));
}
/**
* Invokes a function and captures the last signal that is read during the invocation
* if that signal is read within the `exposable` function context.
* If a signal is captured, it returns the signal instead of the read value.
* @template V
* @param {() => V} possible_signal_fn
* @returns {any}
*/
export function expose(possible_signal_fn) {
const previous_captured_signal = current_captured_signal;
const previous_should_capture_signal = current_should_capture_signal;
current_captured_signal = null;
current_should_capture_signal = true;
try {
const value = possible_signal_fn();
if (current_captured_signal === null) {
return value;
}
return current_captured_signal;
} finally {
current_captured_signal = previous_captured_signal;
current_should_capture_signal = previous_should_capture_signal;
}
}
/**
* Invokes a function and captures all signals that are read during the invocation,
* then invalidates them.
@ -1463,35 +1415,19 @@ export function is_store(val) {
* @template V
* @param {import('./types.js').MaybeSignal<Record<string, unknown>>} props_obj
* @param {string} key
* @param {boolean} immutable
* @param {number} flags
* @param {V | (() => V)} [default_value]
* @param {boolean} [call_default_value]
* @returns {import('./types.js').Signal<V> | (() => V)}
*/
export function prop_source(props_obj, key, immutable, default_value, call_default_value) {
export function prop_source(props_obj, key, flags, default_value) {
const call_default_value = (flags & PROPS_CALL_DEFAULT_VALUE) !== 0;
const immutable = (flags & PROPS_IS_IMMUTABLE) !== 0;
const props = is_signal(props_obj) ? get(props_obj) : props_obj;
const possible_signal = /** @type {import('./types.js').MaybeSignal<V>} */ (
expose(() => props[key])
);
const update_bound_prop = Object.getOwnPropertyDescriptor(props, key)?.set;
const update_bound_prop = get_descriptor(props, key)?.set;
let value = props[key];
const should_set_default_value = value === undefined && default_value !== undefined;
if (
is_signal(possible_signal) &&
possible_signal.v === value &&
update_bound_prop === undefined
) {
if (should_set_default_value) {
set(
possible_signal,
// @ts-expect-error would need a cumbersome method overload to type this
call_default_value ? default_value() : default_value
);
}
return possible_signal;
}
if (should_set_default_value) {
value =
// @ts-expect-error would need a cumbersome method overload to type this
@ -1534,7 +1470,7 @@ export function prop_source(props_obj, key, immutable, default_value, call_defau
}
});
if (is_signal(possible_signal) && update_bound_prop !== undefined) {
if (update_bound_prop !== undefined) {
let ignore_first = !should_set_default_value;
sync_effect(() => {
// Before if to ensure signal dependency is registered
@ -1548,11 +1484,9 @@ export function prop_source(props_obj, key, immutable, default_value, call_defau
return;
}
if (not_equal(immutable, propagating_value, possible_signal.v)) {
ignore_next1 = true;
did_update_to_defined = true;
untrack(() => update_bound_prop(propagating_value));
}
ignore_next1 = true;
did_update_to_defined = true;
untrack(() => update_bound_prop(propagating_value));
});
}

@ -4,8 +4,6 @@ export {
set,
set_sync,
invalidate_inner_signals,
expose,
exposable,
source,
mutable_source,
derived,

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

Loading…
Cancel
Save