Skip to content

Commit

Permalink
refactor: Use prettier to format Story children raw source slices
Browse files Browse the repository at this point in the history
  • Loading branch information
xeho91 committed Jun 6, 2024
1 parent be44123 commit 7881a71
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 51 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"dedent": "^1.2.0",
"estree-util-to-js": "^2.0.0",
"magic-string": "^0.30.1",
"prettier-plugin-svelte": "^3.2.4",
"zimmerframe": "^1.1.2"
},
"devDependencies": {
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 11 additions & 10 deletions src/compiler/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,17 @@ export async function plugin(): Promise<Plugin> {
const svelteStories = [...svelteASTNodes.storyComponents].reverse();
const compiledStories = [...extractedCompiledStoriesNodes].reverse();

for (const [index, compiled] of Object.entries(compiledStories)) {
updateCompiledStoryProps({
code: magicCompiledCode,
componentASTNodes: { svelte: svelteStories[index], compiled },
svelteASTNodes,
filename: id,
originalCode: rawCode,
});
}

await Promise.all(
compiledStories.map((compiled, index) =>
updateCompiledStoryProps({
code: magicCompiledCode,
componentASTNodes: { svelte: svelteStories[index], compiled },
svelteASTNodes,
filename: id,
originalCode: rawCode,
})
)
);
await destructureMetaFromDefineMeta({
code: magicCompiledCode,
nodes: compiledNodes,
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/transform/compiled-story-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface Params {
originalCode: string;
}

export function updateCompiledStoryProps(params: Params) {
export async function updateCompiledStoryProps(params: Params) {
const { code, svelteASTNodes, componentASTNodes, filename, originalCode } = params;
const { svelte, compiled } = componentASTNodes;
const { component, comment } = svelte;
Expand Down Expand Up @@ -74,7 +74,7 @@ export function updateCompiledStoryProps(params: Params) {
if (comment) {
insertDescriptionStory({ comment, currentDocsProperty });
}
insertSourceCode({
await insertSourceCode({
component,
svelteASTNodes,
currentDocsProperty,
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/transform/story-props/insert-source-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface Params {
* to see if the user has explicitly set a `source.code`.
* If he didn't, then it will insert to the existing ObjectExpression.
*/
export function insertSourceCode(params: Params) {
export async function insertSourceCode(params: Params) {
const { component, svelteASTNodes, currentDocsProperty, filename, originalCode } = params;

if (currentDocsProperty.value.type !== 'ObjectExpression') {
Expand Down Expand Up @@ -60,7 +60,7 @@ export function insertSourceCode(params: Params) {
return;
}

const value = getStoryChildrenRawSource({
const value = await getStoryChildrenRawSource({
component,
svelteASTNodes,
originalCode,
Expand Down
67 changes: 30 additions & 37 deletions src/parser/analyse/Story/children.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { Component, SnippetBlock } from 'svelte/compiler';
import { format } from 'prettier';

import { getDefineMetaComponentValue } from '../meta/component-identifier.js';

import type { extractSvelteASTNodes } from '../../extract/svelte/nodes.js';
import { extractStoryAttributesNodes } from '../../extract/svelte/Story/attributes.js';
import { extractStoryChildrenSnippetBlock } from '../../extract/svelte/Story/children.js';
import { extractMetaPropertiesNodes } from '../../extract/meta-properties.js';

interface Params {
component: Component;
Expand All @@ -16,7 +18,7 @@ interface Params {
* Determine the `source.code` of the `<Story />` component children.
* Reference: Step 2 from the comment: https://github.com/storybookjs/addon-svelte-csf/pull/181#issuecomment-2143539873
*/
export function getStoryChildrenRawSource(params: Params): string {
export async function getStoryChildrenRawSource(params: Params): Promise<string> {
const { component, svelteASTNodes, originalCode, filename } = params;

// `<Story />` component is self-closing...
Expand All @@ -40,7 +42,7 @@ export function getStoryChildrenRawSource(params: Params): string {
});

if (storyAttributeChildrenSnippetBlock) {
return getSnippetBlockBodyRawCode(originalCode, storyAttributeChildrenSnippetBlock);
return await getSnippetBlockBodyRawCode(originalCode, storyAttributeChildrenSnippetBlock);
}

/**
Expand All @@ -60,15 +62,23 @@ export function getStoryChildrenRawSource(params: Params): string {
* <Story name="Default" />
* ```
*/
const setTemplateSnippetBlock = findSetTemplateSnippetBlock(svelteASTNodes);
const setTemplateSnippetBlock = findSetTemplateSnippetBlock({
svelteASTNodes,
filename,
});

if (setTemplateSnippetBlock) {
return getSnippetBlockBodyRawCode(originalCode, setTemplateSnippetBlock);
return await getSnippetBlockBodyRawCode(originalCode, setTemplateSnippetBlock);
}

/* Case - No `children` attribute provided, no `setTemplate` used, just a Story */
// TODO: How do we fill ComponentName? Extract from defineMeta? - it can be optional
return `<${getDefineMetaComponentName(svelteASTNodes)} {...args} />`;
const defineMetaComponentValue = getDefineMetaComponentValue({
svelteASTNodes,
filename,
});

// NOTE: It should never be undefined in this particular case, otherwise Storybook wouldn't know what to render.
return `<${defineMetaComponentValue?.name} {...args} />`;
}

/**
Expand All @@ -87,7 +97,7 @@ export function getStoryChildrenRawSource(params: Params): string {
const storyChildrenSnippetBlock = extractStoryChildrenSnippetBlock(component);

if (storyChildrenSnippetBlock) {
return getSnippetBlockBodyRawCode(originalCode, storyChildrenSnippetBlock);
return await getSnippetBlockBodyRawCode(originalCode, storyChildrenSnippetBlock);
}

/**
Expand All @@ -107,7 +117,7 @@ export function getStoryChildrenRawSource(params: Params): string {
const lastNode = nodes[nodes.length - 1];
const rawCode = originalCode.slice(firstNode.start, lastNode.end);

return sanitizeCodeSlice(rawCode);
return prettifyCodeSlice(rawCode);
}

function findTemplateSnippetBlock(
Expand All @@ -120,8 +130,9 @@ function findTemplateSnippetBlock(
}

function findSetTemplateSnippetBlock(
svelteASTNodes: Params['svelteASTNodes']
params: Pick<Params, 'svelteASTNodes' | 'filename'>
): SnippetBlock | undefined {
const { svelteASTNodes, filename } = params;
const { setTemplateCall } = svelteASTNodes;

if (!setTemplateCall) {
Expand Down Expand Up @@ -184,40 +195,22 @@ function findChildrenPropSnippetBlock(
* <Component {...args } />
* ```
*/
function getSnippetBlockBodyRawCode(originalCode: string, node: SnippetBlock) {
async function getSnippetBlockBodyRawCode(originalCode: string, node: SnippetBlock) {
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 sanitizeCodeSlice(rawCode);
return await prettifyCodeSlice(rawCode);
}

/**
* WARN: The current solution was written very quickly. Expect bugs.
* TODO:
* Need to figure a safer way to remove unwanted leading and ending tabs, spaces, new lines and so on.
*/
function sanitizeCodeSlice(rawCode: string): string {
return rawCode.replace(/(\n)/g, '').trim();
}

function getDefineMetaComponentName(svelteASTNodes: Params['svelteASTNodes']) {
const { component } = extractMetaPropertiesNodes({
nodes: svelteASTNodes,
properties: ['component'],
async function prettifyCodeSlice(rawCode: string) {
/**
* FIXME: Perhaps we don't need to prettify the code at this point, and do it at runtime instead?
*/
return await format(rawCode, {
plugins: ['prettier-plugin-svelte'],
parser: 'svelte',
});

if (!component) {
return '!unspecified';
}

const { value } = component;

if (value.type !== 'Identifier') {
throw new Error(`Invalid schema`);
}

return value.name;
}
31 changes: 31 additions & 0 deletions src/parser/analyse/meta/component-identifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Identifier } from 'estree';

import { extractMetaPropertiesNodes } from '../../extract/meta-properties.js';
import type { SvelteASTNodes } from '../../extract/svelte/nodes.js';

interface Params {
svelteASTNodes: SvelteASTNodes;
filename?: string;
}

export function getDefineMetaComponentValue(params: Params): Identifier | undefined {
const { svelteASTNodes, filename } = params;
const { component } = extractMetaPropertiesNodes({
nodes: svelteASTNodes,
properties: ['component'],
});

if (!component) {
return;
}

const { value } = component;

if (value.type !== 'Identifier') {
throw new Error(
`Invalid schema. 'defineMeta's property 'component' value should be an identifier to Svelte's component import specifier. Stories file: ${filename}`
);
}

return value;
}

0 comments on commit 7881a71

Please sign in to comment.