Skip to content

Commit

Permalink
handle id-less templates and stories
Browse files Browse the repository at this point in the history
  • Loading branch information
xeho91 committed Sep 7, 2024
1 parent 3c361d4 commit cfc7beb
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 91 deletions.
138 changes: 100 additions & 38 deletions src/compiler/pre-transform/codemods/legacy-story.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ describe(transformLegacyStory.name, () => {
<Story name="Default" autodocs />
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(
`"<Story name="Default" tags={["autodocs"]} />"`
);
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`"<Story name="Default" tags={["autodocs"]} />"`);
});

it("moving 'autodocs' prop doesn't break with existing 'tags' prop", async ({ expect }) => {
Expand All @@ -30,11 +35,16 @@ describe(transformLegacyStory.name, () => {
<Story name="Default" autodocs tags={["!dev"]} />
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(
`"<Story name="Default" tags={["!dev", "autodocs"]} />"`
);
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`"<Story name="Default" tags={["!dev", "autodocs"]} />"`);
});

it("'source' prop when is a shorthand gets removed", async ({ expect }) => {
Expand All @@ -45,11 +55,16 @@ describe(transformLegacyStory.name, () => {
<Story name="Default" source />
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(
`"<Story name="Default" />"`
);
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`"<Story name="Default" />"`);
});

it("'source' prop when is a text expression gets moved to 'parameters' prop", async ({
Expand All @@ -62,9 +77,16 @@ describe(transformLegacyStory.name, () => {
<Story name="Default" source="'<Button primary />'" />
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(
`
"<Story name="Default" parameters={{
docs: { source: { code: "'<Button primary />'" } }
Expand All @@ -85,9 +107,16 @@ describe(transformLegacyStory.name, () => {
<LegacyStory>{'Hi'}</LegacyStory>
</Story>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(
`
"<Story name="With source as text" parameters={{
docs: {
Expand Down Expand Up @@ -117,9 +146,16 @@ describe(transformLegacyStory.name, () => {
}}
/>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(
`
"<Story name="Default" parameters={{
controls: { disable: true },
Expand All @@ -140,11 +176,16 @@ describe(transformLegacyStory.name, () => {
<Story name="Default" template="someTemplate" />
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(
`"<Story name="Default" children={someTemplate} />"`
);
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`"<Story name="Default" children={someTemplate} />"`);
});

it("when directive 'let:args' is used then it wraps Story fragment with 'children' snippet block", async ({
Expand All @@ -159,9 +200,16 @@ describe(transformLegacyStory.name, () => {
<Button {...args} />
</Story>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(`
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`
"<Story name="Default">
{#snippet children(args)}
<Button {...args} />
Expand All @@ -182,9 +230,16 @@ describe(transformLegacyStory.name, () => {
<p>{context.id}</p>
</Story>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(`
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`
"<Story name="Default">
{#snippet children(_args, context)}
<p>{context.id}</p>
Expand All @@ -206,9 +261,16 @@ describe(transformLegacyStory.name, () => {
<p>{context.id}</p>
</Story>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformLegacyStory({ node }))).toMatchInlineSnapshot(`
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(
print(
transformLegacyStory({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`
"<Story name="Default">
{#snippet children(args, context)}
<h1>{args.title}</h1>
Expand Down
28 changes: 23 additions & 5 deletions src/compiler/pre-transform/codemods/legacy-story.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@ import {
} from '#parser/ast';
import { InvalidTemplateAttribute } from '#utils/error/codemod/index';

import type { State } from '..';

interface Params {
node: SvelteAST.Component;
component: SvelteAST.Component;
filename?: string;
state: State;
}

export function transformLegacyStory(params: Params): SvelteAST.Component {
const { node, filename } = params;
let { attributes, fragment, ...rest } = node;
const { component, filename, state } = params;
let { attributes, fragment, ...rest } = component;
let newAttributes: SvelteAST.Component['attributes'] = [];
let autodocs: SvelteAST.Attribute | undefined;
let source: SvelteAST.Attribute | undefined;
let parameters: SvelteAST.Attribute | undefined;
let tags: SvelteAST.Attribute | undefined;
let letDirectiveArgs: SvelteAST.LetDirective | undefined;
let letDirectiveContext: SvelteAST.LetDirective | undefined;
let hasTemplateAttribute = false;

for (let attribute of attributes) {
if (attribute.type === 'LetDirective' && attribute.name === 'args') {
Expand Down Expand Up @@ -60,11 +64,25 @@ export function transformLegacyStory(params: Params): SvelteAST.Component {

if (attribute.type === 'Attribute' && attribute.name === 'template') {
attribute = templateToChildren(attribute, filename);
hasTemplateAttribute = true;
}

newAttributes.push(attribute);
}

// NOTE: is self-closing AND has no template attribute AND there are existing <Template> components in stories file
if (fragment.nodes.length === 0 && !hasTemplateAttribute && state.templateComponents.length > 0) {
newAttributes.push(
createASTAttribute(
'children',
createASTExpressionTag({
type: 'Identifier',
name: `sb_default_template_${state.templateComponents.length}`,
})
)
);
}

if (autodocs) {
transformAutodocs({
autodocs,
Expand Down Expand Up @@ -233,12 +251,12 @@ function templateToChildren(
};
}

interface TransformTemplateParams {
interface TransformFragmentParams {
letDirectiveArgs?: SvelteAST.LetDirective;
letDirectiveContext?: SvelteAST.LetDirective;
fragment: SvelteAST.Fragment;
}
function transformFragment(params: TransformTemplateParams): SvelteAST.Fragment {
function transformFragment(params: TransformFragmentParams): SvelteAST.Fragment {
let { letDirectiveArgs, letDirectiveContext, fragment } = params;

let parameters: SvelteAST.SnippetBlock['parameters'] = [
Expand Down
49 changes: 35 additions & 14 deletions src/compiler/pre-transform/codemods/template-to-snippet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@ describe(transformTemplateToSnippet.name, () => {
<Button {...args} variant="primary" />
</Template>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformTemplateToSnippet(node))).toMatchInlineSnapshot(`
"{#snippet children(args)}
<Button {...args} variant="primary" />
{/snippet}"
`);
expect(
print(
transformTemplateToSnippet({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`
"{#snippet sb_default_template_0(args)}
<Button {...args} variant="primary" />
{/snippet}"
`);
});

it("covers a case with provided prop 'id'", async ({ expect }) => {
Expand All @@ -37,9 +44,16 @@ describe(transformTemplateToSnippet.name, () => {
<Button {...args} variant="primary" />
</Template>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformTemplateToSnippet(node))).toMatchInlineSnapshot(`
expect(
print(
transformTemplateToSnippet({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`
"{#snippet coolTemplate(args)}
<Button {...args} variant="primary" />
{/snippet}"
Expand All @@ -56,12 +70,19 @@ describe(transformTemplateToSnippet.name, () => {
<p>{context.args}</p>
</Template>
`;
const node = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');

expect(print(transformTemplateToSnippet(node))).toMatchInlineSnapshot(`
"{#snippet children(_args, context)}
<p>{context.args}</p>
{/snippet}"
`);
expect(
print(
transformTemplateToSnippet({
component,
state: { componentIdentifierName: {}, templateComponents: [] },
})
)
).toMatchInlineSnapshot(`
"{#snippet sb_default_template_0(_args, context)}
<p>{context.args}</p>
{/snippet}"
`);
});
});
12 changes: 10 additions & 2 deletions src/compiler/pre-transform/codemods/template-to-snippet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { getStringValueFromAttribute } from '#parser/analyse/story/attributes';
import type { SvelteAST } from '#parser/ast';

import type { State } from '..';

interface Params {
component: SvelteAST.Component;
state: State;
}

/**
*
* Codemod to transform AST node of `<Template>` component to `SnippetBlock`
Expand All @@ -25,7 +32,8 @@ import type { SvelteAST } from '#parser/ast';
* - </Template>
* ```
*/
export function transformTemplateToSnippet(component: SvelteAST.Component): SvelteAST.SnippetBlock {
export function transformTemplateToSnippet(params: Params): SvelteAST.SnippetBlock {
const { component, state } = params;
const { attributes, fragment } = component;

const attributeId = attributes.find((attr) => {
Expand Down Expand Up @@ -65,7 +73,7 @@ export function transformTemplateToSnippet(component: SvelteAST.Component): Svel
type: 'SnippetBlock',
expression: {
type: 'Identifier',
name: id ?? 'children',
name: id ?? `sb_default_template_${state.templateComponents.length}`,
},
parameters,
body: fragment,
Expand Down
Loading

0 comments on commit cfc7beb

Please sign in to comment.