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

Vue: Enhance Support for vue-component-meta with tsconfig References #26698

Merged
25 changes: 10 additions & 15 deletions code/frameworks/vue3-vite/src/plugins/vue-component-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type MetaSource = {
} & ComponentMeta &
MetaCheckerOptions['schema'];

export async function vueComponentMeta(): Promise<PluginOption> {
export async function vueComponentMeta(tsconfigPath = 'tsconfig.json'): Promise<PluginOption> {
const { createFilter } = await import('vite');

// exclude stories, virtual modules and storybook internals
Expand All @@ -28,7 +28,7 @@ export async function vueComponentMeta(): Promise<PluginOption> {
const include = /\.(vue|ts|js|tsx|jsx)$/;
const filter = createFilter(include, exclude);

const checker = await createCheckerWithWorkaround();
const checker = await createVueComponentMetaChecker(tsconfigPath);

return {
name: 'storybook:vue-component-meta-plugin',
Expand Down Expand Up @@ -126,33 +126,28 @@ export async function vueComponentMeta(): Promise<PluginOption> {
}

/**
* Creates the vue-component-meta checker to use for extracting component meta/docs.
* Includes a workaround for projects using references in their tsconfig.json which
* is currently not supported by vue-component-meta.
* Creates the `vue-component-meta` checker to use for extracting component meta/docs.
* Considers the given tsconfig file (will use a fallback checker if it does not exist or is not supported).
*/
async function createCheckerWithWorkaround() {
async function createVueComponentMetaChecker(tsconfigPath = 'tsconfig.json') {
const checkerOptions: MetaCheckerOptions = {
forceUseTs: true,
noDeclarations: true,
printer: { newLine: 1 },
};

const projectRoot = getProjectRoot();
const projectTsConfigPath = path.join(projectRoot, 'tsconfig.json');
const projectTsConfigPath = path.join(projectRoot, tsconfigPath);

const defaultChecker = createCheckerByJson(projectRoot, { include: ['**/*'] }, checkerOptions);

// prefer the tsconfig.json file of the project to support alias resolution etc.
if (await fileExists(projectTsConfigPath)) {
// tsconfig that uses references is currently not supported by vue-component-meta
// see: https://github.com/vuejs/language-tools/issues/3896
// so we return the no-tsconfig defaultChecker if tsconfig references are found
// remove this workaround once the above issue is fixed
// vue-component-meta does currently not resolve tsconfig references (see https://github.com/vuejs/language-tools/issues/3896)
// so we will return the defaultChecker if references are used.
// Otherwise vue-component-meta might not work at all for the Storybook docgen.
const references = await getTsConfigReferences(projectTsConfigPath);
if (references.length > 0) {
// TODO: paths/aliases are not resolvable, find workaround for this
return defaultChecker;
}
if (references.length > 0) return defaultChecker;
return createChecker(projectTsConfigPath, checkerOptions);
}

Expand Down
19 changes: 15 additions & 4 deletions code/frameworks/vue3-vite/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { dirname, join } from 'path';
import type { PluginOption } from 'vite';
import { vueComponentMeta } from './plugins/vue-component-meta';
import { vueDocgen } from './plugins/vue-docgen';
import type { FrameworkOptions, StorybookConfig } from './types';
import type { FrameworkOptions, StorybookConfig, VueDocgenPlugin } from './types';

const getAbsolutePath = <I extends string>(input: I): I =>
dirname(require.resolve(join(input, 'package.json'))) as any;
Expand All @@ -20,11 +20,11 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, options) =
const frameworkOptions: FrameworkOptions =
typeof framework === 'string' ? {} : framework.options ?? {};

const docgenPlugin = frameworkOptions.docgen ?? 'vue-docgen-api';
const docgen = resolveDocgenOptions(frameworkOptions.docgen);

// add docgen plugin depending on framework option
if (docgenPlugin === 'vue-component-meta') {
plugins.push(await vueComponentMeta());
if (docgen.plugin === 'vue-component-meta') {
plugins.push(await vueComponentMeta(docgen.tsconfig));
} else {
plugins.push(await vueDocgen());
}
Expand All @@ -39,3 +39,14 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, options) =
},
});
};

/**
* Resolves the docgen framework option.
*/
const resolveDocgenOptions = (
docgen?: FrameworkOptions['docgen']
): { plugin: VueDocgenPlugin; tsconfig?: string } => {
if (!docgen) return { plugin: 'vue-docgen-api' };
if (typeof docgen === 'string') return { plugin: docgen };
return docgen;
};
16 changes: 15 additions & 1 deletion code/frameworks/vue3-vite/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,21 @@ export type FrameworkOptions = {
* "vue-component-meta" will become the new default in the future and "vue-docgen-api" will be removed.
* @default "vue-docgen-api"
*/
docgen?: VueDocgenPlugin;
docgen?:
| VueDocgenPlugin
| {
plugin: 'vue-component-meta';
/**
* Tsconfig filename to use. Should be set if your main `tsconfig.json` includes references to other tsconfig files
* like `tsconfig.app.json`.
* Otherwise docgen might not be generated correctly (e.g. import aliases are not resolved).
*
* For further information, see our [docs](https://storybook.js.org/docs/get-started/vue3-vite#limitations).
*
* @default "tsconfig.json"
*/
tsconfig: `tsconfig${string}.json`;
};
};

type StorybookConfigFramework = {
Expand Down
24 changes: 20 additions & 4 deletions docs/get-started/vue3-vite.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,14 +277,30 @@ The definition above will generate the following controls:

### Limitations

`vue-component-meta` cannot currently reference types from an import alias. You will need to replace any aliased imports with relative ones, as in the example below. See [this issue](https://github.com/vuejs/language-tools/issues/3896) for more information.
If your `tsconfig.json` file uses `references` to other config files like `tsconfig.app.json`, `tsconfig.node.json` etc., you should define the tsconfig file used for your main application code via the framework option shown below so `vue-component-meta` can use it properly.
Otherwise, you might face missing component types/descriptions or unresolvable import aliases like `@/some/import`.
larsrickert marked this conversation as resolved.
Show resolved Hide resolved

```ts
// YourComponent.ts
import type { MyProps } from '@/types'; // ❌ Cannot be resolved
import type { MyProps } from '../types'; // ✅ Can be resolved
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/vue3-vite';

const config: StorybookConfig = {
framework: {
name: '@storybook/vue3-vite',
options: {
docgen: {
plugin: 'vue-component-meta',
tsconfig: 'tsconfig.app.json',
},
},
},
};

export default config;
```

For further information, you can take a look at [this issue](https://github.com/vuejs/language-tools/issues/3896).

## Troubleshooting

### Storybook doesn't work with my Vue 2 project
Expand Down
Loading