Skip to content

Commit

Permalink
Merge pull request #24096 from j3rem1e/bug/24008
Browse files Browse the repository at this point in the history
Svelte: Fix docs generating when using `lang="ts"` or optional chaining
  • Loading branch information
yannbf authored Oct 13, 2023
2 parents 048272f + 3a77b6c commit 995e064
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 28 deletions.
40 changes: 40 additions & 0 deletions code/e2e-tests/framework-svelte.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-disable jest/no-disabled-tests */
import { test, expect } from '@playwright/test';
import process from 'process';
import { SbPage } from './util';

const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:6006';
const templateName = process.env.STORYBOOK_TEMPLATE_NAME;

test.describe('Svelte', () => {
test.skip(
// eslint-disable-next-line jest/valid-title
!templateName?.includes('svelte'),
'Only run this test on Svelte'
);

test.beforeEach(async ({ page }) => {
await page.goto(storybookUrl);
await new SbPage(page).waitUntilLoaded();
});

test('JS story has auto-generated args table', async ({ page }) => {
const sbPage = new SbPage(page);

await sbPage.navigateToStory('stories/renderers/svelte/js-docs', 'docs');
const root = sbPage.previewRoot();
const argsTable = root.locator('.docblock-argstable');
await expect(argsTable).toContainText('Rounds the button');
});

test('TS story has auto-generated args table', async ({ page }) => {
// eslint-disable-next-line jest/valid-title
test.skip(!templateName?.endsWith('ts') || false, 'Only test TS story in TS templates');
const sbPage = new SbPage(page);

await sbPage.navigateToStory('stories/renderers/svelte/ts-docs', 'docs');
const root = sbPage.previewRoot();
const argsTable = root.locator('.docblock-argstable');
await expect(argsTable).toContainText('Rounds the button');
});
});
1 change: 1 addition & 0 deletions code/frameworks/svelte-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@storybook/svelte": "workspace:*",
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"magic-string": "^0.30.0",
"svelte-preprocess": "^5.0.4",
"sveltedoc-parser": "^4.2.1",
"ts-dedent": "^2.2.0"
},
Expand Down
68 changes: 56 additions & 12 deletions code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ import type { SvelteParserOptions } from 'sveltedoc-parser';
import { logger } from '@storybook/node-logger';
import { preprocess } from 'svelte/compiler';
import { createFilter } from 'vite';
import { replace, typescript } from 'svelte-preprocess';

/*
* Patch sveltedoc-parser internal options.
* Waiting for a fix for https://github.com/alexprey/sveltedoc-parser/issues/87
*/
const svelteDocParserOptions = require('sveltedoc-parser/lib/options.js');

svelteDocParserOptions.getAstDefaultOptions = () => ({
range: true,
loc: true,
comment: true,
tokens: true,
ecmaVersion: 12,
sourceType: 'module',
ecmaFeatures: {},
});

// Most of the code here should probably be exported by @storybook/svelte and reused here.
// See: https://github.com/storybookjs/storybook/blob/next/app/svelte/src/server/svelte-docgen-loader.ts
Expand Down Expand Up @@ -48,19 +65,43 @@ export function svelteDocgen(svelteOptions: Record<string, any> = {}): PluginOpt
const include = /\.(svelte)$/;
const filter = createFilter(include);

let docPreprocessOptions: Parameters<typeof preprocess>[1] | undefined;

return {
name: 'storybook:svelte-docgen-plugin',
async transform(src: string, id: string) {
if (!filter(id)) return undefined;

if (preprocessOptions && !docPreprocessOptions) {
/*
* We can't use vitePreprocess() for the documentation
* because it uses esbuild which removes jsdoc.
*
* By default, only typescript is transpiled, and style tags are removed.
*
* Note: these preprocessors are only used to make the component
* compatible to sveltedoc-parser (no ts), not to compile
* the component.
*/
docPreprocessOptions = [replace([[/<style.+<\/style>/gims, '']])];

try {
const ts = require.resolve('typescript');
if (ts) {
docPreprocessOptions.unshift(typescript());
}
} catch {
// this will error in JavaScript-only projects, this is okay
}
}

const resource = path.relative(cwd, id);

let docOptions;
if (preprocessOptions) {
// eslint-disable-next-line @typescript-eslint/no-shadow
const src = fs.readFileSync(resource).toString();
if (docPreprocessOptions) {
const rawSource = fs.readFileSync(resource).toString();

const { code: fileContent } = await preprocess(src, preprocessOptions, {
const { code: fileContent } = await preprocess(rawSource, docPreprocessOptions, {
filename: resource,
});

Expand All @@ -79,21 +120,24 @@ export function svelteDocgen(svelteOptions: Record<string, any> = {}): PluginOpt

const s = new MagicString(src);

let componentDoc: any;
try {
const componentDoc = await svelteDoc.parse(options);
// get filename for source content
const file = path.basename(resource);

componentDoc.name = path.basename(file);

const componentName = getNameFromFilename(resource);
s.append(`;${componentName}.__docgen = ${JSON.stringify(componentDoc)}`);
componentDoc = await svelteDoc.parse(options);
} catch (error: any) {
componentDoc = { keywords: [], data: [] };
if (logDocgen) {
logger.error(error);
}
}

// get filename for source content
const file = path.basename(resource);

componentDoc.name = path.basename(file);

const componentName = getNameFromFilename(resource);
s.append(`;${componentName}.__docgen = ${JSON.stringify(componentDoc)}`);

return {
code: s.toString(),
map: s.generateMap({ hires: true, source: id }),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts">
/**
* @component Button TypeScript
* @wrapper
*/
import { global as globalThis } from '@storybook/global';
// @ts-ignore
const Button = globalThis.Components?.Button;
/**
* Rounds the button
*/
export let primary: boolean = false;
/**
* Displays the count
*/
export let count: number = 0;
/**
* Button text
* @slot
*/
export let text: string = 'You clicked';
function handleClick(_event: MouseEvent) {
count += 1;
}
</script>

<h1>Button TypeScript</h1>

<Button {primary} on:click on:click={handleClick} label="{text}: {count}" />

<p>A little text to show this is a view.</p>
<p>If we need to test components in a Svelte environment, for instance to test slot behaviour,</p>
<p>then wrapping the component up in a view</p>
<p>made just for the story is the simplest way to achieve this.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ButtonTypescript from './ButtonTypeScript.svelte';

export default {
title: 'stories/renderers/svelte/ts-docs',
component: ButtonTypescript,
args: {
primary: true,
},
tags: ['autodocs'],
};

export const Primary = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts">
/**
* @component Button TypeScript
* @wrapper
*/
import { global as globalThis } from '@storybook/global';
// @ts-ignore
const Button = globalThis.Components?.Button;
/**
* Rounds the button
*/
export let primary: boolean = false;
/**
* Displays the count
*/
export let count: number = 0;
/**
* Button text
* @slot
*/
export let text: string = 'You clicked';
function handleClick(_event: MouseEvent) {
count += 1;
}
</script>

<h1>Button TypeScript</h1>

<Button {primary} on:click on:click={handleClick} label="{text}: {count}" />

<p>A little text to show this is a view.</p>
<p>If we need to test components in a Svelte environment, for instance to test slot behaviour,</p>
<p>then wrapping the component up in a view</p>
<p>made just for the story is the simplest way to achieve this.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ButtonTypescript from './ButtonTypeScript.svelte';

export default {
title: 'stories/renderers/svelte/ts-docs',
component: ButtonTypescript,
args: {
primary: true,
},
tags: ['autodocs'],
};

export const Primary = {};
43 changes: 31 additions & 12 deletions code/presets/svelte-webpack/src/svelte-docgen-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ import * as fs from 'fs';
import { preprocess } from 'svelte/compiler';
import { logger } from '@storybook/node-logger';

/*
* Patch sveltedoc-parser internal options.
* Waiting for a fix for https://github.com/alexprey/sveltedoc-parser/issues/87
*/
const svelteDocParserOptions = require('sveltedoc-parser/lib/options.js');

svelteDocParserOptions.getAstDefaultOptions = () => ({
range: true,
loc: true,
comment: true,
tokens: true,
ecmaVersion: 12,
sourceType: 'module',
ecmaFeatures: {},
});

// From https://github.com/sveltejs/svelte/blob/8db3e8d0297e052556f0b6dde310ef6e197b8d18/src/compiler/compile/utils/get_name_from_filename.ts
// Copied because it is not exported from the compiler
function getNameFromFilename(filename: string) {
Expand Down Expand Up @@ -73,27 +89,30 @@ export default async function svelteDocgen(this: any, source: string) {

let docgen = '';

let componentDoc: any;
try {
// FIXME
// @ts-expect-error (Converted from ts-ignore)
const componentDoc = await svelteDoc.parse(options);
componentDoc = await svelteDoc.parse(options);
} catch (error) {
componentDoc = { keywords: [], data: [] };
if (logDocgen) {
logger.error(error as any);
}
}

// get filename for source content
const file = path.basename(resource);
// get filename for source content
const file = path.basename(resource);

// populate filename in docgen
componentDoc.name = path.basename(file);
// populate filename in docgen
componentDoc.name = path.basename(file);

const componentName = getNameFromFilename(resource);
const componentName = getNameFromFilename(resource);

docgen = dedent`
docgen = dedent`
${componentName}.__docgen = ${JSON.stringify(componentDoc)};
`;
} catch (error) {
if (logDocgen) {
logger.error(error as any);
}
}

// inject __docgen prop in svelte component
const output = source + docgen;

Expand Down
2 changes: 1 addition & 1 deletion code/renderers/svelte/template/stories/args.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
STORY_RENDERED,
} from '@storybook/core-events';
import { addons } from '@storybook/preview-api';
import ButtonView from './views/ButtonView.svelte';
import ButtonView from './views/ButtonJavaScript.svelte';

export default {
component: ButtonView,
Expand Down
11 changes: 11 additions & 0 deletions code/renderers/svelte/template/stories/js-docs.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ButtonJavaScript from './views/ButtonJavaScript.svelte';

export default {
component: ButtonJavaScript,
args: {
primary: true,
},
tags: ['autodocs'],
};

export const Primary = {};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ButtonView from './views/ButtonView.svelte';
import ButtonView from './views/ButtonJavaScript.svelte';
import BorderDecoratorRed from './views/BorderDecoratorRed.svelte';
import BorderDecoratorBlue from './views/BorderDecoratorBlue.svelte';
import BorderDecoratorProps from './views/BorderDecoratorProps.svelte';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { global as globalThis } from '@storybook/global';
import { Meta, Story, Canvas } from '@storybook/addon-docs';
import ButtonView from './views/ButtonView.svelte';
import ButtonView from './views/ButtonJavaScript.svelte';
import BorderDecoratorRed from './views/BorderDecoratorRed.svelte';

<Meta title="stories/renderers/svelte/svelte-mdx" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* @wrapper
*/
import { global as globalThis } from '@storybook/global';
const Button = globalThis.Components.Button;
// @ts-ignore
const Button = globalThis.Components?.Button;
/**
* Rounds the button
Expand Down
1 change: 1 addition & 0 deletions code/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8061,6 +8061,7 @@ __metadata:
"@types/node": ^18.0.0
magic-string: ^0.30.0
svelte: ^4.0.0
svelte-preprocess: ^5.0.4
sveltedoc-parser: ^4.2.1
ts-dedent: ^2.2.0
typescript: ~4.9.3
Expand Down

0 comments on commit 995e064

Please sign in to comment.