diff --git a/src/node/block/await.ts b/src/node/block/await.ts index e4241145..db8465b9 100644 --- a/src/node/block/await.ts +++ b/src/node/block/await.ts @@ -1,28 +1,140 @@ +/** + * Related to Svelte AST node {@link AwaitBlock}. + * @see {@link https://svelte.dev/docs/logic-blocks#await} + * @module + */ + import { print } from "esrap"; import { print_fragment } from "#node/fragment"; import { define_printer } from "#printer"; import type { AwaitBlock } from "#types"; +import { insert } from "#util"; +/** + * Print Svelte AST node {@link AwaitBlock} as string. + * @see {@link https://svelte.dev/docs/logic-blocks#await} + * + * @example standard + * ```svelte + * {#await expression}...{:then name}...{:catch name}...{/await} + * ``` + * + * @example without catch + * ```svelte + * {#if expression}...{:else if expression}...{/if} + * ``` + * + * @example without pending body + * ```svelte + * {#await expression then name}...{/await} + * ``` + * + * @example with catch body only + * ```svelte + * https://svelte.dev/docs/logic-blocks#await + * ``` + */ export const print_await_block = define_printer((node: AwaitBlock, options) => { const { catch: catch_, error, expression, pending, then, value } = node; - return [ - `{#await ${print(expression).code}}`, - pending ? print_fragment(pending, options) : "", - [ - // then block - "{:then", - value ? ` ${print(value).code}` : "", - "}", - then ? print_fragment(then, options) : "", - ].join(""), - [ - // catch block - "{:catch", - error ? ` ${print(error).code}` : "", - "}", - catch_ ? print_fragment(catch_, options) : "", - ].join(""), + + return insert( + "{#await ", + print(expression).code, + insert( + then && !pending && insert(" then", value && insert(" ", print(value).code)), + catch_ && !pending && insert(" catch", error && insert(" ", print(error).code)), + ), + "}", + pending && print_fragment(pending, options), + then && + insert( + pending && insert("{:then", value && insert(" ", print(value).code), "}"), + print_fragment(then, options), + ), + catch_ && + insert( + pending && insert("{:catch", error && insert(" ", print(error).code), "}"), + print_fragment(catch_, options), + ), "{/await}", - ].join(""); + ); }); + +if (import.meta.vitest) { + const { describe, it } = import.meta.vitest; + const [{ parse_and_extract_svelte_node }, { DEFAULT_OPTIONS }] = await Promise.all([ + import("#test/mod"), + import("#options"), + ]); + + describe("AwaitBlock", () => { + it("correctly prints standard example", ({ expect }) => { + const code = ` + {#await promise} +

waiting for the promise to resolve...

+ {:then value} +

The value is {value}

+ {:catch error} +

Something went wrong: {error.message}

+ {/await} + `; + const node = parse_and_extract_svelte_node(code, "AwaitBlock"); + expect(print_await_block(node, DEFAULT_OPTIONS)).toMatchInlineSnapshot(` + "{#await promise} +

waiting for the promise to resolve...

+ {:then value} +

The value is {value}

+ {:catch error} +

Something went wrong: {error.message}

+ {/await}" + `); + }); + + it("correctly prints with omitted catch", ({ expect }) => { + const code = ` + {#await promise} +

waiting for the promise to resolve...

+ {:then value} +

The value is {value}

+ {/await} + `; + const node = parse_and_extract_svelte_node(code, "AwaitBlock"); + expect(print_await_block(node, DEFAULT_OPTIONS)).toMatchInlineSnapshot(` + "{#await promise} +

waiting for the promise to resolve...

+ {:then value} +

The value is {value}

+ {/await}" + `); + }); + + it("correctly prints omitted initial block", ({ expect }) => { + const code = ` + {#await promise then value} +

The value is {value}

+ {/await} + `; + const node = parse_and_extract_svelte_node(code, "AwaitBlock"); + expect(print_await_block(node, DEFAULT_OPTIONS)).toMatchInlineSnapshot(` + "{#await promise then value} +

The value is {value}

+ {/await}" + `); + }); + + it("correctly prints omitted then block", ({ expect }) => { + const code = ` + {#await promise catch error} +

The error is {error}

+ {/await} + `; + const node = parse_and_extract_svelte_node(code, "AwaitBlock"); + expect(print_await_block(node, DEFAULT_OPTIONS)).toMatchInlineSnapshot(` + "{#await promise catch error} +

The error is {error}

+ {/await}" + `); + }); + }); +} diff --git a/src/node/block/if.ts b/src/node/block/if.ts index de691fc9..07b9019f 100644 --- a/src/node/block/if.ts +++ b/src/node/block/if.ts @@ -9,6 +9,7 @@ import { print } from "esrap"; import { print_fragment } from "#node/fragment"; import { define_printer } from "#printer"; import type { Fragment, IfBlock } from "#types"; +import { insert } from "#util"; const has_alternate_else_if = (node: Fragment): boolean => node.nodes.some((n) => n.type === "IfBlock"); @@ -45,37 +46,25 @@ export const print_if_block = define_printer((node: IfBlock, options) => { const { alternate, consequent, elseif, test } = node; if (elseif) { - return [ + return insert( "{:else if", " ", print(test).code, "}", print_fragment(consequent, options), - alternate - ? [ - // - !has_alternate_else_if(alternate) ? "{:else}" : "", - print_fragment(alternate, options), - ].join("") - : "", - ].join(""); + alternate && insert(!has_alternate_else_if(alternate) && "{:else}", print_fragment(alternate, options)), + ); } - return [ + return insert( "{#if", " ", print(test).code, "}", print_fragment(consequent, options), - alternate - ? [ - // - !has_alternate_else_if(alternate) ? "{:else}" : "", - print_fragment(alternate, options), - ].join("") - : "", + alternate && insert(!has_alternate_else_if(alternate) && "{:else}", print_fragment(alternate, options)), "{/if}", - ].join(""); + ); }); if (import.meta.vitest) { diff --git a/src/node/tag/expression.ts b/src/node/tag/expression.ts index 3721a8b0..52230994 100644 --- a/src/node/tag/expression.ts +++ b/src/node/tag/expression.ts @@ -7,6 +7,7 @@ import { print } from "esrap"; import { define_printer } from "#printer"; import type { ExpressionTag } from "#types"; +import { insert } from "#util"; /** * Print Svelte AST node {@link ExpressionTag} as string. @@ -18,12 +19,7 @@ import type { ExpressionTag } from "#types"; */ export const print_expression_tag = define_printer((node: ExpressionTag, _options) => { const { expression } = node; - return [ - // - "{", - print(expression).code, - "}", - ].join(""); + return insert("{", print(expression).code, "}"); }); if (import.meta.vitest) { diff --git a/src/util.ts b/src/util.ts index f75a2b04..09b4cca3 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1 +1,16 @@ export const NEW_LINE = "\n"; + +/** + * Filter out the falsy values and join the truthy (string) values together. + */ +export function insert(...values: (string | null | false)[]) { + let results = ""; + + for (const value of values) { + if (typeof value === "string") { + results += value; + } + } + + return results; +}