evaluate keys, remove closure for `ClassDeclaration`

gh-17012
ComputerGuy 1 day ago
parent 4f757ef271
commit 02194d68f4

@ -1,6 +1,6 @@
import type { Scope } from '../scope.js';
import type { ComponentAnalysis, ReactiveStatement } from '../types.js';
import type { AST, ExpressionMetadata, StateFields, ValidatedCompileOptions } from '#compiler';
import type { AST, StateFields, ValidatedCompileOptions } from '#compiler';
import type { ExpressionMetadata } from '../nodes.js';
export interface AnalysisState {

@ -47,6 +47,7 @@ import { RenderTag } from './visitors/RenderTag.js';
import { SlotElement } from './visitors/SlotElement.js';
import { SnippetBlock } from './visitors/SnippetBlock.js';
import { SpreadAttribute } from './visitors/SpreadAttribute.js';
import { StaticBlock } from './visitors/StaticBlock.js';
import { SvelteBody } from './visitors/SvelteBody.js';
import { SvelteComponent } from './visitors/SvelteComponent.js';
import { SvelteDocument } from './visitors/SvelteDocument.js';
@ -127,6 +128,7 @@ const visitors = {
SlotElement,
SnippetBlock,
SpreadAttribute,
StaticBlock,
SvelteBody,
SvelteComponent,
SvelteDocument,

@ -1,4 +1,4 @@
/** @import { ArrowFunctionExpression, BlockStatement, Expression, FunctionDeclaration, FunctionExpression, Statement } from 'estree' */
/** @import { ArrowFunctionExpression, BlockStatement, Declaration, Expression, FunctionDeclaration, FunctionExpression, Statement, VariableDeclaration } from 'estree' */
/** @import { ComponentContext } from '../types' */
import { add_state_transformers } from './shared/declarations.js';
import * as b from '#compiler/builders';
@ -11,6 +11,24 @@ export function BlockStatement(node, context) {
add_state_transformers(context);
const tracing = context.state.scope.tracing;
/** @type {BlockStatement['body']} */
const body = [];
for (const child of node.body) {
const visited = /** @type {Declaration | Statement} */ (context.visit(child));
if (
visited.type === 'ClassDeclaration' &&
'metadata' in visited &&
visited.metadata !== null &&
typeof visited.metadata === 'object' &&
'computed_field_declarations' in visited.metadata
) {
body.push(
.../** @type {VariableDeclaration[]} */ (visited.metadata.computed_field_declarations)
);
}
body.push(visited);
}
if (tracing !== null) {
const parent =
/** @type {ArrowFunctionExpression | FunctionDeclaration | FunctionExpression} */ (
@ -22,11 +40,10 @@ export function BlockStatement(node, context) {
const call = b.call(
'$.trace',
/** @type {Expression} */ (tracing),
b.thunk(b.block(node.body.map((n) => /** @type {Statement} */ (context.visit(n)))), is_async)
b.thunk(b.block(body), is_async)
);
return b.block([b.return(is_async ? b.await(call) : call)]);
}
context.next();
return b.block(body);
}

@ -35,23 +35,40 @@ export function ClassBody(node, context) {
) {
continue;
}
const member = b.member(b.this, field.key);
const should_proxy = field.type === '$state' && true; // TODO
if (typeof name === 'number' && field.computed_key) {
const computed_key = /** @type {Expression} */ (context.visit(field.computed_key));
const evaluation = context.state.scope.evaluate(computed_key);
if (evaluation.is_known) {
body.push(
b.prop_def(field.key, null),
b.method(
'get',
b.literal(evaluation.value),
[],
[b.return(b.call('$.get', member))],
true
),
b.method(
'set',
b.literal(evaluation.value),
[b.id('value')],
[b.stmt(b.call('$.set', member, b.id('value'), should_proxy && b.true))],
true
)
);
continue;
}
const key = context.state.scope.generate('key');
computed_field_declarations.push(b.let(key));
const member = b.member(b.this, field.key);
const should_proxy = field.type === '$state' && true; // TODO
body.push(
b.prop_def(field.key, null),
b.method(
'get',
b.assignment(
'=',
b.id(key),
/** @type {Expression} */ (context.visit(field.computed_key))
),
b.assignment('=', b.id(key), computed_key),
[],
[b.return(b.call('$.get', member))],
true
@ -66,11 +83,6 @@ export function ClassBody(node, context) {
);
continue;
}
const member = b.member(b.this, field.key);
const should_proxy = field.type === '$state' && true; // TODO
const key = b.key(/** @type {string} */ (name));
body.push(

@ -17,16 +17,25 @@ export function ClassDeclaration(node, context) {
: null;
const body = /** @type {ClassBody} */ (context.visit(node.body, state));
if (state.computed_field_declarations.length > 0) {
const init = b.call(
b.arrow(
[],
b.block([
...state.computed_field_declarations,
b.return(b.class(node.id, body, super_class))
])
)
);
return node.id ? b.var(node.id, init) : init;
if (context.path.at(-1)?.type === 'ExportDefaultDeclaration') {
const init = b.call(
b.arrow(
[],
b.block([
...state.computed_field_declarations,
b.return(b.class(node.id, body, super_class))
])
)
);
return node.id ? b.var(node.id, init) : init;
} else {
return {
...b.class_declaration(node.id, body, super_class),
metadata: {
computed_field_declarations: state.computed_field_declarations
}
}
}
}
return b.class_declaration(node.id, body, super_class);
}

@ -1,14 +1,14 @@
/** @import { Expression, ImportDeclaration, MemberExpression, Program } from 'estree' */
/** @import { Declaration, Expression, ImportDeclaration, MemberExpression, Program, Statement, VariableDeclaration } from 'estree' */
/** @import { ComponentContext } from '../types' */
import { build_getter, is_prop_source } from '../utils.js';
import * as b from '#compiler/builders';
import { add_state_transformers } from './shared/declarations.js';
/**
* @param {Program} _
* @param {Program} node
* @param {ComponentContext} context
*/
export function Program(_, context) {
export function Program(node, context) {
if (!context.state.analysis.runes) {
context.state.transform['$$props'] = {
read: (node) => ({ ...node, name: '$$sanitized_props' })
@ -137,5 +137,26 @@ export function Program(_, context) {
add_state_transformers(context);
context.next();
/** @type {Program['body']} */
const body = [];
for (const child of node.body) {
const visited = /** @type {Declaration | Statement} */ (context.visit(child));
if (
visited.type === 'ClassDeclaration' &&
'metadata' in visited &&
visited.metadata !== null &&
typeof visited.metadata === 'object' &&
'computed_field_declarations' in visited.metadata
) {
body.push(
.../** @type {VariableDeclaration[]} */ (visited.metadata.computed_field_declarations)
);
}
body.push(visited);
}
return {
...node,
body
};
}

@ -0,0 +1,31 @@
/** @import { Declaration, Statement, StaticBlock, VariableDeclaration } from 'estree' */
/** @import { ComponentContext } from '../types' */
/**
* @param {StaticBlock} node
* @param {ComponentContext} context
*/
export function StaticBlock(node, context) {
/** @type {StaticBlock['body']} */
const body = [];
for (const child of node.body) {
const visited = /** @type {Declaration | Statement} */ (context.visit(child));
if (
visited.type === 'ClassDeclaration' &&
'metadata' in visited &&
visited.metadata !== null &&
typeof visited.metadata === 'object' &&
'computed_field_declarations' in visited.metadata
) {
body.push(
.../** @type {VariableDeclaration[]} */ (visited.metadata.computed_field_declarations)
);
}
body.push(visited);
}
return {
...node,
body
};
}

@ -11,6 +11,7 @@ import { render_stylesheet } from '../css/index.js';
import { AssignmentExpression } from './visitors/AssignmentExpression.js';
import { AwaitBlock } from './visitors/AwaitBlock.js';
import { AwaitExpression } from './visitors/AwaitExpression.js';
import { BlockStatement } from './visitors/BlockStatement.js';
import { CallExpression } from './visitors/CallExpression.js';
import { ClassBody } from './visitors/ClassBody.js';
import { ClassDeclaration } from './visitors/ClassDeclaration.js';
@ -27,12 +28,14 @@ import { IfBlock } from './visitors/IfBlock.js';
import { KeyBlock } from './visitors/KeyBlock.js';
import { LabeledStatement } from './visitors/LabeledStatement.js';
import { MemberExpression } from './visitors/MemberExpression.js';
import { Program } from './visitors/Program.js';
import { PropertyDefinition } from './visitors/PropertyDefinition.js';
import { RegularElement } from './visitors/RegularElement.js';
import { RenderTag } from './visitors/RenderTag.js';
import { SlotElement } from './visitors/SlotElement.js';
import { SnippetBlock } from './visitors/SnippetBlock.js';
import { SpreadAttribute } from './visitors/SpreadAttribute.js';
import { StaticBlock } from './visitors/StaticBlock.js';
import { SvelteComponent } from './visitors/SvelteComponent.js';
import { SvelteElement } from './visitors/SvelteElement.js';
import { SvelteFragment } from './visitors/SvelteFragment.js';
@ -49,6 +52,7 @@ const global_visitors = {
_: set_scope,
AssignmentExpression,
AwaitExpression,
BlockStatement,
CallExpression,
ClassBody,
ClassDeclaration,
@ -57,7 +61,9 @@ const global_visitors = {
Identifier,
LabeledStatement,
MemberExpression,
Program,
PropertyDefinition,
StaticBlock,
UpdateExpression,
VariableDeclaration
};

@ -0,0 +1,31 @@
/** @import { BlockStatement, Declaration, Statement, VariableDeclaration } from 'estree' */
/** @import { Context } from '../types' */
/**
* @param {BlockStatement} node
* @param {Context} context
*/
export function BlockStatement(node, context) {
/** @type {BlockStatement['body']} */
const body = [];
for (const child of node.body) {
const visited = /** @type {Declaration | Statement} */ (context.visit(child));
if (
visited.type === 'ClassDeclaration' &&
'metadata' in visited &&
visited.metadata !== null &&
typeof visited.metadata === 'object' &&
'computed_field_declarations' in visited.metadata
) {
body.push(
.../** @type {VariableDeclaration[]} */ (visited.metadata.computed_field_declarations)
);
}
body.push(visited);
}
return {
...node,
body
};
}

@ -32,10 +32,10 @@ export function ClassBody(node, context) {
continue;
}
const member = b.member(b.this, field.key);
if (typeof name !== 'string' && field.computed_key) {
const key = context.state.scope.generate('key');
computed_field_declarations.push(b.let(key));
const member = b.member(b.this, field.key);
body.push(
b.prop_def(field.key, null),
b.method(
@ -62,8 +62,6 @@ export function ClassBody(node, context) {
// insert backing fields for stuff declared in the constructor
if (field.type === '$derived' || field.type === '$derived.by') {
const member = b.member(b.this, field.key);
body.push(
b.prop_def(field.key, null),
b.method('get', b.key(/** @type {string} */ (name)), [], [b.return(b.call(member))]),
@ -95,6 +93,7 @@ export function ClassBody(node, context) {
body.push(/** @type {PropertyDefinition} */ (context.visit(definition, child_state)));
continue;
}
const member = b.member(b.this, field.key);
if (
(typeof name === 'string' && name[0] === '#') ||
@ -104,8 +103,6 @@ export function ClassBody(node, context) {
body.push(/** @type {PropertyDefinition} */ (context.visit(definition, child_state)));
} else if (field.node === definition && typeof name === 'string') {
// $derived / $derived.by
const member = b.member(b.this, field.key);
body.push(
b.prop_def(
field.key,
@ -117,7 +114,25 @@ export function ClassBody(node, context) {
);
} else if (field.computed_key) {
// $derived / $derived.by
const member = b.member(b.this, field.key);
const computed_key = /** @type {Expression} */ (context.visit(field.computed_key));
const evaluation = context.state.scope.evaluate(computed_key);
if (evaluation.is_known) {
body.push(
b.prop_def(
field.key,
/** @type {CallExpression} */ (context.visit(field.value, child_state))
),
b.method('get', b.literal(evaluation.value), [], [b.return(b.call(member))], true),
b.method(
'set',
b.literal(evaluation.value),
[b.id('$$value')],
[b.return(b.call(member, b.id('$$value')))],
true
)
);
continue;
}
const key = context.state.scope.generate('key');
computed_field_declarations.push(b.let(key));
body.push(

@ -17,16 +17,25 @@ export function ClassDeclaration(node, context) {
: null;
const body = /** @type {ClassBody} */ (context.visit(node.body, state));
if (state.computed_field_declarations.length > 0) {
const init = b.call(
b.arrow(
[],
b.block([
...state.computed_field_declarations,
b.return(b.class(node.id, body, super_class))
])
)
);
return node.id ? b.var(node.id, init) : init;
if (context.path.at(-1)?.type === 'ExportDefaultDeclaration') {
const init = b.call(
b.arrow(
[],
b.block([
...state.computed_field_declarations,
b.return(b.class(node.id, body, super_class))
])
)
);
return node.id ? b.var(node.id, init) : init;
} else {
return {
...b.class_declaration(node.id, body, super_class),
metadata: {
computed_field_declarations: state.computed_field_declarations
}
};
}
}
return b.class_declaration(node.id, body, super_class);
}

@ -0,0 +1,31 @@
/** @import { Declaration, Program, Statement, VariableDeclaration } from 'estree' */
/** @import { Context } from '../types.js' */
/**
* @param {Program} node
* @param {Context} context
*/
export function Program(node, context) {
/** @type {Program['body']} */
const body = [];
for (const child of node.body) {
const visited = /** @type {Declaration | Statement} */ (context.visit(child));
if (
visited.type === 'ClassDeclaration' &&
'metadata' in visited &&
visited.metadata !== null &&
typeof visited.metadata === 'object' &&
'computed_field_declarations' in visited.metadata
) {
body.push(
.../** @type {VariableDeclaration[]} */ (visited.metadata.computed_field_declarations)
);
}
body.push(visited);
}
return {
...node,
body
};
}

@ -0,0 +1,31 @@
/** @import { Declaration, Statement, StaticBlock, VariableDeclaration } from 'estree' */
/** @import { Context } from '../types' */
/**
* @param {StaticBlock} node
* @param {Context} context
*/
export function StaticBlock(node, context) {
/** @type {StaticBlock['body']} */
const body = [];
for (const child of node.body) {
const visited = /** @type {Declaration | Statement} */ (context.visit(child));
if (
visited.type === 'ClassDeclaration' &&
'metadata' in visited &&
visited.metadata !== null &&
typeof visited.metadata === 'object' &&
'computed_field_declarations' in visited.metadata
) {
body.push(
.../** @type {VariableDeclaration[]} */ (visited.metadata.computed_field_declarations)
);
}
body.push(visited);
}
return {
...node,
body
};
}

@ -1,4 +1,4 @@
import type { AST, Binding, StateField, StateFields } from '#compiler';
import type { AST, Binding, StateFields } from '#compiler';
import type {
AwaitExpression,
CallExpression,

Loading…
Cancel
Save