chore: unified expression metadata (#12644)

* add ExpressionMetadata type

* use unified reactivity metadata

* simplify component

* rename

* more consistency

* store bindings on expression metadata

* tidy

* regenerate types

* remove junk from options node

* reuse

* tweak jsdoc
animation-params
Rich Harris 2 months ago committed by GitHub
parent 363535c849
commit 65234b80c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -119,13 +119,23 @@ export function parse(source, { filename, rootDir, modern } = {}) {
*/
function to_public_ast(source, ast, modern) {
if (modern) {
const clean = (/** @type {any} */ node) => {
delete node.metadata;
delete node.parent;
};
ast.options?.attributes.forEach((attribute) => {
clean(attribute);
clean(attribute.value);
if (Array.isArray(attribute.value)) {
attribute.value.forEach(clean);
}
});
// remove things that we don't want to treat as public API
return zimmerframe_walk(ast, null, {
_(node, { next }) {
// @ts-ignore
delete node.parent;
// @ts-ignore
delete node.metadata;
clean(node);
next();
}
});

@ -9,7 +9,7 @@ import { decode_character_references } from '../utils/html.js';
import * as e from '../../../errors.js';
import * as w from '../../../warnings.js';
import { create_fragment } from '../utils/create.js';
import { create_attribute } from '../../nodes.js';
import { create_attribute, create_expression_metadata } from '../../nodes.js';
import { get_attribute_expression, is_expression_attribute } from '../../../utils/ast.js';
import { closing_tag_omitted } from '../../../../html-tree-validation.js';
@ -127,7 +127,7 @@ export default function element(parser) {
const type = meta_tags.has(name)
? meta_tags.get(name)
: regex_capital_letter.test(name[0]) || name === 'svelte:self' || name === 'svelte:component'
: regex_capital_letter.test(name[0])
? 'Component'
: name === 'title' && parent_is_head(parser.stack)
? 'TitleElement'
@ -140,7 +140,7 @@ export default function element(parser) {
const element =
type === 'RegularElement'
? {
type: type,
type,
start,
end: -1,
name,
@ -163,7 +163,7 @@ export default function element(parser) {
fragment: create_fragment(true),
parent: null,
metadata: {
svg: false
// unpopulated at first, differs between types
}
});
@ -508,8 +508,7 @@ function read_attribute(parser) {
expression,
parent: null,
metadata: {
contains_call_expression: false,
dynamic: false
expression: create_expression_metadata()
}
};
@ -538,8 +537,7 @@ function read_attribute(parser) {
},
parent: null,
metadata: {
dynamic: false,
contains_call_expression: false
expression: create_expression_metadata()
}
};
@ -584,7 +582,7 @@ function read_attribute(parser) {
value,
parent: null,
metadata: {
dynamic: false
expression: create_expression_metadata()
}
};
}
@ -616,17 +614,10 @@ function read_attribute(parser) {
modifiers,
expression,
metadata: {
dynamic: false,
contains_call_expression: false
expression: create_expression_metadata()
}
};
if (directive.type === 'ClassDirective') {
directive.metadata = {
dynamic: false
};
}
if (directive.type === 'TransitionDirective') {
const direction = name.slice(0, colon_index);
directive.intro = direction === 'in' || direction === 'transition';
@ -789,8 +780,7 @@ function read_sequence(parser, done, location) {
expression,
parent: null,
metadata: {
contains_call_expression: false,
dynamic: false
expression: create_expression_metadata()
}
};

@ -7,6 +7,7 @@ import * as e from '../../../errors.js';
import { create_fragment } from '../utils/create.js';
import { walk } from 'zimmerframe';
import { parse_expression_at } from '../acorn.js';
import { create_expression_metadata } from '../../nodes.js';
const regex_whitespace_with_closing_curly_brace = /^\s*}/;
@ -39,8 +40,7 @@ export default function tag(parser) {
end: parser.index,
expression,
metadata: {
contains_call_expression: false,
dynamic: false
expression: create_expression_metadata()
}
});
}

@ -1178,20 +1178,19 @@ const common_visitors = {
context.next();
node.metadata.dynamic = get_attribute_chunks(node.value).some((chunk) => {
if (chunk.type !== 'ExpressionTag') {
return false;
}
for (const chunk of get_attribute_chunks(node.value)) {
if (chunk.type !== 'ExpressionTag') continue;
if (
chunk.expression.type === 'FunctionExpression' ||
chunk.expression.type === 'ArrowFunctionExpression'
) {
return false;
continue;
}
return chunk.metadata.dynamic || chunk.metadata.contains_call_expression;
});
node.metadata.expression.has_state ||= chunk.metadata.expression.has_state;
node.metadata.expression.has_call ||= chunk.metadata.expression.has_call;
}
if (is_event_attribute(node)) {
const parent = context.path.at(-1);
@ -1211,10 +1210,10 @@ const common_visitors = {
}
},
ClassDirective(node, context) {
context.next({ ...context.state, expression: node });
context.next({ ...context.state, expression: node.metadata.expression });
},
SpreadAttribute(node, context) {
context.next({ ...context.state, expression: node });
context.next({ ...context.state, expression: node.metadata.expression });
},
SlotElement(node, context) {
let name = 'default';
@ -1230,17 +1229,21 @@ const common_visitors = {
if (node.value === true) {
const binding = context.state.scope.get(node.name);
if (binding?.kind !== 'normal') {
node.metadata.dynamic = true;
node.metadata.expression.has_state = true;
}
} else {
context.next();
node.metadata.dynamic = get_attribute_chunks(node.value).some(
(node) => node.type === 'ExpressionTag' && node.metadata.dynamic
);
for (const chunk of get_attribute_chunks(node.value)) {
if (chunk.type !== 'ExpressionTag') continue;
node.metadata.expression.has_state ||= chunk.metadata.expression.has_state;
node.metadata.expression.has_call ||= chunk.metadata.expression.has_call;
}
}
},
ExpressionTag(node, context) {
context.next({ ...context.state, expression: node });
context.next({ ...context.state, expression: node.metadata.expression });
},
Identifier(node, context) {
const parent = /** @type {Node} */ (context.path.at(-1));
@ -1261,10 +1264,18 @@ const common_visitors = {
const binding = context.state.scope.get(node.name);
if (binding && context.state.expression) {
context.state.expression.dependencies.add(binding);
if (binding.kind !== 'normal') {
context.state.expression.has_state = true;
}
}
// if no binding, means some global variable
if (binding && binding.kind !== 'normal') {
if (context.state.expression) {
context.state.expression.metadata.dynamic = true;
context.state.expression.has_state = true;
}
// TODO it would be better to just bail out when we hit the ExportSpecifier node but that's
@ -1297,13 +1308,10 @@ const common_visitors = {
},
CallExpression(node, context) {
const { expression, render_tag } = context.state;
if (
(expression?.type === 'ExpressionTag' ||
expression?.type === 'SpreadAttribute' ||
expression?.type === 'OnDirective') &&
!is_known_safe_call(node, context)
) {
expression.metadata.contains_call_expression = true;
if (expression && !is_known_safe_call(node, context)) {
expression.has_call = true;
expression.has_state = true;
}
if (render_tag) {
@ -1354,7 +1362,7 @@ const common_visitors = {
},
MemberExpression(node, context) {
if (context.state.expression) {
context.state.expression.metadata.dynamic = true;
context.state.expression.has_state = true;
}
if (!is_safe_identifier(node, context.state.scope)) {
@ -1368,7 +1376,7 @@ const common_visitors = {
if (parent?.type === 'SvelteElement' || parent?.type === 'RegularElement') {
state.analysis.event_directive_node ??= node;
}
next({ ...state, expression: node });
next({ ...state, expression: node.metadata.expression });
},
BindDirective(node, context) {
let i = context.path.length;

@ -2,6 +2,7 @@ import type { Scope } from '../scope.js';
import type { ComponentAnalysis, ReactiveStatement } from '../types.js';
import type {
ClassDirective,
ExpressionMetadata,
ExpressionTag,
OnDirective,
RenderTag,
@ -20,8 +21,8 @@ export interface AnalysisState {
has_props_rune: boolean;
/** Which slots the current parent component has */
component_slots: Set<string>;
/** The current {expression}, if any */
expression: ExpressionTag | ClassDirective | OnDirective | SpreadAttribute | null;
/** Information about the current expression/directive/block value */
expression: ExpressionMetadata | null;
/** The current {@render ...} tag, if any */
render_tag: null | RenderTag;
private_derived_state: string[];

@ -1,5 +1,5 @@
/** @import { BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Pattern, Property, Statement, Super, TemplateElement, TemplateLiteral } from 'estree' */
/** @import { Attribute, BindDirective, Binding, ClassDirective, Component, DelegatedEvent, EachBlock, ExpressionTag, Namespace, OnDirective, RegularElement, SpreadAttribute, StyleDirective, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TemplateNode, Text } from '#compiler' */
/** @import { Attribute, BindDirective, Binding, ClassDirective, Component, DelegatedEvent, EachBlock, ExpressionMetadata, ExpressionTag, Namespace, OnDirective, RegularElement, SpreadAttribute, StyleDirective, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TemplateNode, Text } from '#compiler' */
/** @import { SourceLocation } from '#shared' */
/** @import { Scope } from '../../../scope.js' */
/** @import { ComponentClientTransformState, ComponentContext, ComponentVisitors } from '../types.js' */
@ -103,13 +103,11 @@ function serialize_style_directives(style_directives, element_id, context, is_at
)
);
const contains_call_expression = get_attribute_chunks(directive.value).some(
(v) => v.type === 'ExpressionTag' && v.metadata.contains_call_expression
);
const { has_state, has_call } = directive.metadata.expression;
if (!is_attributes_reactive && contains_call_expression) {
if (!is_attributes_reactive && has_call) {
state.init.push(serialize_update(update));
} else if (is_attributes_reactive || directive.metadata.dynamic || contains_call_expression) {
} else if (is_attributes_reactive || has_state || has_call) {
state.update.push(update);
} else {
state.init.push(update);
@ -151,11 +149,12 @@ function serialize_class_directives(class_directives, element_id, context, is_at
for (const directive of class_directives) {
const value = /** @type {Expression} */ (context.visit(directive.expression));
const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value));
const contains_call_expression = directive.expression.type === 'CallExpression';
if (!is_attributes_reactive && contains_call_expression) {
const { has_state, has_call } = directive.metadata.expression;
if (!is_attributes_reactive && has_call) {
state.init.push(serialize_update(update));
} else if (is_attributes_reactive || directive.metadata.dynamic || contains_call_expression) {
} else if (is_attributes_reactive || has_state || has_call) {
state.update.push(update);
} else {
state.init.push(update);
@ -277,7 +276,7 @@ function serialize_element_spread_attributes(
for (const attribute of attributes) {
if (attribute.type === 'Attribute') {
const name = get_attribute_name(element, attribute, context);
// TODO: handle contains_call_expression
// TODO: handle has_call
const [, value] = serialize_attribute_value(attribute.value, context);
if (
@ -306,7 +305,7 @@ function serialize_element_spread_attributes(
}
needs_isolation ||=
attribute.type === 'SpreadAttribute' && attribute.metadata.contains_call_expression;
attribute.type === 'SpreadAttribute' && attribute.metadata.expression.has_call;
}
const lowercase_attributes =
@ -405,11 +404,11 @@ function serialize_dynamic_element_attributes(attributes, context, element_id) {
}
is_reactive ||=
attribute.metadata.dynamic ||
attribute.metadata.expression.has_state ||
// objects could contain reactive getters -> play it safe and always assume spread attributes are reactive
attribute.type === 'SpreadAttribute';
needs_isolation ||=
attribute.type === 'SpreadAttribute' && attribute.metadata.contains_call_expression;
attribute.type === 'SpreadAttribute' && attribute.metadata.expression.has_call;
}
if (needs_isolation || is_reactive) {
@ -486,13 +485,13 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu
const name = get_attribute_name(element, attribute, context);
const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg';
const is_mathml = context.state.metadata.namespace === 'mathml';
let [contains_call_expression, value] = serialize_attribute_value(attribute.value, context);
let [has_call, value] = serialize_attribute_value(attribute.value, context);
// The foreign namespace doesn't have any special handling, everything goes through the attr function
if (context.state.metadata.namespace === 'foreign') {
const statement = b.stmt(b.call('$.set_attribute', node_id, b.literal(name), value));
if (attribute.metadata.dynamic) {
if (attribute.metadata.expression.has_state) {
const id = state.scope.generate(`${node_id.name}_${name}`);
serialize_update_assignment(state, id, undefined, value, statement);
return true;
@ -529,8 +528,8 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu
update = b.stmt(b.call(callee, node_id, b.literal(name), value));
}
if (attribute.metadata.dynamic) {
if (contains_call_expression) {
if (attribute.metadata.expression.has_state) {
if (has_call) {
state.init.push(serialize_update(update));
} else {
state.update.push(update);
@ -552,12 +551,12 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu
function serialize_custom_element_attribute_update_assignment(node_id, attribute, context) {
const state = context.state;
const name = attribute.name; // don't lowercase, as we set the element's property, which might be case sensitive
let [contains_call_expression, value] = serialize_attribute_value(attribute.value, context);
let [has_call, value] = serialize_attribute_value(attribute.value, context);
const update = b.stmt(b.call('$.set_custom_element_data', node_id, b.literal(name), value));
if (attribute.metadata.dynamic) {
if (contains_call_expression) {
if (attribute.metadata.expression.has_state) {
if (has_call) {
state.init.push(serialize_update(update));
} else {
state.update.push(update);
@ -592,7 +591,7 @@ function serialize_element_special_value_attribute(element, node_id, attribute,
value
)
);
const is_reactive = attribute.metadata.dynamic;
const is_reactive = attribute.metadata.expression.has_state;
const is_select_with_value =
// attribute.metadata.dynamic would give false negatives because even if the value does not change,
// the inner options could still change, so we need to always treat it as reactive
@ -723,10 +722,10 @@ function serialize_inline_component(node, component_name, context, anchor = cont
events[attribute.name].push(handler);
} else if (attribute.type === 'SpreadAttribute') {
const expression = /** @type {Expression} */ (context.visit(attribute));
if (attribute.metadata.dynamic) {
if (attribute.metadata.expression.has_state) {
let value = expression;
if (attribute.metadata.contains_call_expression) {
if (attribute.metadata.expression.has_call) {
const id = b.id(context.state.scope.generate('spread_element'));
context.state.init.push(b.var(id, b.call('$.derived', b.thunk(value))));
value = b.call('$.get', id);
@ -754,7 +753,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont
const [, value] = serialize_attribute_value(attribute.value, context);
if (attribute.metadata.dynamic) {
if (attribute.metadata.expression.has_state) {
let arg = value;
// When we have a non-simple computation, anything other than an Identifier or Member expression,
@ -1118,7 +1117,7 @@ function serialize_render_stmt(update) {
/**
* Serializes the event handler function of the `on:` directive
* @param {Pick<OnDirective, 'name' | 'modifiers' | 'expression'>} node
* @param {null | { contains_call_expression: boolean; dynamic: boolean; } | null} metadata
* @param {null | ExpressionMetadata} metadata
* @param {ComponentContext} context
*/
function serialize_event_handler(node, metadata, { state, visit }) {
@ -1145,7 +1144,7 @@ function serialize_event_handler(node, metadata, { state, visit }) {
);
if (
metadata?.contains_call_expression &&
metadata?.has_call &&
!(
(handler.type === 'ArrowFunctionExpression' || handler.type === 'FunctionExpression') &&
handler.metadata.hoistable
@ -1227,7 +1226,7 @@ function serialize_event_handler(node, metadata, { state, visit }) {
/**
* Serializes an event handler function of the `on:` directive or an attribute starting with `on`
* @param {{name: string;modifiers: string[];expression: Expression | null;delegated?: DelegatedEvent | null;}} node
* @param {null | { contains_call_expression: boolean; dynamic: boolean; }} metadata
* @param {null | ExpressionMetadata} metadata
* @param {ComponentContext} context
*/
function serialize_event(node, metadata, context) {
@ -1357,7 +1356,9 @@ function serialize_event_attribute(node, context) {
modifiers,
delegated: node.metadata.delegated
},
!Array.isArray(node.value) && node.value?.type === 'ExpressionTag' ? node.value.metadata : null,
!Array.isArray(node.value) && node.value?.type === 'ExpressionTag'
? node.value.metadata.expression
: null,
context
);
}
@ -1401,9 +1402,9 @@ function process_children(nodes, expression, is_element, { visit, state }) {
b.call('$.set_text', text_id, /** @type {Expression} */ (visit(node.expression, state)))
);
if (node.metadata.contains_call_expression && !within_bound_contenteditable) {
if (node.metadata.expression.has_call && !within_bound_contenteditable) {
state.init.push(serialize_update(update));
} else if (node.metadata.dynamic && !within_bound_contenteditable) {
} else if (node.metadata.expression.has_state && !within_bound_contenteditable) {
state.update.push(update);
} else {
state.init.push(
@ -1424,14 +1425,16 @@ function process_children(nodes, expression, is_element, { visit, state }) {
state.template.push(' ');
const [contains_call_expression, value] = serialize_template_literal(sequence, visit, state);
const [has_call, value] = serialize_template_literal(sequence, visit, state);
const update = b.stmt(b.call('$.set_text', text_id, value));
if (contains_call_expression && !within_bound_contenteditable) {
if (has_call && !within_bound_contenteditable) {
state.init.push(serialize_update(update));
} else if (
sequence.some((node) => node.type === 'ExpressionTag' && node.metadata.dynamic) &&
sequence.some(
(node) => node.type === 'ExpressionTag' && node.metadata.expression.has_state
) &&
!within_bound_contenteditable
) {
state.update.push(update);
@ -1517,7 +1520,7 @@ function get_node_id(expression, state, name) {
/**
* @param {Attribute['value']} value
* @param {ComponentContext} context
* @returns {[contains_call_expression: boolean, Expression]}
* @returns {[has_call: boolean, Expression]}
*/
function serialize_attribute_value(value, context) {
if (value === true) {
@ -1532,7 +1535,7 @@ function serialize_attribute_value(value, context) {
}
return [
chunk.metadata.contains_call_expression,
chunk.metadata.expression.has_call,
/** @type {Expression} */ (context.visit(chunk.expression))
];
}
@ -1552,18 +1555,18 @@ function serialize_template_literal(values, visit, state) {
/** @type {Expression[]} */
const expressions = [];
let contains_call_expression = false;
let has_call = false;
let contains_multiple_call_expression = false;
quasis.push(b.quasi(''));
for (let i = 0; i < values.length; i++) {
const node = values[i];
if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) {
if (contains_call_expression) {
if (node.type === 'ExpressionTag' && node.metadata.expression.has_call) {
if (has_call) {
contains_multiple_call_expression = true;
}
contains_call_expression = true;
has_call = true;
}
}
@ -1600,7 +1603,7 @@ function serialize_template_literal(values, visit, state) {
}
// TODO instead of this tuple, return a `{ dynamic, complex, value }` object. will DRY stuff out
return [contains_call_expression, b.template(quasis, expressions)];
return [has_call, b.template(quasis, expressions)];
}
/** @type {ComponentVisitors} */
@ -2855,7 +2858,7 @@ export const template_visitors = {
context.next({ ...context.state, in_constructor: false });
},
OnDirective(node, context) {
serialize_event(node, node.metadata, context);
serialize_event(node, node.metadata.expression, context);
},
UseDirective(node, { state, next, visit }) {
const params = [b.id('$$node')];
@ -3238,7 +3241,7 @@ export const template_visitors = {
name = value;
is_default = false;
} else if (attribute.name !== 'slot') {
if (attribute.metadata.dynamic) {
if (attribute.metadata.expression.has_state) {
props.push(b.get(attribute.name, [b.return(value)]));
} else {
props.push(b.init(attribute.name, value));

@ -12,7 +12,11 @@ import {
LoadErrorElements,
WhitespaceInsensitiveAttributes
} from '../../../../constants.js';
import { create_attribute, is_custom_element_node } from '../../../../nodes.js';
import {
create_attribute,
create_expression_metadata,
is_custom_element_node
} from '../../../../nodes.js';
import { regex_starts_with_newline } from '../../../../patterns.js';
import * as b from '../../../../../utils/builders.js';
import {
@ -142,8 +146,7 @@ export function serialize_element_attributes(node, context) {
serialize_attribute_value(value_attribute.value, context)
),
metadata: {
contains_call_expression: false,
dynamic: false
expression: create_expression_metadata()
}
}
])
@ -158,8 +161,7 @@ export function serialize_element_attributes(node, context) {
parent: attribute,
expression: attribute.expression,
metadata: {
contains_call_expression: false,
dynamic: false
expression: create_expression_metadata()
}
}
])
@ -399,7 +401,9 @@ function serialize_class_directives(class_directives, class_attribute) {
),
b.literal(' ')
),
metadata: { contains_call_expression: false, dynamic: false }
metadata: {
expression: create_expression_metadata()
}
});
class_attribute.value = chunks;

@ -45,8 +45,19 @@ export function create_attribute(name, start, end, value) {
value,
parent: null,
metadata: {
dynamic: false,
expression: create_expression_metadata(),
delegated: null
}
};
}
/**
* @returns {Compiler.ExpressionMetadata}
*/
export function create_expression_metadata() {
return {
dependencies: new Set(),
has_state: false,
has_call: false
};
}

@ -316,6 +316,15 @@ export interface Binding {
} | null;
}
export interface ExpressionMetadata {
/** All the bindings that are referenced inside this expression */
dependencies: Set<Binding>;
/** True if the expression references state directly, or _might_ (via member/call expressions) */
has_state: boolean;
/** True if the expression involves a call expression (often, it will need to be wrapped in a derived) */
has_call: boolean;
}
export * from './template.js';
export { Css };

@ -1,4 +1,4 @@
import type { Binding, Css } from '#compiler';
import type { Binding, Css, ExpressionMetadata } from '#compiler';
import type {
ArrayExpression,
ArrowFunctionExpression,
@ -111,12 +111,7 @@ export interface ExpressionTag extends BaseNode {
type: 'ExpressionTag';
expression: Expression;
metadata: {
contains_call_expression: boolean;
/**
* Whether or not the expression contains any dynamic references
* determines whether it will be updated in a render effect or not
*/
dynamic: boolean;
expression: ExpressionMetadata;
};
}
@ -190,7 +185,7 @@ export interface ClassDirective extends BaseNode {
/** The 'y' in `class:x={y}`, or the `x` in `class:x` */
expression: Expression;
metadata: {
dynamic: false;
expression: ExpressionMetadata;
};
}
@ -212,8 +207,7 @@ export interface OnDirective extends BaseNode {
expression: null | Expression;
modifiers: string[]; // TODO specify
metadata: {
contains_call_expression: boolean;
dynamic: boolean;
expression: ExpressionMetadata;
};
}
@ -233,7 +227,7 @@ export interface StyleDirective extends BaseNode {
value: true | ExpressionTag | Array<ExpressionTag | Text>;
modifiers: Array<'important'>;
metadata: {
dynamic: boolean;
expression: ExpressionMetadata;
};
}
@ -454,7 +448,7 @@ export interface Attribute extends BaseNode {
name: string;
value: true | ExpressionTag | Array<Text | ExpressionTag>;
metadata: {
dynamic: boolean;
expression: ExpressionMetadata;
/** May be set if this is an event attribute */
delegated: null | DelegatedEvent;
};
@ -464,8 +458,7 @@ export interface SpreadAttribute extends BaseNode {
type: 'SpreadAttribute';
expression: Expression;
metadata: {
contains_call_expression: boolean;
dynamic: boolean;
expression: ExpressionMetadata;
};
}

@ -39,15 +39,9 @@
"end": 48,
"type": "Text",
"raw": "my-custom-element",
"data": "my-custom-element",
"parent": null
"data": "my-custom-element"
}
],
"parent": null,
"metadata": {
"dynamic": false,
"delegated": null
}
]
},
{
"type": "Attribute",
@ -74,17 +68,7 @@
},
"value": true,
"raw": "true"
},
"parent": null,
"metadata": {
"contains_call_expression": false,
"dynamic": false
}
},
"parent": null,
"metadata": {
"dynamic": false,
"delegated": null
}
}
],

@ -965,6 +965,15 @@ declare module 'svelte/compiler' {
inside_rest?: boolean;
} | null;
}
interface ExpressionMetadata {
/** All the bindings that are referenced inside this expression */
dependencies: Set<Binding>;
/** True if the expression references state directly, or _might_ (via member/call expressions) */
has_state: boolean;
/** True if the expression involves a call expression (often, it will need to be wrapped in a derived) */
has_call: boolean;
}
/**
* The preprocess function provides convenient hooks for arbitrarily transforming component source code.
* For example, it can be used to convert a <style lang="sass"> block into vanilla CSS.
@ -1563,12 +1572,7 @@ declare module 'svelte/compiler' {
type: 'ExpressionTag';
expression: Expression;
metadata: {
contains_call_expression: boolean;
/**
* Whether or not the expression contains any dynamic references
* determines whether it will be updated in a render effect or not
*/
dynamic: boolean;
expression: ExpressionMetadata;
};
}
@ -1642,7 +1646,7 @@ declare module 'svelte/compiler' {
/** The 'y' in `class:x={y}`, or the `x` in `class:x` */
expression: Expression;
metadata: {
dynamic: false;
expression: ExpressionMetadata;
};
}
@ -1664,8 +1668,7 @@ declare module 'svelte/compiler' {
expression: null | Expression;
modifiers: string[]; // TODO specify
metadata: {
contains_call_expression: boolean;
dynamic: boolean;
expression: ExpressionMetadata;
};
}
@ -1685,7 +1688,7 @@ declare module 'svelte/compiler' {
value: true | ExpressionTag | Array<ExpressionTag | Text>;
modifiers: Array<'important'>;
metadata: {
dynamic: boolean;
expression: ExpressionMetadata;
};
}
@ -1906,7 +1909,7 @@ declare module 'svelte/compiler' {
name: string;
value: true | ExpressionTag | Array<Text | ExpressionTag>;
metadata: {
dynamic: boolean;
expression: ExpressionMetadata;
/** May be set if this is an event attribute */
delegated: null | DelegatedEvent;
};
@ -1916,8 +1919,7 @@ declare module 'svelte/compiler' {
type: 'SpreadAttribute';
expression: Expression;
metadata: {
contains_call_expression: boolean;
dynamic: boolean;
expression: ExpressionMetadata;
};
}

Loading…
Cancel
Save