mirror of https://github.com/sveltejs/svelte
Merge 5b5e9efb92 into da00abe116
commit
2d57078af7
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': minor
|
||||
---
|
||||
|
||||
feat: allow `$state` in computed class fields
|
||||
@ -0,0 +1,41 @@
|
||||
/** @import { ClassBody, ClassDeclaration, Expression, VariableDeclaration } from 'estree' */
|
||||
/** @import { ClientTransformState, Context } from '../types' */
|
||||
import * as b from '#compiler/builders';
|
||||
|
||||
/**
|
||||
* @param {ClassDeclaration} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function ClassDeclaration(node, context) {
|
||||
/** @type {ClientTransformState & { computed_field_declarations: VariableDeclaration[] }} */
|
||||
const state = {
|
||||
...context.state,
|
||||
computed_field_declarations: []
|
||||
};
|
||||
const super_class = node.superClass
|
||||
? /** @type {Expression} */ (context.visit(node.superClass))
|
||||
: null;
|
||||
const body = /** @type {ClassBody} */ (context.visit(node.body, state));
|
||||
if (state.computed_field_declarations.length > 0) {
|
||||
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 { ClassBody, ClassExpression, Expression, VariableDeclaration } from 'estree' */
|
||||
/** @import { ClientTransformState, Context } from '../types' */
|
||||
import * as b from '#compiler/builders';
|
||||
|
||||
/**
|
||||
* @param {ClassExpression} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function ClassExpression(node, context) {
|
||||
/** @type {ClientTransformState & { computed_field_declarations: VariableDeclaration[] }} */
|
||||
const state = {
|
||||
...context.state,
|
||||
computed_field_declarations: []
|
||||
};
|
||||
const super_class = node.superClass
|
||||
? /** @type {Expression} */ (context.visit(node.superClass))
|
||||
: null;
|
||||
const body = /** @type {ClassBody} */ (context.visit(node.body, state));
|
||||
if (state.computed_field_declarations.length > 0) {
|
||||
return b.call(
|
||||
b.arrow(
|
||||
[],
|
||||
b.block([
|
||||
...state.computed_field_declarations,
|
||||
b.return(b.class(node.id, body, super_class))
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
return b.class(node.id, body, super_class);
|
||||
}
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/** @import { ClassBody, ClassDeclaration, Expression, VariableDeclaration } from 'estree' */
|
||||
/** @import { ServerTransformState, Context } from '../types' */
|
||||
import * as b from '#compiler/builders';
|
||||
|
||||
/**
|
||||
* @param {ClassDeclaration} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function ClassDeclaration(node, context) {
|
||||
/** @type {ServerTransformState & { computed_field_declarations: VariableDeclaration[] }} */
|
||||
const state = {
|
||||
...context.state,
|
||||
computed_field_declarations: []
|
||||
};
|
||||
const super_class = node.superClass
|
||||
? /** @type {Expression} */ (context.visit(node.superClass))
|
||||
: null;
|
||||
const body = /** @type {ClassBody} */ (context.visit(node.body, state));
|
||||
if (state.computed_field_declarations.length > 0) {
|
||||
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 { ClassBody, ClassExpression, Expression, VariableDeclaration } from 'estree' */
|
||||
/** @import { ServerTransformState, Context } from '../types' */
|
||||
import * as b from '#compiler/builders';
|
||||
|
||||
/**
|
||||
* @param {ClassExpression} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function ClassExpression(node, context) {
|
||||
/** @type {ServerTransformState & { computed_field_declarations: VariableDeclaration[] }} */
|
||||
const state = {
|
||||
...context.state,
|
||||
computed_field_declarations: []
|
||||
};
|
||||
const super_class = node.superClass
|
||||
? /** @type {Expression} */ (context.visit(node.superClass))
|
||||
: null;
|
||||
const body = /** @type {ClassBody} */ (context.visit(node.body, state));
|
||||
if (state.computed_field_declarations.length > 0) {
|
||||
return b.call(
|
||||
b.arrow(
|
||||
[],
|
||||
b.block([
|
||||
...state.computed_field_declarations,
|
||||
b.return(b.class(node.id, body, super_class))
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
return b.class(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,14 +1 @@
|
||||
[
|
||||
{
|
||||
"code": "state_invalid_placement",
|
||||
"message": "`$state(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.",
|
||||
"start": {
|
||||
"line": 5,
|
||||
"column": 16
|
||||
},
|
||||
"end": {
|
||||
"line": 5,
|
||||
"column": 25
|
||||
}
|
||||
}
|
||||
]
|
||||
[]
|
||||
|
||||
Loading…
Reference in new issue