give this another try

pull/10320/head
S. Elliott Johnson 2 years ago
parent eab690d31a
commit 76903719ca

@ -67,7 +67,7 @@ export type MessageEventHandler<T extends EventTarget> = EventHandler<MessageEve
export interface DOMAttributes<T extends EventTarget> { export interface DOMAttributes<T extends EventTarget> {
// Implicit children prop every element has // Implicit children prop every element has
// Add this here so that libraries doing `$props<HTMLButtonAttributes>()` don't need a separate interface // Add this here so that libraries doing `$props<HTMLButtonAttributes>()` don't need a separate interface
children?: import('svelte').Snippet<void>; children?: import('svelte').Snippet;
// Clipboard Events // Clipboard Events
'on:copy'?: ClipboardEventHandler<T> | undefined | null; 'on:copy'?: ClipboardEventHandler<T> | undefined | null;

@ -274,7 +274,12 @@ function open(parser) {
parser.allow_whitespace(); parser.allow_whitespace();
const context = parser.match(')') ? null : read_context(parser); const elements = [];
while (!parser.match(')')) {
elements.push(read_context(parser));
parser.eat(',');
parser.allow_whitespace();
}
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat(')', true); parser.eat(')', true);
@ -294,7 +299,10 @@ function open(parser) {
end: name_end, end: name_end,
name name
}, },
context, context: {
type: 'ArrayPattern',
elements
},
body: create_fragment() body: create_fragment()
}) })
); );
@ -589,10 +597,6 @@ function special(parser) {
error(expression, 'TODO', 'expected an identifier followed by (...)'); error(expression, 'TODO', 'expected an identifier followed by (...)');
} }
if (expression.arguments.length > 1) {
error(expression.arguments[1], 'TODO', 'expected at most one argument');
}
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
@ -602,7 +606,7 @@ function special(parser) {
start, start,
end: parser.index, end: parser.index,
expression: expression.callee, expression: expression.callee,
argument: expression.arguments[0] ?? null arguments: expression.arguments
}) })
); );
} }

@ -1755,9 +1755,9 @@ export const template_visitors = {
/** @type {import('estree').Expression[]} */ /** @type {import('estree').Expression[]} */
const args = [context.state.node]; const args = [context.state.node];
if (node.argument) { node.arguments.forEach((arg) =>
args.push(b.thunk(/** @type {import('estree').Expression} */ (context.visit(node.argument)))); args.push(b.thunk(/** @type {import('estree').Expression} */ (context.visit(arg))))
} );
let snippet_function = /** @type {import('estree').Expression} */ ( let snippet_function = /** @type {import('estree').Expression} */ (
context.visit(node.expression) context.visit(node.expression)
@ -2445,21 +2445,21 @@ export const template_visitors = {
/** @type {import('estree').BlockStatement} */ /** @type {import('estree').BlockStatement} */
let body; let body;
if (node.context) {
const id = node.context.type === 'Identifier' ? node.context : b.id('$$context');
args.push(id);
/** @type {import('estree').Statement[]} */ /** @type {import('estree').Statement[]} */
const declarations = []; const declarations = [];
// some of this is duplicated with EachBlock — TODO dedupe? node.context.elements.forEach((element, i) => {
if (node.context.type === 'Identifier') { if (!element) return;
const id = element.type === 'Identifier' ? element : b.id(`$$context${i}`);
args.push(id);
if (element.type === 'Identifier') {
const binding = /** @type {import('#compiler').Binding} */ ( const binding = /** @type {import('#compiler').Binding} */ (
context.state.scope.get(id.name) context.state.scope.get(id.name)
); );
binding.expression = b.call(id); binding.expression = b.call(id);
} else { } else {
const paths = extract_paths(node.context); const paths = extract_paths(element);
for (const path of paths) { for (const path of paths) {
const name = /** @type {import('estree').Identifier} */ (path.node).name; const name = /** @type {import('estree').Identifier} */ (path.node).name;
@ -2471,7 +2471,7 @@ export const template_visitors = {
path.node, path.node,
b.thunk( b.thunk(
/** @type {import('estree').Expression} */ ( /** @type {import('estree').Expression} */ (
context.visit(path.expression?.(b.call('$$context'))) context.visit(path.expression?.(b.call(`$$arg${i}`)))
) )
) )
) )
@ -2486,14 +2486,12 @@ export const template_visitors = {
binding.expression = b.call(name); binding.expression = b.call(name);
} }
} }
});
body = b.block([ body = b.block([
...declarations, ...declarations,
.../** @type {import('estree').BlockStatement} */ (context.visit(node.body)).body .../** @type {import('estree').BlockStatement} */ (context.visit(node.body)).body
]); ]);
} else {
body = /** @type {import('estree').BlockStatement} */ (context.visit(node.body));
}
const path = context.path; const path = context.path;
// If we're top-level, then we can create a function for the snippet so that it can be referenced // If we're top-level, then we can create a function for the snippet so that it can be referenced

@ -1127,21 +1127,19 @@ const template_visitors = {
const snippet_function = state.options.dev const snippet_function = state.options.dev
? b.call('$.validate_snippet', node.expression) ? b.call('$.validate_snippet', node.expression)
: node.expression; : node.expression;
if (node.argument) {
state.template.push( state.template.push(
t_statement( t_statement(
b.stmt( b.stmt(
b.call( b.call(
snippet_function, snippet_function,
b.id('$$payload'), b.id('$$payload'),
/** @type {import('estree').Expression} */ (context.visit(node.argument)) ...node.arguments.map(
(arg) => /** @type {import('estree').Expression} */ (context.visit(arg))
)
) )
) )
) )
); );
} else {
state.template.push(t_statement(b.stmt(b.call(snippet_function, b.id('$$payload')))));
}
state.template.push(t_expression(anchor_id)); state.template.push(t_expression(anchor_id));
}, },

@ -592,11 +592,9 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
const child_scope = state.scope.child(); const child_scope = state.scope.child();
scopes.set(node, child_scope); scopes.set(node, child_scope);
if (node.context) {
for (const id of extract_identifiers(node.context)) { for (const id of extract_identifiers(node.context)) {
child_scope.declare(id, 'each', 'let'); child_scope.declare(id, 'each', 'let');
} }
}
context.next({ scope: child_scope }); context.next({ scope: child_scope });
}, },

@ -2,6 +2,7 @@ import type { Binding } from '#compiler';
import type { import type {
ArrayExpression, ArrayExpression,
ArrowFunctionExpression, ArrowFunctionExpression,
ArrayPattern,
VariableDeclaration, VariableDeclaration,
VariableDeclarator, VariableDeclarator,
Expression, Expression,
@ -12,7 +13,8 @@ import type {
Node, Node,
ObjectExpression, ObjectExpression,
Pattern, Pattern,
Program Program,
SpreadElement
} from 'estree'; } from 'estree';
export interface BaseNode { export interface BaseNode {
@ -146,7 +148,7 @@ export interface DebugTag extends BaseNode {
export interface RenderTag extends BaseNode { export interface RenderTag extends BaseNode {
type: 'RenderTag'; type: 'RenderTag';
expression: Identifier; expression: Identifier;
argument: null | Expression; argument: (Expression | SpreadElement)[];
} }
type Tag = ExpressionTag | HtmlTag | ConstTag | DebugTag | RenderTag; type Tag = ExpressionTag | HtmlTag | ConstTag | DebugTag | RenderTag;
@ -413,7 +415,7 @@ export interface KeyBlock extends BaseNode {
export interface SnippetBlock extends BaseNode { export interface SnippetBlock extends BaseNode {
type: 'SnippetBlock'; type: 'SnippetBlock';
expression: Identifier; expression: Identifier;
context: null | Pattern; context: ArrayPattern;
body: Fragment; body: Fragment;
} }

@ -2843,7 +2843,7 @@ export function sanitize_slots(props) {
/** /**
* @param {() => Function} get_snippet * @param {() => Function} get_snippet
* @param {Node} node * @param {Node} node
* @param {() => any} args * @param {(() => any)[]} args
* @returns {void} * @returns {void}
*/ */
export function snippet_effect(get_snippet, node, args) { export function snippet_effect(get_snippet, node, args) {

@ -195,8 +195,11 @@ declare const SnippetReturn: unique symbol;
* ``` * ```
* You can only call a snippet through the `{@render ...}` tag. * You can only call a snippet through the `{@render ...}` tag.
*/ */
export interface Snippet<T = void> { export interface Snippet<T extends unknown[] = []> {
(arg: T): typeof SnippetReturn & { (
this: void,
...args: T
): typeof SnippetReturn & {
_: 'functions passed to {@render ...} tags must use the `Snippet` type imported from "svelte"'; _: 'functions passed to {@render ...} tags must use the `Snippet` type imported from "svelte"';
}; };
} }

@ -20,18 +20,18 @@ const d: Snippet<boolean> = (a: string, b: number) => {
const e: Snippet = (a: string) => { const e: Snippet = (a: string) => {
return return_type; return return_type;
}; };
const f: Snippet = (a) => {
// @ts-expect-error // @ts-expect-error
const f: Snippet = (a) => {
a?.x; a?.x;
return return_type; return return_type;
}; };
const g: Snippet<boolean> = (a) => { const g: Snippet<[boolean]> = (a) => {
// @ts-expect-error // @ts-expect-error
a === ''; a === '';
a === true; a === true;
return return_type; return return_type;
}; };
const h: Snippet<{ a: true }> = (a) => { const h: Snippet<[{ a: true }]> = (a) => {
a.a === true; a.a === true;
return return_type; return return_type;
}; };

Loading…
Cancel
Save