From a0b0a96b7f6f94a39ca4b596d14e1a517a83fa0e Mon Sep 17 00:00:00 2001 From: Mateusz Kadlubowski Date: Sun, 10 Nov 2024 14:27:30 +0800 Subject: [PATCH] refactor transform parser --- examples/Templating.stories.svelte | 10 ++-- .../post-transform/story/insert-svelte-csf.ts | 4 +- .../codemods/legacy-story.test.ts | 6 +- .../pre-transform/codemods/legacy-story.ts | 2 +- .../codemods/template-to-snippet.ts | 2 +- src/compiler/pre-transform/index.test.ts | 2 +- .../{children.test.ts => content.test.ts} | 44 +++++++-------- .../analyse/story/{children.ts => content.ts} | 26 ++++----- src/parser/extract/svelte/snippet-block.ts | 55 +++++-------------- .../{children.test.ts => template.test.ts} | 16 +++--- .../svelte/story/{children.ts => template.ts} | 4 +- src/utils/error/parser/extract/svelte.ts | 10 ++-- 12 files changed, 75 insertions(+), 106 deletions(-) rename src/parser/analyse/story/{children.test.ts => content.test.ts} (84%) rename src/parser/analyse/story/{children.ts => content.ts} (82%) rename src/parser/extract/svelte/story/{children.test.ts => template.test.ts} (76%) rename src/parser/extract/svelte/story/{children.ts => template.ts} (86%) diff --git a/examples/Templating.stories.svelte b/examples/Templating.stories.svelte index bf0c870..b7d44dc 100644 --- a/examples/Templating.stories.svelte +++ b/examples/Templating.stories.svelte @@ -73,7 +73,7 @@ --> { const { args, canvasElement } = context; const canvas = within(canvasElement); @@ -85,7 +85,7 @@ }} > {#snippet template(args)} -

Children snippet

+

Template snippet

{args?.text}

{/snippet}
@@ -97,7 +97,7 @@ component', + text: 'This story uses a shared snippet, which is explicitly set as the `template` prop to the component', }} play={async (context) => { const { args, canvasElement } = context; diff --git a/src/compiler/post-transform/story/insert-svelte-csf.ts b/src/compiler/post-transform/story/insert-svelte-csf.ts index 6ee2ffe..d4b7f0c 100644 --- a/src/compiler/post-transform/story/insert-svelte-csf.ts +++ b/src/compiler/post-transform/story/insert-svelte-csf.ts @@ -6,7 +6,7 @@ import { import type { extractStoriesNodesFromExportDefaultFn } from '#parser/extract/compiled/stories'; import { getStoryPropsObjectExpression } from '#parser/extract/compiled/story'; import type { SvelteASTNodes, extractSvelteASTNodes } from '#parser/extract/svelte/nodes'; -import { getStoryChildrenRawCode } from '#parser/analyse/story/children'; +import { getStoryContentRawCode } from '#parser/analyse/story/content'; import { createASTObjectExpression, createASTProperty } from '#parser/ast'; interface Params { @@ -46,7 +46,7 @@ export function insertSvelteCSFToStoryParameters(params: Params) { ); } - const rawCode = getStoryChildrenRawCode({ + const rawCode = getStoryContentRawCode({ nodes: { component: component.svelte.component, svelte, diff --git a/src/compiler/pre-transform/codemods/legacy-story.test.ts b/src/compiler/pre-transform/codemods/legacy-story.test.ts index cb140d8..5408c78 100644 --- a/src/compiler/pre-transform/codemods/legacy-story.test.ts +++ b/src/compiler/pre-transform/codemods/legacy-story.test.ts @@ -185,7 +185,7 @@ describe(transformLegacyStory.name, () => { state: { componentIdentifierName: {} }, }) ) - ).toMatchInlineSnapshot(`""`); + ).toMatchInlineSnapshot(`""`); }); it("when directive 'let:args' is used then it wraps Story fragment with 'children' snippet block", async ({ @@ -280,9 +280,7 @@ describe(transformLegacyStory.name, () => { `); }); - it("leaves existing Story parameters untouched", async ({ - expect, - }) => { + it('leaves existing Story parameters untouched', async ({ expect }) => { const code = ` - +

Static content

`; const ast = getSvelteAST({ code }); const svelteASTNodes = await extractSvelteASTNodes({ ast }); const { storyComponents } = svelteASTNodes; const component = storyComponents[0].component; - const rawSource = getStoryChildrenRawCode({ + const rawSource = getStoryContentRawCode({ nodes: { component, svelte: svelteASTNodes, @@ -181,10 +181,10 @@ describe(getStoryChildrenRawCode.name, () => { originalCode: code, }); - expect(rawSource).toBe(``); + expect(rawSource).toBe(`

Static content

`); }); - it("works when a `children` svelte's snippet block used inside", async ({ expect }) => { + it("works when a `template` svelte's snippet block used inside", async ({ expect }) => { const code = ` - {#snippet children(args)} + {#snippet template(args)} {/snippet} @@ -206,7 +206,7 @@ describe(getStoryChildrenRawCode.name, () => { const svelteASTNodes = await extractSvelteASTNodes({ ast }); const { storyComponents } = svelteASTNodes; const component = storyComponents[0].component; - const rawSource = getStoryChildrenRawCode({ + const rawSource = getStoryContentRawCode({ nodes: { component, svelte: svelteASTNodes, @@ -217,7 +217,7 @@ describe(getStoryChildrenRawCode.name, () => { expect(rawSource).toBe(``); }); - it("inner ``'s children content takes precedence over `setTemplate`", async ({ + it("inner ``'s template content takes precedence over `setTemplate`", async ({ expect, }) => { const code = ` @@ -240,8 +240,8 @@ describe(getStoryChildrenRawCode.name, () => { {/snippet} - {#snippet children(args)} - + {#snippet template(args)} + {/snippet} `; @@ -249,7 +249,7 @@ describe(getStoryChildrenRawCode.name, () => { const svelteASTNodes = await extractSvelteASTNodes({ ast }); const { storyComponents } = svelteASTNodes; const component = storyComponents[0].component; - const rawSource = getStoryChildrenRawCode({ + const rawSource = getStoryContentRawCode({ nodes: { component, svelte: svelteASTNodes, @@ -257,7 +257,7 @@ describe(getStoryChildrenRawCode.name, () => { originalCode: code, }); - expect(rawSource).toBe(``); + expect(rawSource).toBe(``); }); }); }); diff --git a/src/parser/analyse/story/children.ts b/src/parser/analyse/story/content.ts similarity index 82% rename from src/parser/analyse/story/children.ts rename to src/parser/analyse/story/content.ts index 1abdbd7..711ac66 100644 --- a/src/parser/analyse/story/children.ts +++ b/src/parser/analyse/story/content.ts @@ -3,10 +3,10 @@ import dedent from 'dedent'; import { getDefineMetaComponentValue } from '#parser/analyse/define-meta/component-identifier'; import type { SvelteAST } from '#parser/ast'; import type { extractSvelteASTNodes } from '#parser/extract/svelte/nodes'; -import { extractStoryChildrenSnippetBlock } from '#parser/extract/svelte/story/children'; +import { extractStoryTemplateSnippetBlock } from '#parser/extract/svelte/story/template'; import { findSetTemplateSnippetBlock, - findStoryAttributeChildrenSnippetBlock, + findStoryAttributeTemplateSnippetBlock, } from '#parser/extract/svelte/snippet-block'; interface Params { @@ -19,17 +19,17 @@ interface Params { } /** - * Extract the source code of the `` component children. + * Extract the source code of the `` component content (children or template snippet). * Reference: Step 2 from the comment: https://github.com/storybookjs/addon-svelte-csf/pull/181#issuecomment-2143539873 */ -export function getStoryChildrenRawCode(params: Params): string { +export function getStoryContentRawCode(params: Params): string { const { nodes, originalCode, filename } = params; const { component, svelte } = nodes; // `` component is self-closing... if (component.fragment.nodes.length === 0) { /** - * Case - "explicit template" - `children` attribute references to a snippet block at the root level of fragment. + * Case - "explicit template" - `template` attribute references to a snippet block at the root level of fragment. * * Example: * @@ -38,17 +38,17 @@ export function getStoryChildrenRawCode(params: Params): string { * * {/snippet} * - * + * * ``` */ - const storyAttributeChildrenSnippetBlock = findStoryAttributeChildrenSnippetBlock({ + const storyAttributTemplateSnippetBlock = findStoryAttributeTemplateSnippetBlock({ component, nodes: svelte, filename, }); - if (storyAttributeChildrenSnippetBlock) { - return getSnippetBlockBodyRawCode(originalCode, storyAttributeChildrenSnippetBlock); + if (storyAttributTemplateSnippetBlock) { + return getSnippetBlockBodyRawCode(originalCode, storyAttributTemplateSnippetBlock); } /** @@ -88,19 +88,19 @@ export function getStoryChildrenRawCode(params: Params): string { } /** - * Case - Story with children - and with a snippet block `children` inside + * Case - Story with children - and with a snippet block `template` inside * * Example: * * ```svelte * - * {#snippet children(args)} + * {#snippet template(args)} * * {/snippet} * * ``` */ - const storyChildrenSnippetBlock = extractStoryChildrenSnippetBlock(component); + const storyChildrenSnippetBlock = extractStoryTemplateSnippetBlock(component); if (storyChildrenSnippetBlock) { return getSnippetBlockBodyRawCode(originalCode, storyChildrenSnippetBlock); @@ -132,7 +132,7 @@ export function getStoryChildrenRawCode(params: Params): string { * For example, from the following case: * * ```svelte - * {#snippet children(args)} + * {#snippet template(args)} * * "Static text" * diff --git a/src/parser/extract/svelte/snippet-block.ts b/src/parser/extract/svelte/snippet-block.ts index 778bd80..e562508 100644 --- a/src/parser/extract/svelte/snippet-block.ts +++ b/src/parser/extract/svelte/snippet-block.ts @@ -4,76 +4,47 @@ import { extractStoryAttributesNodes } from '#parser/extract/svelte/story/attrib import { InvalidSetTemplateFirstArgumentError, - InvalidStoryChildrenAttributeError, + InvalidStoryTemplateAttributeError, } from '#utils/error/parser/extract/svelte'; /** - * Svelte 5 allows to passing `children` as attribute _(aka prop)_. - * * For example: * * ```svelte * {#snippet myTemplate()} * * {/snippet} - * + * * ``` * * This function attempts to extract the AST node of the snippet block from the root fragment of `*.svelte` file, - * which was referenced by the attribute `children`. Following example above - it would be snippet `myTemplate`. + * which was referenced by the attribute `template`. Following example above - it would be snippet `myTemplate`. */ -export function findStoryAttributeChildrenSnippetBlock(options: { +export function findStoryAttributeTemplateSnippetBlock(options: { component: SvelteAST.Component; nodes: SvelteASTNodes; filename?: string; -}) { +}): SvelteAST.SnippetBlock | undefined { const { component, nodes, filename } = options; - const { children } = extractStoryAttributesNodes({ + const { template } = extractStoryAttributesNodes({ component, - attributes: ['children'], + attributes: ['template'], }); - if (!children) { + if (!template) { return; } - const { value } = children; - - if (value === true) { - throw new InvalidStoryChildrenAttributeError({ - component: component, - childrenAttribute: children, - filename, - }); - } - - // value is SvelteAST.ExpressionTag - if (!Array.isArray(value)) { - if (value.expression.type !== 'Identifier') { - throw new InvalidStoryChildrenAttributeError({ - component: component, - childrenAttribute: children, - filename, - }); - } - - return findSnippetBlockByName({ - name: value.expression.name, - nodes: nodes, - }); - } - - // value is Array - I haven't figured out when it would happen - if (value[0].type !== 'ExpressionTag') { - throw new InvalidStoryChildrenAttributeError({ + if (template.value === true || Array.isArray(template.value)) { + throw new InvalidStoryTemplateAttributeError({ component: component, - childrenAttribute: children, + templateAttribute: template, filename, }); } return findSnippetBlockByName({ - name: value[0].expression.name, + name: template.value.expression.name, nodes: nodes, }); } @@ -125,7 +96,7 @@ export function findSetTemplateSnippetBlock(options: { function findSnippetBlockByName(options: { /** * Snippet's block expression name to find by. - * For example, from the following: `{#snippet children(args)}` - `children` + * For example, from the following: `{#snippet template(args)}` - `template` */ name: string; nodes: SvelteASTNodes; diff --git a/src/parser/extract/svelte/story/children.test.ts b/src/parser/extract/svelte/story/template.test.ts similarity index 76% rename from src/parser/extract/svelte/story/children.test.ts rename to src/parser/extract/svelte/story/template.test.ts index 4aa4bb8..38e353b 100644 --- a/src/parser/extract/svelte/story/children.test.ts +++ b/src/parser/extract/svelte/story/template.test.ts @@ -1,12 +1,12 @@ import { describe, it } from 'vitest'; -import { extractStoryChildrenSnippetBlock } from './children'; +import { extractStoryTemplateSnippetBlock } from './template'; import { getSvelteAST } from '#parser/ast'; import { extractSvelteASTNodes } from '#parser/extract/svelte/nodes'; -describe(extractStoryChildrenSnippetBlock.name, () => { - it('returns correctly AST node, when a `` compponent has a snippet block `children` inside', async ({ +describe(extractStoryTemplateSnippetBlock.name, () => { + it('returns correctly AST node, when a `` compponent has a snippet block `template` inside', async ({ expect, }) => { const code = ` @@ -20,8 +20,8 @@ describe(extractStoryChildrenSnippetBlock.name, () => { }); - - {#snippet children(args)} + + {#snippet template(args)} {/snippet} @@ -32,8 +32,8 @@ describe(extractStoryChildrenSnippetBlock.name, () => { const { storyComponents } = svelteASTNodes; const component = storyComponents[0].component; - expect(extractStoryChildrenSnippetBlock(component)).toBeDefined(); - expect(extractStoryChildrenSnippetBlock(component)?.expression.name).toBe('children'); + expect(extractStoryTemplateSnippetBlock(component)).toBeDefined(); + expect(extractStoryTemplateSnippetBlock(component)?.expression.name).toBe('template'); }); it('returns undefined, when a `` compponent is a self-closing tag', async ({ expect }) => { @@ -56,6 +56,6 @@ describe(extractStoryChildrenSnippetBlock.name, () => { const { storyComponents } = svelteASTNodes; const component = storyComponents[0].component; - expect(extractStoryChildrenSnippetBlock(component)).not.toBeDefined(); + expect(extractStoryTemplateSnippetBlock(component)).not.toBeDefined(); }); }); diff --git a/src/parser/extract/svelte/story/children.ts b/src/parser/extract/svelte/story/template.ts similarity index 86% rename from src/parser/extract/svelte/story/children.ts rename to src/parser/extract/svelte/story/template.ts index 2764876..39bab56 100644 --- a/src/parser/extract/svelte/story/children.ts +++ b/src/parser/extract/svelte/story/template.ts @@ -8,11 +8,11 @@ type Result = SvelteAST.SnippetBlock | undefined; * This AST node will help us in the further transformation of the `parameters.docs.source.code` on the compiled code, * and at runtime. */ -export function extractStoryChildrenSnippetBlock(component: SvelteAST.Component): Result { +export function extractStoryTemplateSnippetBlock(component: SvelteAST.Component): Result { const { fragment } = component; const { nodes } = fragment; return nodes.find( - (node) => node.type === 'SnippetBlock' && node.expression.name === 'children' + (node) => node.type === 'SnippetBlock' && node.expression.name === 'template' ) as Result; } diff --git a/src/utils/error/parser/extract/svelte.ts b/src/utils/error/parser/extract/svelte.ts index bf1650f..3729f69 100644 --- a/src/utils/error/parser/extract/svelte.ts +++ b/src/utils/error/parser/extract/svelte.ts @@ -149,24 +149,24 @@ export class GetDefineMetaFirstArgumentError extends StorybookSvelteCSFError { } } -export class InvalidStoryChildrenAttributeError extends StorybookSvelteCSFError { +export class InvalidStoryTemplateAttributeError extends StorybookSvelteCSFError { readonly category = StorybookSvelteCSFError.CATEGORY.parserExtractSvelte; readonly code = 7; public documentation = true; - public childrenAttribute: SvelteAST.Attribute; + public templateAttribute: SvelteAST.Attribute; constructor({ filename, component, - childrenAttribute, + templateAttribute, }: { filename?: StorybookSvelteCSFError['filename']; component: NonNullable; - childrenAttribute: InvalidStoryChildrenAttributeError['childrenAttribute']; + templateAttribute: InvalidStoryTemplateAttributeError['templateAttribute']; }) { super({ filename, component }); - this.childrenAttribute = childrenAttribute; + this.templateAttribute = templateAttribute; } template() {