Skip to content

Commit

Permalink
refactor: Finish feature of extracting raw source of Story source.code
Browse files Browse the repository at this point in the history
... and added tests!
  • Loading branch information
xeho91 committed Jun 6, 2024
1 parent 63ae9d4 commit be44123
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 144 deletions.
12 changes: 6 additions & 6 deletions src/compiler/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export async function plugin(): Promise<Plugin> {
}

const svelteAST = getSvelteAST({ code: rawCode, filename: id });
const svelteNodes = await extractSvelteASTNodes({
const svelteASTNodes = await extractSvelteASTNodes({
ast: svelteAST,
filename: id,
});
Expand All @@ -76,14 +76,14 @@ export async function plugin(): Promise<Plugin> {
* then other nodes' `start` and `end` numbers will not be correct anymore.
* Hence the reason why reversing both arrays with stories _(svelte and compiled)_.
*/
const svelteStories = [...svelteNodes.storyComponents].reverse();
const svelteStories = [...svelteASTNodes.storyComponents].reverse();
const compiledStories = [...extractedCompiledStoriesNodes].reverse();

for (const [index, compiled] of Object.entries(compiledStories)) {
updateCompiledStoryProps({
code: magicCompiledCode,
nodes: { svelte: svelteStories[index], compiled },
setTemplateSnippetBlock: svelteNodes.setTemplateSnippetBlock,
componentASTNodes: { svelte: svelteStories[index], compiled },
svelteASTNodes,
filename: id,
originalCode: rawCode,
});
Expand All @@ -98,7 +98,7 @@ export async function plugin(): Promise<Plugin> {
code: magicCompiledCode,
nodes: {
compiled: compiledNodes,
svelte: svelteNodes,
svelte: svelteASTNodes,
},
filename: id,
});
Expand All @@ -111,7 +111,7 @@ export async function plugin(): Promise<Plugin> {
code: magicCompiledCode,
nodes: {
compiled: compiledNodes,
svelte: svelteNodes,
svelte: svelteASTNodes,
},
filename: id,
});
Expand Down
17 changes: 7 additions & 10 deletions src/compiler/transform/compiled-story-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,21 @@ import { insertSourceCode } from './story-props/insert-source-code.js';

import type { extractStoriesNodesFromExportDefaultFn } from '../../parser/extract/compiled/stories.js';
import type { SvelteASTNodes } from '../../parser/extract/svelte/nodes.js';
import type { extractFragmentNodes } from '../../parser/extract/svelte/fragment-nodes.js';

interface Params {
code: MagicString;
nodes: {
componentASTNodes: {
svelte: SvelteASTNodes['storyComponents'][number];
compiled: Awaited<ReturnType<typeof extractStoriesNodesFromExportDefaultFn>>[number];
};
setTemplateSnippetBlock: Awaited<
ReturnType<typeof extractFragmentNodes>
>['setTemplateSnippetBlock'];
svelteASTNodes: SvelteASTNodes;
filename: string;
originalCode: string;
}

export function updateCompiledStoryProps(params: Params) {
const { code, setTemplateSnippetBlock, nodes, filename, originalCode } = params;
const { svelte, compiled } = nodes;
const { code, svelteASTNodes, componentASTNodes, filename, originalCode } = params;
const { svelte, compiled } = componentASTNodes;
const { component, comment } = svelte;

const storyPropsObjectExpression = getStoryPropsObjectExpression(compiled);
Expand Down Expand Up @@ -79,15 +76,15 @@ export function updateCompiledStoryProps(params: Params) {
}
insertSourceCode({
component,
setTemplateSnippetBlock,
svelteASTNodes,
currentDocsProperty,
filename,
originalCode,
});

return updateCompiledNode({
code,
nodes,
nodes: componentASTNodes,
metaObjectExpression: storyPropsObjectExpression,
});
}
Expand Down Expand Up @@ -137,7 +134,7 @@ function updateCompiledNode({
metaObjectExpression,
}: {
code: Params['code'];
nodes: Params['nodes'];
nodes: Params['componentASTNodes'];
metaObjectExpression: ObjectExpression;
}) {
const { compiled } = nodes;
Expand Down
12 changes: 5 additions & 7 deletions src/compiler/transform/story-props/insert-source-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import type { ObjectExpression, Property } from 'estree';
import type { Component } from 'svelte/compiler';

import { createASTObjectExpression, createASTProperty, findASTPropertyIndex } from './shared.js';
import type { extractFragmentNodes } from '../../../parser/extract/svelte/fragment-nodes.js';

import { getStoryChildrenRawSource } from '../../../parser/analyse/Story/children.js';
import type { extractSvelteASTNodes } from '../../../parser/extract/svelte/nodes.js';

interface Params {
component: Component;
setTemplateSnippetBlock: Awaited<
ReturnType<typeof extractFragmentNodes>
>['setTemplateSnippetBlock'];
svelteASTNodes: Awaited<ReturnType<typeof extractSvelteASTNodes>>;
currentDocsProperty: Property;
filename?: string;
originalCode: string;
Expand All @@ -21,8 +20,7 @@ interface Params {
* If he didn't, then it will insert to the existing ObjectExpression.
*/
export function insertSourceCode(params: Params) {
const { component, setTemplateSnippetBlock, currentDocsProperty, filename, originalCode } =
params;
const { component, svelteASTNodes, currentDocsProperty, filename, originalCode } = params;

if (currentDocsProperty.value.type !== 'ObjectExpression') {
throw new Error(
Expand Down Expand Up @@ -64,7 +62,7 @@ export function insertSourceCode(params: Params) {

const value = getStoryChildrenRawSource({
component,
setTemplateSnippetBlock,
svelteASTNodes,
originalCode,
});

Expand Down
249 changes: 249 additions & 0 deletions src/parser/analyse/Story/children.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import { describe, it } from 'vitest';

import { getStoryChildrenRawSource } from './children.js';

import { getSvelteAST } from '../../ast.js';
import { extractSvelteASTNodes } from '../../extract/svelte/nodes.js';

describe(getStoryChildrenRawSource.name, () => {
describe('When a `<Story />` is a self-closing tag...', () => {
it('works when `children` attribute was provided with a reference to snippet at the root of fragment', async ({
expect,
}) => {
const code = `
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
import SampleComponent from "./SampleComponent.svelte";
const { Story } = defineMeta({
component: SampleComponent,
});
</script>
{#snippet template(args)}
<SomeComponent {...args} />
{/snippet}
<Story name="Default" children={template} />
`;
const ast = getSvelteAST({ code });
const svelteASTNodes = await extractSvelteASTNodes({ ast });
const { storyComponents } = svelteASTNodes;
const component = storyComponents[0].component;
const rawSource = getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode: code,
});

expect(rawSource).toBe('<SomeComponent {...args} />');
});

it('works when `setTemplate` was used correctly in the instance tag', async ({ expect }) => {
const code = `
<script context="module">
import { defineMeta, setTemplate } from "@storybook/addon-svelte-csf";
import SampleComponent from "./SampleComponent.svelte";
const { Story } = defineMeta({
component: SampleComponent,
});
</script>
<script>
setTemplate(template);
</script>
{#snippet template(args)}
<SomeComponent {...args} />
{/snippet}
<Story name="Default" />
`;
const ast = getSvelteAST({ code });
const svelteASTNodes = await extractSvelteASTNodes({ ast });
const { storyComponents } = svelteASTNodes;
const component = storyComponents[0].component;
const rawSource = getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode: code,
});

expect(rawSource).toBe('<SomeComponent {...args} />');
});

it('works implicit `children` attribute takes precedence over `setTemplate`', async ({
expect,
}) => {
const code = `
<script context="module">
import { defineMeta, setTemplate } from "@storybook/addon-svelte-csf";
import SampleComponent from "./SampleComponent.svelte";
const { Story } = defineMeta({
component: SampleComponent,
});
</script>
<script>
setTemplate(template);
</script>
{#snippet templateForSetTemplate(args)}
<SomeComponent wins="setTemplate" {...args} />
{/snippet}
{#snippet templateForChildren(args)}
<SomeComponent wins="childrenAttribute" {...args} />
{/snippet}
<Story name="Default" children={templateForChildren} />
`;
const ast = getSvelteAST({ code });
const svelteASTNodes = await extractSvelteASTNodes({ ast });
const { storyComponents } = svelteASTNodes;
const component = storyComponents[0].component;
const rawSource = getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode: code,
});

expect(rawSource).toBe(`<SomeComponent wins="childrenAttribute" {...args} />`);
});

it('works when no `setTemplate`, no `children` attribute, just a story', async ({ expect }) => {
const code = `
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
import SampleComponent from "./SampleComponent.svelte";
const { Story } = defineMeta({
component: SampleComponent,
});
</script>
<Story name="Default" />
`;
const ast = getSvelteAST({ code });
const svelteASTNodes = await extractSvelteASTNodes({ ast });
const { storyComponents } = svelteASTNodes;
const component = storyComponents[0].component;
const rawSource = getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode: code,
});

expect(rawSource).toBe(`<SampleComponent {...args} />`);
});
});

describe('When a `<Story />` is NOT a self-closing tag...', () => {
it('works when a static children content provided', async ({ expect }) => {
const code = `
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
import SampleComponent from "./SampleComponent.svelte";
const { Story } = defineMeta({
component: SampleComponent,
});
</script>
<Story name="Default">
<SomeComponent foo="bar" />
</Story>
`;
const ast = getSvelteAST({ code });
const svelteASTNodes = await extractSvelteASTNodes({ ast });
const { storyComponents } = svelteASTNodes;
const component = storyComponents[0].component;
const rawSource = getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode: code,
});

expect(rawSource).toBe(`<SomeComponent foo="bar" />`);
});

it("works when a `children` svelte's snippet block used inside", async ({ expect }) => {
const code = `
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
import SampleComponent from "./SampleComponent.svelte";
const { Story } = defineMeta({
component: SampleComponent,
});
</script>
<Story name="Default">
{#snippet children(args)}
<SomeComponent {...args} />
{/snippet}
</Story>
`;
const ast = getSvelteAST({ code });
const svelteASTNodes = await extractSvelteASTNodes({ ast });
const { storyComponents } = svelteASTNodes;
const component = storyComponents[0].component;
const rawSource = getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode: code,
});

expect(rawSource).toBe(`<SomeComponent {...args} />`);
});

it("inner `<Story>`'s children content takes precedence over `setTemplate`", async ({
expect,
}) => {
const code = `
<script context="module">
import { defineMeta, setTemplate } from "@storybook/addon-svelte-csf";
import SampleComponent from "./SampleComponent.svelte";
const { Story } = defineMeta({
component: SampleComponent,
});
</script>
<script>
setTemplate(template);
</script>
{#snippet templateForSetTemplate(args)}
<SomeComponent wins="setTemplate" {...args} />
{/snippet}
<Story name="Default">
{#snippet children(args)}
<SomeComponent wins="children" {...args} />
{/snippet}
</Story>
`;
const ast = getSvelteAST({ code });
const svelteASTNodes = await extractSvelteASTNodes({ ast });
const { storyComponents } = svelteASTNodes;
const component = storyComponents[0].component;
const rawSource = getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode: code,
});

expect(rawSource).toBe(`<SomeComponent wins="children" {...args} />`);
});
});
});
Loading

0 comments on commit be44123

Please sign in to comment.