|
|
|
@ -2,12 +2,13 @@
|
|
|
|
/** @import { Binding } from '#compiler' */
|
|
|
|
/** @import { Binding } from '#compiler' */
|
|
|
|
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
|
|
|
|
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
|
|
|
|
import { dev } from '../../../../state.js';
|
|
|
|
import { dev } from '../../../../state.js';
|
|
|
|
import { build_pattern, extract_paths } from '../../../../utils/ast.js';
|
|
|
|
import { extract_paths } from '../../../../utils/ast.js';
|
|
|
|
import * as b from '#compiler/builders';
|
|
|
|
import * as b from '#compiler/builders';
|
|
|
|
import * as assert from '../../../../utils/assert.js';
|
|
|
|
import * as assert from '../../../../utils/assert.js';
|
|
|
|
import { get_rune } from '../../../scope.js';
|
|
|
|
import { get_rune } from '../../../scope.js';
|
|
|
|
import { get_prop_source, is_prop_source, is_state_source, should_proxy } from '../utils.js';
|
|
|
|
import { get_prop_source, is_prop_source, is_state_source, should_proxy } from '../utils.js';
|
|
|
|
import { is_hoisted_function } from '../../utils.js';
|
|
|
|
import { is_hoisted_function } from '../../utils.js';
|
|
|
|
|
|
|
|
import { get_value } from './shared/declarations.js';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @param {VariableDeclaration} node
|
|
|
|
* @param {VariableDeclaration} node
|
|
|
|
@ -116,7 +117,7 @@ export function VariableDeclaration(node, context) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const args = /** @type {CallExpression} */ (init).arguments;
|
|
|
|
const args = /** @type {CallExpression} */ (init).arguments;
|
|
|
|
const value = args.length > 0 ? /** @type {Expression} */ (context.visit(args[0])) : b.void0;
|
|
|
|
const value = /** @type {Expression} */ (args[0]) ?? b.void0; // TODO do we need the void 0? can we just omit it altogether?
|
|
|
|
|
|
|
|
|
|
|
|
if (rune === '$state' || rune === '$state.raw') {
|
|
|
|
if (rune === '$state' || rune === '$state.raw') {
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@ -137,24 +138,34 @@ export function VariableDeclaration(node, context) {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (declarator.id.type === 'Identifier') {
|
|
|
|
if (declarator.id.type === 'Identifier') {
|
|
|
|
|
|
|
|
const expression = /** @type {Expression} */ (context.visit(value));
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push(
|
|
|
|
declarations.push(
|
|
|
|
b.declarator(declarator.id, create_state_declarator(declarator.id, value))
|
|
|
|
b.declarator(declarator.id, create_state_declarator(declarator.id, expression))
|
|
|
|
);
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const [pattern, replacements] = build_pattern(declarator.id, context.state.scope);
|
|
|
|
const tmp = b.id(context.state.scope.generate('tmp'));
|
|
|
|
|
|
|
|
const { inserts, paths } = extract_paths(declarator.id, tmp);
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push(
|
|
|
|
declarations.push(
|
|
|
|
b.declarator(pattern, value),
|
|
|
|
b.declarator(tmp, value),
|
|
|
|
.../** @type {[Identifier, Identifier][]} */ ([...replacements]).map(
|
|
|
|
...inserts.map(({ id, value }) => {
|
|
|
|
([original, replacement]) => {
|
|
|
|
id.name = context.state.scope.generate('$$array');
|
|
|
|
const binding = context.state.scope.get(original.name);
|
|
|
|
context.state.transform[id.name] = { read: get_value };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
|
|
|
|
|
|
|
|
return b.declarator(id, b.call('$.derived', expression));
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
...paths.map((path) => {
|
|
|
|
|
|
|
|
const value = /** @type {Expression} */ (context.visit(path.expression));
|
|
|
|
|
|
|
|
const binding = context.state.scope.get(/** @type {Identifier} */ (path.node).name);
|
|
|
|
return b.declarator(
|
|
|
|
return b.declarator(
|
|
|
|
original,
|
|
|
|
path.node,
|
|
|
|
binding?.kind === 'state' || binding?.kind === 'raw_state'
|
|
|
|
binding?.kind === 'state' || binding?.kind === 'raw_state'
|
|
|
|
? create_state_declarator(binding.node, replacement)
|
|
|
|
? create_state_declarator(binding.node, value)
|
|
|
|
: replacement
|
|
|
|
: value
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -163,44 +174,41 @@ export function VariableDeclaration(node, context) {
|
|
|
|
|
|
|
|
|
|
|
|
if (rune === '$derived' || rune === '$derived.by') {
|
|
|
|
if (rune === '$derived' || rune === '$derived.by') {
|
|
|
|
if (declarator.id.type === 'Identifier') {
|
|
|
|
if (declarator.id.type === 'Identifier') {
|
|
|
|
declarations.push(
|
|
|
|
let expression = /** @type {Expression} */ (context.visit(value));
|
|
|
|
b.declarator(
|
|
|
|
if (rune === '$derived') expression = b.thunk(expression);
|
|
|
|
declarator.id,
|
|
|
|
|
|
|
|
b.call('$.derived', rune === '$derived.by' ? value : b.thunk(value))
|
|
|
|
declarations.push(b.declarator(declarator.id, b.call('$.derived', expression)));
|
|
|
|
)
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const [pattern, replacements] = build_pattern(declarator.id, context.state.scope);
|
|
|
|
|
|
|
|
const init = /** @type {CallExpression} */ (declarator.init);
|
|
|
|
const init = /** @type {CallExpression} */ (declarator.init);
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Identifier} */
|
|
|
|
|
|
|
|
let id;
|
|
|
|
|
|
|
|
let rhs = value;
|
|
|
|
let rhs = value;
|
|
|
|
|
|
|
|
|
|
|
|
if (rune === '$derived' && init.arguments[0].type === 'Identifier') {
|
|
|
|
if (rune !== '$derived' || init.arguments[0].type !== 'Identifier') {
|
|
|
|
id = init.arguments[0];
|
|
|
|
const id = b.id(context.state.scope.generate('$$d'));
|
|
|
|
} else {
|
|
|
|
|
|
|
|
id = b.id(context.state.scope.generate('$$d'));
|
|
|
|
|
|
|
|
rhs = b.call('$.get', id);
|
|
|
|
rhs = b.call('$.get', id);
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push(
|
|
|
|
let expression = /** @type {Expression} */ (context.visit(value));
|
|
|
|
b.declarator(id, b.call('$.derived', rune === '$derived.by' ? value : b.thunk(value)))
|
|
|
|
if (rune === '$derived') expression = b.thunk(expression);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push(b.declarator(id, b.call('$.derived', expression)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < replacements.size; i++) {
|
|
|
|
const { inserts, paths } = extract_paths(declarator.id, rhs);
|
|
|
|
const [original, replacement] = [...replacements][i];
|
|
|
|
|
|
|
|
declarations.push(
|
|
|
|
for (const { id, value } of inserts) {
|
|
|
|
b.declarator(
|
|
|
|
id.name = context.state.scope.generate('$$array');
|
|
|
|
original,
|
|
|
|
context.state.transform[id.name] = { read: get_value };
|
|
|
|
b.call(
|
|
|
|
|
|
|
|
'$.derived',
|
|
|
|
const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
|
|
|
|
b.arrow([], b.block([b.let(pattern, rhs), b.return(replacement)]))
|
|
|
|
declarations.push(b.declarator(id, b.call('$.derived', expression)));
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
);
|
|
|
|
for (const path of paths) {
|
|
|
|
|
|
|
|
const expression = /** @type {Expression} */ (context.visit(path.expression));
|
|
|
|
|
|
|
|
declarations.push(b.declarator(path.node, b.call('$.derived', b.thunk(expression))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -229,20 +237,29 @@ export function VariableDeclaration(node, context) {
|
|
|
|
if (declarator.id.type !== 'Identifier') {
|
|
|
|
if (declarator.id.type !== 'Identifier') {
|
|
|
|
// Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
|
|
|
|
// Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
|
|
|
|
// means that foo and bar are the props (i.e. the leafs are the prop names), not x and z.
|
|
|
|
// means that foo and bar are the props (i.e. the leafs are the prop names), not x and z.
|
|
|
|
const tmp = context.state.scope.generate('tmp');
|
|
|
|
const tmp = b.id(context.state.scope.generate('tmp'));
|
|
|
|
const paths = extract_paths(declarator.id);
|
|
|
|
const { inserts, paths } = extract_paths(declarator.id, tmp);
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push(
|
|
|
|
declarations.push(
|
|
|
|
b.declarator(
|
|
|
|
b.declarator(
|
|
|
|
b.id(tmp),
|
|
|
|
tmp,
|
|
|
|
/** @type {Expression} */ (context.visit(/** @type {Expression} */ (declarator.init)))
|
|
|
|
/** @type {Expression} */ (context.visit(/** @type {Expression} */ (declarator.init)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const { id, value } of inserts) {
|
|
|
|
|
|
|
|
id.name = context.state.scope.generate('$$array');
|
|
|
|
|
|
|
|
context.state.transform[id.name] = { read: get_value };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
|
|
|
|
|
|
|
|
declarations.push(b.declarator(id, b.call('$.derived', expression)));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const path of paths) {
|
|
|
|
for (const path of paths) {
|
|
|
|
const name = /** @type {Identifier} */ (path.node).name;
|
|
|
|
const name = /** @type {Identifier} */ (path.node).name;
|
|
|
|
const binding = /** @type {Binding} */ (context.state.scope.get(name));
|
|
|
|
const binding = /** @type {Binding} */ (context.state.scope.get(name));
|
|
|
|
const value = path.expression?.(b.id(tmp));
|
|
|
|
const value = /** @type {Expression} */ (context.visit(path.expression));
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push(
|
|
|
|
declarations.push(
|
|
|
|
b.declarator(
|
|
|
|
b.declarator(
|
|
|
|
path.node,
|
|
|
|
path.node,
|
|
|
|
@ -276,7 +293,7 @@ export function VariableDeclaration(node, context) {
|
|
|
|
declarations.push(
|
|
|
|
declarations.push(
|
|
|
|
...create_state_declarators(
|
|
|
|
...create_state_declarators(
|
|
|
|
declarator,
|
|
|
|
declarator,
|
|
|
|
context.state,
|
|
|
|
context,
|
|
|
|
/** @type {Expression} */ (declarator.init && context.visit(declarator.init))
|
|
|
|
/** @type {Expression} */ (declarator.init && context.visit(declarator.init))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
);
|
|
|
|
@ -296,32 +313,41 @@ export function VariableDeclaration(node, context) {
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Creates the output for a state declaration in legacy mode.
|
|
|
|
* Creates the output for a state declaration in legacy mode.
|
|
|
|
* @param {VariableDeclarator} declarator
|
|
|
|
* @param {VariableDeclarator} declarator
|
|
|
|
* @param {ComponentClientTransformState} scope
|
|
|
|
* @param {ComponentContext} context
|
|
|
|
* @param {Expression} value
|
|
|
|
* @param {Expression} value
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function create_state_declarators(declarator, { scope, analysis }, value) {
|
|
|
|
function create_state_declarators(declarator, context, value) {
|
|
|
|
if (declarator.id.type === 'Identifier') {
|
|
|
|
if (declarator.id.type === 'Identifier') {
|
|
|
|
return [
|
|
|
|
return [
|
|
|
|
b.declarator(
|
|
|
|
b.declarator(
|
|
|
|
declarator.id,
|
|
|
|
declarator.id,
|
|
|
|
b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined)
|
|
|
|
b.call('$.mutable_source', value, context.state.analysis.immutable ? b.true : undefined)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
];
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const [pattern, replacements] = build_pattern(declarator.id, scope);
|
|
|
|
const tmp = b.id(context.state.scope.generate('tmp'));
|
|
|
|
|
|
|
|
const { inserts, paths } = extract_paths(declarator.id, tmp);
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
return [
|
|
|
|
b.declarator(pattern, value),
|
|
|
|
b.declarator(tmp, value),
|
|
|
|
.../** @type {[Identifier, Identifier][]} */ ([...replacements]).map(
|
|
|
|
...inserts.map(({ id, value }) => {
|
|
|
|
([original, replacement]) => {
|
|
|
|
id.name = context.state.scope.generate('$$array');
|
|
|
|
const binding = scope.get(original.name);
|
|
|
|
context.state.transform[id.name] = { read: get_value };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
|
|
|
|
|
|
|
|
return b.declarator(id, b.call('$.derived', expression));
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
...paths.map((path) => {
|
|
|
|
|
|
|
|
const value = /** @type {Expression} */ (context.visit(path.expression));
|
|
|
|
|
|
|
|
const binding = context.state.scope.get(/** @type {Identifier} */ (path.node).name);
|
|
|
|
|
|
|
|
|
|
|
|
return b.declarator(
|
|
|
|
return b.declarator(
|
|
|
|
original,
|
|
|
|
path.node,
|
|
|
|
binding?.kind === 'state'
|
|
|
|
binding?.kind === 'state'
|
|
|
|
? b.call('$.mutable_source', replacement, analysis.immutable ? b.true : undefined)
|
|
|
|
? b.call('$.mutable_source', value, context.state.analysis.immutable ? b.true : undefined)
|
|
|
|
: replacement
|
|
|
|
: value
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
];
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|