@storybook/addon-svelte-csf
Version:
Allows to write stories in Svelte syntax
161 lines (160 loc) • 5.35 kB
JavaScript
import dedent from 'dedent';
import { getDefineMetaComponentValue } from '../define-meta/component-identifier.js';
import { extractStoryTemplateSnippetBlock } from '../../extract/svelte/story/template.js';
import { findMetaRenderSnippetBlock, findStoryAttributeTemplateSnippetBlock, } from '../../extract/svelte/snippet-block.js';
import { extractStoryAttributesNodes } from '../../extract/svelte/story/attributes.js';
/**
* Extract the source code of the `<Story />` 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 getStoryContentRawCode(params) {
const { nodes, originalCode, filename } = params;
const { component, svelte } = nodes;
// `<Story />` component is self-closing...
if (component.fragment.nodes.length === 0) {
/**
* Case - "explicit template" - `template` attribute references to a snippet block at the root level of fragment.
*
* Example:
*
* ```svelte
* {#snippet template1(args)}
* <SomeComponent {...args} />
* {/snippet}
*
* <Story name="Default" template={template1} />
* ```
*/
const storyAttributTemplateSnippetBlock = findStoryAttributeTemplateSnippetBlock({
component,
nodes: svelte,
filename,
});
if (storyAttributTemplateSnippetBlock) {
return getSnippetBlockBodyRawCode(originalCode, storyAttributTemplateSnippetBlock);
}
/**
* Case - `render` was set in `defineMeta`
*
* Example:
*
* ```svelte
* <script module>
* import { defineMeta } from "@storybook/addon-svelte-csf";
*
* const { Story } = defineMeta({
* render: myCustomTemplate,
* });
* </script>
*
* {#snippet myCustomTemplate(args)}
* <SomeComponent {...args} />
* {/snippet}
*
* <Story name="Default" />
* ```
*/
const metaRenderSnippetBlock = findMetaRenderSnippetBlock({
nodes: svelte,
filename,
});
if (metaRenderSnippetBlock) {
return getSnippetBlockBodyRawCode(originalCode, metaRenderSnippetBlock);
}
/* Case - No `children` attribute provided, no `render` used, just a Story */
const defineMetaComponentValue = getDefineMetaComponentValue({
nodes: svelte,
filename,
});
// NOTE: It should never be `undefined` in this particular case, otherwise Storybook wouldn't know what to render.
return `<${defineMetaComponentValue?.name} {...args} />`;
}
/**
* Case - Story with children - and with a snippet block `template` inside
*
* Example:
*
* ```svelte
* <Story name="Default">
* {#snippet template(args)}
* <SomeComponent {...args} />
* {/snippet}
* </Story>
* ```
*/
const storyChildrenSnippetBlock = extractStoryTemplateSnippetBlock(component);
if (storyChildrenSnippetBlock) {
return getSnippetBlockBodyRawCode(originalCode, storyChildrenSnippetBlock);
}
/**
* Case - Inner children used directly with `asChild` attribute
*
* Example:
*
* ```svelte
* <Story name="Default" asChild>
* <SomeComponent foo="bar" />
* </Story>
* ```
*/
const { asChild } = extractStoryAttributesNodes({
component,
attributes: ['asChild'],
});
const { fragment } = component;
const firstNode = fragment.nodes[0];
const lastNode = fragment.nodes[fragment.nodes.length - 1];
const rawCode = dedent(originalCode.slice(firstNode.start, lastNode.end));
if (asChild) {
return rawCode;
}
/**
* Case - `children` provided as prop to component or template
*
* Example:
*
* ```svelte
* <Story name="Default">
* <SomeComponent foo="bar" />
* </Story>
* ```
*/
const defineMetaComponentValue = getDefineMetaComponentValue({
nodes: svelte,
filename,
});
// NOTE: It should never be `undefined` in this particular case, otherwise Storybook wouldn't know what to render.
return dedent(`<${defineMetaComponentValue?.name} {...args}>
${rawCode}
</${defineMetaComponentValue?.name}>`);
}
/**
* Extract from the original code a string slice with the body of the svelte's snippet block.
* Starting from the start of the first node, and ending with the end of the last node.
*
* For example, from the following case:
*
* ```svelte
* {#snippet template(args)}
* <!-- Some comment... -->
* "Static text"
* <Component {...args } />
* {/snippet}
* ```
*
* The result would be:
*
* ```txt
* <!-- Some comment... -->
* "Static text"
* <Component {...args } />
* ```
*/
function getSnippetBlockBodyRawCode(originalCode, node) {
const { body } = node;
const { nodes } = body;
const firstNode = nodes[0];
const lastNode = nodes[nodes.length - 1];
const rawCode = originalCode.slice(firstNode.start, lastNode.end);
return dedent(rawCode);
}