I was stupid and forgot how `this` works in POJOs, disregard my last few commits

pull/15593/head
ComputerGuy 6 months ago
parent 6140fdea22
commit 91b7d1df65

@ -1,9 +1,8 @@
/** @import { ObjectExpression, Property, CallExpression, Expression, SpreadElement, Node, Identifier, PrivateIdentifier, Statement } from 'estree' */
/** @import { ObjectExpression, Property, CallExpression, Expression, SpreadElement, Statement } from 'estree' */
/** @import { Context } from '../types' */
import * as b from '../../../../utils/builders.js';
import { get_rune } from '../../../scope.js';
import { should_proxy } from '../utils.js';
import { walk } from 'zimmerframe';
/**
* @param {ObjectExpression} node
@ -11,163 +10,42 @@ import { walk } from 'zimmerframe';
*/
export function ObjectExpression(node, context) {
/**
* @typedef {[string, NonNullable<ReturnType<typeof get_rune>>, '$.state' | '$.derived', Expression, boolean]} ReactiveProperty
* @typedef {[string, NonNullable<ReturnType<typeof get_rune>>]} ReactiveProperty
*/
let has_runes = false;
/**
* @type {Array<{rune: NonNullable<ReturnType<typeof get_rune>>, property: Property & {value: CallExpression}}>}
*/
const reactive_properties = [];
const valid_property_runes = ['$state', '$derived', '$state.raw', '$derived.by'];
/** @type {Statement[]} */
const body = [];
/** @type {Map<Property, ReactiveProperty>} */
const sources = new Map();
let counter = 0;
for (let property of node.properties) {
if (property.type !== 'Property') continue;
const rune = get_rune(property.value, context.state.scope);
if (rune && valid_property_runes.includes(rune)) {
has_runes = true;
reactive_properties.push({
rune,
property: /**@type {Property & {value: CallExpression}} */ (property)
});
const name = context.state.scope.generate(`$$${++counter}`);
const call = rune.match(/^\$state/) ? '$.state' : '$.derived';
/** @type {Expression} */
let value = /** @type {Expression} */ (
context.visit(/** @type {CallExpression} */ (property.value).arguments[0] ?? b.void0)
);
value =
rune === '$derived'
? b.thunk(value)
: rune === '$state' && should_proxy(value, context.state.scope)
? b.call('$.proxy', value)
: value;
/** @type {ReactiveProperty} */
const source = [name, rune];
sources.set(property, source);
body.push(b.let(name, b.call(call, value)));
}
}
if (!has_runes) {
context.next();
return;
}
/** @type {Statement[]} */
const body = [];
/** @type {Map<Property, ReactiveProperty>} */
const sources = new Map();
let has_this_reference = false;
let counter = 0;
/** @type {Statement[]} */
const before = [];
/** @type {Statement[]} */
const after = [];
/** @type {string[]} */
const declarations = [];
/** @type {Map<string, Expression | undefined>} */
const initial_declarations = new Map();
// if a computed property is accessed, we treat it as if all of the object's properties have been accessed
let all_are_referenced = false;
/** @type {Set<any>} */
const is_referenced = new Set();
for (let property of node.properties) {
walk(property, null, {
//@ts-ignore
FunctionExpression() {
return;
},
//@ts-ignore
FunctionDeclaration() {
return;
},
ObjectExpression() {
return;
},
/**
*
* @param {Node} node
* @param {import('zimmerframe').Context<Node, null>} context
*/
ThisExpression(node, context) {
const parent = context.path.at(-1);
if (parent?.type === 'MemberExpression') {
if (parent.computed) {
all_are_referenced = true;
} else {
is_referenced.add(/** @type {Identifier | PrivateIdentifier} */ (parent.property).name);
}
}
},
ClassBody() {
return;
}
});
}
for (let { rune, property } of reactive_properties) {
const name = context.state.scope.generate(`$$${++counter}`);
const call = rune.match(/^\$state/) ? '$.state' : '$.derived';
let references_this = false;
/** @type {Expression} */
let value = /** @type {Expression} */ (context.visit(property.value.arguments[0] ?? b.void0));
value = walk(value, null, {
FunctionExpression() {
return;
},
//@ts-ignore
FunctionDeclaration() {
return;
},
ObjectExpression() {
return;
},
ThisExpression() {
has_this_reference = true;
references_this = true;
return b.id('$$object');
},
ClassBody() {
return;
}
});
value =
rune === '$derived'
? b.thunk(value)
: rune === '$state' && should_proxy(value, context.state.scope)
? b.call('$.proxy', value)
: value;
let key = property.computed
? Symbol()
: property.key.type === 'Literal'
? property.key.value
: /** @type {Identifier} */ (property.key).name;
if (rune.match(/^\$state/) && !(all_are_referenced || is_referenced.has(key))) {
let should_be_declared = false;
walk(value, null, {
CallExpression(node, context) {
should_be_declared = true;
context.stop();
},
MemberExpression(node, context) {
should_be_declared = true;
context.stop();
}
});
if (should_be_declared) {
const value_name = context.state.scope.generate('$$initial');
initial_declarations.set(value_name, value);
value = b.id(value_name);
}
}
/** @type {ReactiveProperty} */
const source = [
name,
rune,
call,
value,
(value.type === 'Identifier' && initial_declarations.has(value.name)) || references_this
];
sources.set(property, source);
if (references_this) {
declarations.push(name);
} else if (source[4]) {
before.push(b.let(name, value));
} else {
before.push(b.let(name, b.call(call, value)));
}
}
if (declarations.length) {
before.push(
b.declaration(
'let',
declarations.map((name) => b.declarator(name))
)
);
}
for (let [name, value] of initial_declarations) {
after.push(b.let(name, value));
}
/** @type {(Property | SpreadElement)[]} */
const properties = [];
for (let property of node.properties) {
@ -176,17 +54,12 @@ export function ObjectExpression(node, context) {
continue;
}
if (sources.has(property)) {
const [name, rune, call, value, initially_declared] = /** @type {ReactiveProperty} */ (
sources.get(property)
);
let maybe_assign = initially_declared
? b.assignment('??=', b.id(name), b.call(call, value))
: b.id(name);
const [name, rune] = /** @type {ReactiveProperty} */ (sources.get(property));
properties.push(
b.prop(
'get',
/** @type {Expression} */ (context.visit(/**@type {Expression} */ (property.key))),
b.function(null, [], b.block([b.return(b.call('$.get', maybe_assign))])),
b.function(null, [], b.block([b.return(b.call('$.get', b.id(name)))])),
property.computed
),
b.prop(
@ -197,12 +70,7 @@ export function ObjectExpression(node, context) {
[b.id('$$value')],
b.block([
b.stmt(
b.call(
'$.set',
maybe_assign,
b.id('$$value'),
rune === '$state' ? b.true : undefined
)
b.call('$.set', b.id(name), b.id('$$value'), rune === '$state' ? b.true : undefined)
)
])
),
@ -213,14 +81,6 @@ export function ObjectExpression(node, context) {
properties.push(/** @type {Property} */ (context.visit(property)));
}
}
if (has_this_reference) {
body.push(...before);
body.push(b.let('$$object', b.object(properties)));
body.push(...after);
body.push(b.return(b.id('$$object')));
} else {
body.push(...before, ...after);
body.push(b.return(b.object(properties)));
}
body.push(b.return(b.object(properties)));
return b.call(b.arrow([], b.block(body)));
}

Loading…
Cancel
Save