Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Svelte: Fix docs generating when using lang="ts" or optional chaining #24096

Merged
merged 12 commits into from
Oct 13, 2023
29 changes: 29 additions & 0 deletions code/e2e-tests/framework-svelte.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* 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('Story have a documentation', async ({ page }) => {
const sbPage = new SbPage(page);

await sbPage.navigateToStory('stories/renderers/svelte/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
58 changes: 47 additions & 11 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 @@ -44,10 +61,26 @@ function getNameFromFilename(filename: string) {

export function svelteDocgen(svelteOptions: Record<string, any> = {}): PluginOption {
const cwd = process.cwd();
const { preprocess: preprocessOptions, logDocgen = false } = svelteOptions;
const { preprocess: preprocessOptions, docPreprocess, logDocgen = false } = svelteOptions;
const include = /\.(svelte)$/;
const filter = createFilter(include);

let docPreprocessOptions = docPreprocess;
if (!docPreprocessOptions && preprocessOptions) {
/*
* We can't use vitePreprocess() for the documentation.
* This preprocessor uses esbuild which removes jsdoc.
JReinhold marked this conversation as resolved.
Show resolved Hide resolved
*
* By default, only typescript is transpiled, and style tags are removed.
* This can be configured with the `docPreprocess` options.
*
* Note: theses preprocessors are only used to make the component
JReinhold marked this conversation as resolved.
Show resolved Hide resolved
* compatible to sveltedoc-parser (no ts), not to compile
* the component.
*/
docPreprocessOptions = [typescript(), replace([[/<style.+<\/style>/gims, '']])];
}

return {
name: 'storybook:svelte-docgen-plugin',
async transform(src: string, id: string) {
Expand All @@ -56,11 +89,11 @@ export function svelteDocgen(svelteOptions: Record<string, any> = {}): PluginOpt
const resource = path.relative(cwd, id);

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

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

Expand All @@ -79,21 +112,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
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
11 changes: 11 additions & 0 deletions code/renderers/svelte/template/stories/docs.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ButtonView from './views/ButtonView.svelte';

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

export const Primary = {};
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script>
<script lang="ts">
/**
* @component Button View
* @wrapper
*/
import { global as globalThis } from '@storybook/global';
const Button = globalThis.Components.Button;
const Button = globalThis.Components?.Button;

/**
* Rounds the button
Expand All @@ -22,7 +22,7 @@
*/
export let text = 'You clicked';

function handleClick(_event) {
function handleClick(_event: MouseEvent) {
count += 1;
}
</script>
Expand Down
1 change: 1 addition & 0 deletions code/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8050,6 +8050,7 @@ __metadata:
"@types/node": ^16.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