simpler to just keep it as a SequenceExpression

pull/14307/head
Rich Harris 2 years ago
parent 8f925b9e60
commit 9f71f660dc

@ -507,7 +507,7 @@ function read_attribute(parser) {
const colon_index = name.indexOf(':');
const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index));
/** @type {Expression | [Expression, Expression] | null} */
/** @type {Expression | null} */
let expression = null;
/** @type {true | AST.ExpressionTag | Array<AST.Text | AST.ExpressionTag>} */
@ -536,24 +536,11 @@ function read_attribute(parser) {
!Array.isArray(value) &&
value.expression.type === 'SequenceExpression'
) {
try {
const bind_getter_setter = parse_expression_at(
`[${parser.template.slice(value_start, value.expression.end)}]`,
parser.ts,
0
);
if (
bind_getter_setter.type !== 'ArrayExpression' ||
bind_getter_setter.elements.length !== 2
) {
e.invalid_bind_directive(value_start);
}
expression = /** @type {[Expression, Expression]} */ (bind_getter_setter.elements);
} catch {
e.invalid_bind_directive(value_start);
if (value.expression.expressions.length !== 2) {
e.invalid_bind_directive(value);
}
}
end = parser.index;
} else if (parser.match_regex(regex_starts_with_quote_characters)) {
e.expected_token(parser.index, '=');

@ -125,7 +125,7 @@ export function BindDirective(node, context) {
// When dealing with bind getters/setters skip the specific binding validation
// Group bindings aren't supported for getter/setters so we don't need to handle
// the metadata
if (Array.isArray(node.expression)) {
if (node.expression.type === 'SequenceExpression') {
if (node.name === 'group') {
e.bind_group_invalid_expression(node);
}

@ -1,4 +1,4 @@
/** @import { CallExpression, Expression, MemberExpression } from 'estree' */
/** @import { CallExpression, Expression, MemberExpression, SequenceExpression } from 'estree' */
/** @import { AST, SvelteNode } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import { dev, is_ignored } from '../../../../state.js';
@ -20,10 +20,8 @@ export function BindDirective(node, context) {
let get, set;
if (Array.isArray(expression)) {
const [get_expression, set_expression] = expression;
get = /** @type {Expression} */ (context.visit(get_expression));
set = /** @type {Expression} */ (context.visit(set_expression));
if (expression.type === 'SequenceExpression') {
[get, set] = /** @type {SequenceExpression} */ (context.visit(expression)).expressions;
} else {
if (
dev &&

@ -1,4 +1,4 @@
/** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Property, Statement } from 'estree' */
/** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Property, SequenceExpression, Statement } from 'estree' */
/** @import { AST, TemplateNode } from '#compiler' */
/** @import { ComponentContext } from '../../types.js' */
import { dev, is_ignored } from '../../../../../state.js';
@ -42,7 +42,7 @@ export function build_component(node, component_name, context, anchor = context.
/** @type {Property[]} */
const custom_css_props = [];
/** @type {Identifier | MemberExpression | [Expression, Expression] | null} */
/** @type {Identifier | MemberExpression | SequenceExpression | null} */
let bind_this = null;
/** @type {ExpressionStatement[]} */
@ -161,13 +161,26 @@ export function build_component(node, component_name, context, anchor = context.
push_prop(b.init(attribute.name, value));
}
} else if (attribute.type === 'BindDirective') {
if (Array.isArray(attribute.expression)) {
const expression = /** @type {Expression} */ (context.visit(attribute.expression));
if (dev && attribute.name !== 'this') {
binding_initializers.push(
b.stmt(
b.call(
b.id('$.add_owner_effect'),
b.thunk(expression),
b.id(component_name),
is_ignored(node, 'ownership_invalid_binding') && b.true
)
)
);
}
if (expression.type === 'SequenceExpression') {
if (attribute.name === 'this') {
bind_this = attribute.expression;
} else {
const [get_expression, set_expression] = attribute.expression;
const get = /** @type {Expression} */ (context.visit(get_expression));
const set = /** @type {Expression} */ (context.visit(set_expression));
const [get, set] = expression.expressions;
const get_id = b.id(context.state.scope.generate('bind_get'));
const set_id = b.id(context.state.scope.generate('bind_set'));
@ -178,8 +191,6 @@ export function build_component(node, component_name, context, anchor = context.
push_prop(b.set(attribute.name, [b.stmt(b.call(set_id, b.id('$$value')))]));
}
} else {
const expression = /** @type {Expression} */ (context.visit(attribute.expression));
if (
dev &&
expression.type === 'MemberExpression' &&
@ -192,19 +203,6 @@ export function build_component(node, component_name, context, anchor = context.
if (attribute.name === 'this') {
bind_this = attribute.expression;
} else {
if (dev) {
binding_initializers.push(
b.stmt(
b.call(
b.id('$.add_owner_effect'),
b.thunk(expression),
b.id(component_name),
is_ignored(node, 'ownership_invalid_binding') && b.true
)
)
);
}
const is_store_sub =
attribute.expression.type === 'Identifier' &&
context.state.scope.get(attribute.expression.name)?.kind === 'store_sub';

@ -161,16 +161,13 @@ export function build_update_assignment(state, id, init, value, update) {
/**
* Serializes `bind:this` for components and elements.
* @param {Identifier | MemberExpression | [Expression, Expression]} expression
* @param {Identifier | MemberExpression | SequenceExpression} expression
* @param {Expression} value
* @param {import('zimmerframe').Context<SvelteNode, ComponentClientTransformState>} context
*/
export function build_bind_this(expression, value, { state, visit }) {
if (Array.isArray(expression)) {
const [get_expression, set_expression] = expression;
const get = /** @type {Expression} */ (visit(get_expression));
const set = /** @type {Expression} */ (visit(set_expression));
if (expression.type === 'SequenceExpression') {
const [get, set] = /** @type {SequenceExpression} */ (visit(expression)).expressions;
return b.call('$.bind_this', value, set, get);
}
@ -250,7 +247,7 @@ export function build_bind_this(expression, value, { state, visit }) {
* @param {MemberExpression} expression
*/
export function validate_binding(state, binding, expression) {
if (Array.isArray(binding.expression)) {
if (binding.expression.type === 'SequenceExpression') {
return;
}
// If we are referencing a $store.foo then we don't need to add validation

@ -1,4 +1,4 @@
/** @import { BlockStatement, Expression, Pattern, Property, Statement } from 'estree' */
/** @import { BlockStatement, Expression, Pattern, Property, SequenceExpression, Statement } from 'estree' */
/** @import { AST, TemplateNode } from '#compiler' */
/** @import { ComponentContext } from '../../types.js' */
import { empty_comment, build_attribute_value } from './utils.js';
@ -81,10 +81,9 @@ export function build_inline_component(node, expression, context) {
const value = build_attribute_value(attribute.value, context, false, true);
push_prop(b.prop('init', b.key(attribute.name), value));
} else if (attribute.type === 'BindDirective' && attribute.name !== 'this') {
if (Array.isArray(attribute.expression)) {
const [get_expression, set_expression] = attribute.expression;
const get = /** @type {Expression} */ (context.visit(get_expression));
const set = /** @type {Expression} */ (context.visit(set_expression));
if (attribute.expression.type === 'SequenceExpression') {
const [get, set] = /** @type {SequenceExpression} */ (context.visit(attribute.expression))
.expressions;
const get_id = b.id(context.state.scope.generate('bind_get'));
const set_id = b.id(context.state.scope.generate('bind_set'));

@ -109,18 +109,16 @@ export function build_element_attributes(node, context) {
const binding = binding_properties[attribute.name];
if (binding?.omit_in_ssr) continue;
if (is_content_editable_binding(attribute.name)) {
content = Array.isArray(attribute.expression)
? b.call(/** @type {Expression} */ (context.visit(attribute.expression[0])))
const get =
attribute.expression.type === 'SequenceExpression'
? b.call(/** @type {Expression} */ (context.visit(attribute.expression.expressions[0])))
: /** @type {Expression} */ (context.visit(attribute.expression));
if (is_content_editable_binding(attribute.name)) {
content = get;
} else if (attribute.name === 'value' && node.name === 'textarea') {
content = b.call(
'$.escape',
Array.isArray(attribute.expression)
? b.call(/** @type {Expression} */ (context.visit(attribute.expression[0])))
: /** @type {Expression} */ (context.visit(attribute.expression))
);
} else if (attribute.name === 'group' && !Array.isArray(attribute.expression)) {
content = b.call('$.escape', get);
} else if (attribute.name === 'group' && attribute.expression.type !== 'SequenceExpression') {
const value_attribute = /** @type {AST.Attribute | undefined} */ (
node.attributes.find((attr) => attr.type === 'Attribute' && attr.name === 'value')
);
@ -158,10 +156,6 @@ export function build_element_attributes(node, context) {
])
);
} else {
const attribute_expression = Array.isArray(attribute.expression)
? b.call(attribute.expression[0])
: attribute.expression;
attributes.push(
create_attribute(attribute.name, -1, -1, [
{
@ -169,7 +163,7 @@ export function build_element_attributes(node, context) {
start: -1,
end: -1,
parent: attribute,
expression: attribute_expression,
expression: get,
metadata: {
expression: create_expression_metadata()
}

@ -50,7 +50,7 @@ export interface LegacyBinding extends BaseNode {
/** The 'x' in `bind:x` */
name: string;
/** The y in `bind:x={y}` */
expression: Identifier | MemberExpression | [Expression, Expression];
expression: Identifier | MemberExpression | SequenceExpression;
}
export interface LegacyBody extends BaseElement {

@ -14,7 +14,8 @@ import type {
Pattern,
Program,
ChainExpression,
SimpleCallExpression
SimpleCallExpression,
SequenceExpression
} from 'estree';
import type { Scope } from '../phases/scope';
@ -185,7 +186,7 @@ export namespace AST {
/** The 'x' in `bind:x` */
name: string;
/** The y in `bind:x={y}` */
expression: Identifier | MemberExpression | [Expression, Expression];
expression: Identifier | MemberExpression | SequenceExpression;
/** @internal */
metadata: {
binding_group_name: Identifier;

@ -606,7 +606,7 @@ declare module 'svelte/animate' {
}
declare module 'svelte/compiler' {
import type { Expression, Identifier, ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, MemberExpression, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression } from 'estree';
import type { Expression, Identifier, ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, MemberExpression, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree';
import type { SourceMap } from 'magic-string';
import type { Location } from 'locate-character';
/**
@ -1047,7 +1047,7 @@ declare module 'svelte/compiler' {
/** The 'x' in `bind:x` */
name: string;
/** The y in `bind:x={y}` */
expression: Identifier | MemberExpression | [Expression, Expression];
expression: Identifier | MemberExpression | SequenceExpression;
}
/** A `class:` directive */

Loading…
Cancel
Save