alternative approach

pull/12985/head
Dominic Gannaway 2 years ago
parent 4ed816864d
commit 5386440304

@ -1,8 +1,6 @@
/** @import { ArrowFunctionExpression, BlockStatement, FunctionExpression, Node, Pattern } from 'estree' */
/** @import { ArrowFunctionExpression, FunctionExpression, Node } from 'estree' */
/** @import { ComponentContext } from '../../types' */
import { build_hoisted_params } from '../../utils.js';
import * as b from '../../../../../utils/builders.js';
import { walk } from 'zimmerframe';
/**
* @param {ArrowFunctionExpression | FunctionExpression} node
@ -12,27 +10,10 @@ export const visit_function = (node, context) => {
const metadata = node.metadata;
let state = { ...context.state, in_constructor: false };
let in_constructor = false;
let has_super_call = false;
if (node.type === 'FunctionExpression') {
const parent = /** @type {Node} */ (context.path.at(-1));
in_constructor = state.in_constructor =
parent.type === 'MethodDefinition' && parent.kind === 'constructor';
// We might not use runes in the immediate class, but we do in the extended class
// so we need to inject the enter/leave logic in this constructor if it does a super()
// call
if (in_constructor) {
walk(node.body, null, {
// @ts-expect-error
CallExpression(node) {
if (node.callee.type === 'Super') {
has_super_call = true;
}
}
});
}
state.in_constructor = parent.type === 'MethodDefinition' && parent.kind === 'constructor';
}
if (metadata?.hoisted === true) {
@ -45,25 +26,5 @@ export const visit_function = (node, context) => {
});
}
if (
state.analysis.runes &&
in_constructor &&
(has_super_call || state.private_state.size > 0 || state.public_state.size > 0)
) {
const params = /** @type {Pattern[]} */ (node.params.map((p) => context.visit(p, state)));
const body = /** @type {BlockStatement} */ (context.visit(node.body, state));
const enter = b.var('$$', b.call('$.enter_constructor'));
const leave = b.stmt(b.call('$.leave_constructor', b.id('$$')));
body.body.splice(0, 0, enter);
body.body.push(leave);
return {
...node,
params,
body
};
} else {
context.next(state);
}
context.next(state);
};

@ -105,14 +105,7 @@ export {
user_effect,
user_pre_effect
} from './reactivity/effects.js';
export {
mutable_source,
mutate,
source,
set,
enter_constructor,
leave_constructor
} from './reactivity/sources.js';
export { mutable_source, mutate, source, set } from './reactivity/sources.js';
export {
prop,
rest_props,

@ -1,4 +1,4 @@
/** @import { Derived } from '#client' */
/** @import { Derived, Source } from '#client' */
import { DEV } from 'esm-env';
import { CLEAN, DERIVED, DESTROYED, DIRTY, MAYBE_DIRTY, UNOWNED } from '../constants.js';
import {
@ -14,6 +14,20 @@ import {
import { equals, safe_equals } from './equality.js';
import * as e from '../errors.js';
/**
* When sources are created within a derived, we record them so that we can safely allow
* local mutations to these sources without the side-effect error being invoked unnecessarily.
* @type {null | Source[]}
*/
export let derived_sources = null;
/**
* @param {Source[] | null} sources
*/
export function set_derived_sources(sources) {
derived_sources = sources;
}
/**
* @template V
* @param {() => V} fn
@ -108,7 +122,13 @@ export function update_derived(derived) {
}
} else {
destroy_derived_children(derived);
value = update_reaction(derived);
const prev_derived_sources = derived_sources;
derived_sources = null;
try {
value = update_reaction(derived);
} finally {
derived_sources = prev_derived_sources;
}
}
var status =

@ -1,4 +1,4 @@
/** @import { Derived, Effect, Source, Value, Reaction } from '#client' */
/** @import { Derived, Effect, Source, Value } from '#client' */
import { DEV } from 'esm-env';
import {
current_component_context,
@ -26,16 +26,9 @@ import {
MAYBE_DIRTY
} from '../constants.js';
import * as e from '../errors.js';
import { derived_sources, set_derived_sources } from './deriveds.js';
let inspect_effects = new Set();
/**
* When we enter a class constructor, we set `constructor_reaction` and then reset it when we leave the
* constructor. When setting source signals, we avoid firing the mutations inside derived error if the derived
* reaction matches `constructor_reaction`.
*
* @type {Reaction | null}
* */
let constructor_reaction = null;
/**
* @template V
@ -44,13 +37,21 @@ let constructor_reaction = null;
*/
/*#__NO_SIDE_EFFECTS__*/
export function source(v) {
return {
var source = {
f: 0, // TODO ideally we could skip this altogether, but it causes type errors
v,
reactions: null,
equals,
version: 0
};
if (current_reaction !== null && (current_reaction.f & DERIVED) !== 0) {
if (derived_sources === null) {
set_derived_sources([source]);
} else {
derived_sources.push(source);
}
}
return source;
}
/**
@ -94,9 +95,11 @@ export function mutate(source, value) {
export function set(source, value) {
if (
current_reaction !== null &&
current_reaction !== constructor_reaction &&
is_runes() &&
(current_reaction.f & DERIVED) !== 0
(current_reaction.f & DERIVED) !== 0 &&
// If the source was created locally within the current derived, then
// we allow the mutation.
(derived_sources === null || !derived_sources.includes(source))
) {
e.state_unsafe_mutation();
}
@ -182,16 +185,3 @@ function mark_reactions(signal, status) {
}
}
}
export function enter_constructor() {
const prev = constructor_reaction;
constructor_reaction = current_reaction;
return prev;
}
/**
* @param {Reaction | null} reaction
*/
export function leave_constructor(reaction) {
constructor_reaction = reaction;
}

@ -15,7 +15,9 @@
"end": 36,
"type": "StyleDirective",
"name": "color",
"modifiers": ["important"],
"modifiers": [
"important"
],
"value": [
{
"type": "MustacheTag",
@ -45,4 +47,4 @@
}
]
}
}
}

@ -9,8 +9,11 @@
"start": 0,
"end": 31,
"data": " svelte-ignore foo, bar ",
"ignores": ["foo", "bar"]
"ignores": [
"foo",
"bar"
]
}
]
}
}
}

@ -18,11 +18,8 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro
#b = $.source();
constructor() {
var $$ = $.enter_constructor();
this.a = 1;
this.#b.v = 2;
$.leave_constructor($$);
}
}

Loading…
Cancel
Save