UNPKG

svelte

Version:

Cybernetically enhanced web apps

151 lines (128 loc) 3.89 kB
/** @import { Expression, PrivateIdentifier, SourceLocation } from 'estree' */ /** @import { AST, Binding } from '#compiler' */ import * as b from '#compiler/builders'; /** * All nodes that can appear elsewhere than the top level, have attributes and can contain children */ const element_nodes = [ 'SvelteElement', 'RegularElement', 'SvelteFragment', 'Component', 'SvelteComponent', 'SvelteSelf', 'SlotElement' ]; /** * Returns true for all nodes that can appear elsewhere than the top level, have attributes and can contain children * @param {AST.SvelteNode} node * @returns {node is AST.Component | AST.RegularElement | AST.SlotElement | AST.SvelteComponent | AST.SvelteElement | AST.SvelteFragment | AST.SvelteSelf} */ export function is_element_node(node) { return element_nodes.includes(node.type); } /** * Returns true for all component-like nodes * @param {AST.SvelteNode} node * @returns {node is AST.Component | AST.SvelteComponent | AST.SvelteSelf} */ export function is_component_node(node) { return ['Component', 'SvelteComponent', 'SvelteSelf'].includes(node.type); } /** * @param {AST.RegularElement | AST.SvelteElement} node * @returns {boolean} */ export function is_custom_element_node(node) { return ( node.type === 'RegularElement' && (node.name.includes('-') || node.attributes.some((attr) => attr.type === 'Attribute' && attr.name === 'is')) ); } /** * @param {string} name * @param {SourceLocation | null} name_loc * @param {number} start * @param {number} end * @param {AST.Attribute['value']} value * @returns {AST.Attribute} */ export function create_attribute(name, name_loc, start, end, value) { return { type: 'Attribute', start, end, name, name_loc, value, metadata: { delegated: false, needs_clsx: false } }; } export class ExpressionMetadata { /** True if the expression references state directly, or _might_ (via member/call expressions) */ has_state = false; /** True if the expression involves a call expression (often, it will need to be wrapped in a derived) */ has_call = false; /** True if the expression contains `await` */ has_await = false; /** True if the expression includes a member expression */ has_member_expression = false; /** True if the expression includes an assignment or an update */ has_assignment = false; /** * All the bindings that are referenced eagerly (not inside functions) in this expression * @type {Set<Binding>} */ dependencies = new Set(); /** * True if the expression references state directly, or _might_ (via member/call expressions) * @type {Set<Binding>} */ references = new Set(); /** @type {null | Set<Expression>} */ #blockers = null; #get_blockers() { if (!this.#blockers) { this.#blockers = new Set(); for (const d of this.dependencies) { if (d.blocker) this.#blockers.add(d.blocker); } } return this.#blockers; } blockers() { return b.array([...this.#get_blockers()]); } has_blockers() { return this.#get_blockers().size > 0; } is_async() { return this.has_await || this.#get_blockers().size > 0; } /** * @param {ExpressionMetadata} source */ merge(source) { this.has_state ||= source.has_state; this.has_call ||= source.has_call; this.has_await ||= source.has_await; this.has_member_expression ||= source.has_member_expression; this.has_assignment ||= source.has_assignment; this.#blockers = null; // so that blockers are recalculated for (const r of source.references) this.references.add(r); for (const b of source.dependencies) this.dependencies.add(b); } } /** * @param {Expression | PrivateIdentifier} node */ export function get_name(node) { if (node.type === 'Literal') return String(node.value); if (node.type === 'PrivateIdentifier') return '#' + node.name; if (node.type === 'Identifier') return node.name; return null; }