Merge latest-release into version-non-patch-from-8.1.0-beta.1 with conflicts resolved to ours [skip ci]

This commit is contained in:
storybook-bot 2024-05-14 08:18:48 +00:00
commit 254bd81b4d
11 changed files with 167 additions and 33 deletions

View File

@ -5,8 +5,8 @@ import path from 'path';
import type { PluginOption } from 'vite'; import type { PluginOption } from 'vite';
import { import {
TypeMeta, TypeMeta,
createComponentMetaChecker, createChecker,
createComponentMetaCheckerByJsonConfig, createCheckerByJson,
type ComponentMeta, type ComponentMeta,
type MetaCheckerOptions, type MetaCheckerOptions,
} from 'vue-component-meta'; } from 'vue-component-meta';
@ -19,7 +19,7 @@ type MetaSource = {
} & ComponentMeta & } & ComponentMeta &
MetaCheckerOptions['schema']; MetaCheckerOptions['schema'];
export async function vueComponentMeta(): Promise<PluginOption> { export async function vueComponentMeta(tsconfigPath = 'tsconfig.json'): Promise<PluginOption> {
const { createFilter } = await import('vite'); const { createFilter } = await import('vite');
// exclude stories, virtual modules and storybook internals // exclude stories, virtual modules and storybook internals
@ -28,7 +28,7 @@ export async function vueComponentMeta(): Promise<PluginOption> {
const include = /\.(vue|ts|js|tsx|jsx)$/; const include = /\.(vue|ts|js|tsx|jsx)$/;
const filter = createFilter(include, exclude); const filter = createFilter(include, exclude);
const checker = await createChecker(); const checker = await createVueComponentMetaChecker(tsconfigPath);
return { return {
name: 'storybook:vue-component-meta-plugin', name: 'storybook:vue-component-meta-plugin',
@ -126,9 +126,10 @@ export async function vueComponentMeta(): Promise<PluginOption> {
} }
/** /**
* Creates the vue-component-meta checker to use for extracting component meta/docs. * 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 createChecker() { async function createVueComponentMetaChecker(tsconfigPath = 'tsconfig.json') {
const checkerOptions: MetaCheckerOptions = { const checkerOptions: MetaCheckerOptions = {
forceUseTs: true, forceUseTs: true,
noDeclarations: true, noDeclarations: true,
@ -136,26 +137,18 @@ async function createChecker() {
}; };
const projectRoot = getProjectRoot(); const projectRoot = getProjectRoot();
const projectTsConfigPath = path.join(projectRoot, 'tsconfig.json'); const projectTsConfigPath = path.join(projectRoot, tsconfigPath);
const defaultChecker = createComponentMetaCheckerByJsonConfig( const defaultChecker = createCheckerByJson(projectRoot, { include: ['**/*'] }, checkerOptions);
projectRoot,
{ include: ['**/*'] },
checkerOptions
);
// prefer the tsconfig.json file of the project to support alias resolution etc. // prefer the tsconfig.json file of the project to support alias resolution etc.
if (await fileExists(projectTsConfigPath)) { if (await fileExists(projectTsConfigPath)) {
// tsconfig that uses references is currently not supported by vue-component-meta // vue-component-meta does currently not resolve tsconfig references (see https://github.com/vuejs/language-tools/issues/3896)
// see: https://github.com/vuejs/language-tools/issues/3896 // so we will return the defaultChecker if references are used.
// so we return the no-tsconfig defaultChecker if tsconfig references are found // Otherwise vue-component-meta might not work at all for the Storybook docgen.
// remove this workaround once the above issue is fixed
const references = await getTsConfigReferences(projectTsConfigPath); const references = await getTsConfigReferences(projectTsConfigPath);
if (references.length > 0) { if (references.length > 0) return defaultChecker;
// TODO: paths/aliases are not resolvable, find workaround for this return createChecker(projectTsConfigPath, checkerOptions);
return defaultChecker;
}
return createComponentMetaChecker(projectTsConfigPath, checkerOptions);
} }
return defaultChecker; return defaultChecker;

View File

@ -3,7 +3,7 @@ import { dirname, join } from 'path';
import type { PluginOption } from 'vite'; import type { PluginOption } from 'vite';
import { vueComponentMeta } from './plugins/vue-component-meta'; import { vueComponentMeta } from './plugins/vue-component-meta';
import { vueDocgen } from './plugins/vue-docgen'; 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 => const getAbsolutePath = <I extends string>(input: I): I =>
dirname(require.resolve(join(input, 'package.json'))) as any; dirname(require.resolve(join(input, 'package.json'))) as any;
@ -20,11 +20,11 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, options) =
const frameworkOptions: FrameworkOptions = const frameworkOptions: FrameworkOptions =
typeof framework === 'string' ? {} : framework.options ?? {}; typeof framework === 'string' ? {} : framework.options ?? {};
const docgenPlugin = frameworkOptions.docgen ?? 'vue-docgen-api'; const docgen = resolveDocgenOptions(frameworkOptions.docgen);
// add docgen plugin depending on framework option // add docgen plugin depending on framework option
if (docgenPlugin === 'vue-component-meta') { if (docgen.plugin === 'vue-component-meta') {
plugins.push(await vueComponentMeta()); plugins.push(await vueComponentMeta(docgen.tsconfig));
} else { } else {
plugins.push(await vueDocgen()); plugins.push(await vueDocgen());
} }
@ -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;
};

View File

@ -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. * "vue-component-meta" will become the new default in the future and "vue-docgen-api" will be removed.
* @default "vue-docgen-api" * @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#override-the-default-configuration).
*
* @default "tsconfig.json"
*/
tsconfig: `tsconfig${string}.json`;
};
}; };
type StorybookConfigFramework = { type StorybookConfigFramework = {

View File

@ -20,6 +20,7 @@ import {
getStorybookInfo, getStorybookInfo,
loadMainConfig, loadMainConfig,
JsPackageManagerFactory, JsPackageManagerFactory,
getCoercedStorybookVersion,
} from '@storybook/core-common'; } from '@storybook/core-common';
import { automigrate } from './automigrate/index'; import { automigrate } from './automigrate/index';
import { autoblock } from './autoblock/index'; import { autoblock } from './autoblock/index';
@ -146,10 +147,11 @@ export const doUpgrade = async ({
throw new UpgradeStorybookToSameVersionError({ beforeVersion }); throw new UpgradeStorybookToSameVersionError({ beforeVersion });
} }
const [latestVersion, packageJson] = await Promise.all([ const [latestVersion, packageJson, storybookVersion] = await Promise.all([
// //
packageManager.latestVersion('@storybook/cli'), packageManager.latestVersion('@storybook/cli'),
packageManager.retrievePackageJson(), packageManager.retrievePackageJson(),
getCoercedStorybookVersion(packageManager),
]); ]);
const isOutdated = lt(currentVersion, latestVersion); const isOutdated = lt(currentVersion, latestVersion);
@ -193,7 +195,7 @@ export const doUpgrade = async ({
const mainConfig = await loadMainConfig({ configDir }); const mainConfig = await loadMainConfig({ configDir });
// GUARDS // GUARDS
if (!beforeVersion) { if (!storybookVersion) {
throw new UpgradeStorybookUnknownCurrentVersionError(); throw new UpgradeStorybookUnknownCurrentVersionError();
} }

View File

@ -218,6 +218,34 @@ describe('composeConfigs', () => {
}); });
}); });
it('allows single array to be written without array', () => {
expect(
composeConfigs([
{
argsEnhancers: ['1', '2'],
argTypesEnhancers: ['1', '2'],
loaders: '1',
},
{
argsEnhancers: '3',
argTypesEnhancers: '3',
loaders: ['2', '3'],
},
])
).toEqual({
parameters: {},
decorators: [],
args: {},
argsEnhancers: ['1', '2', '3'],
argTypes: {},
argTypesEnhancers: ['1', '2', '3'],
globals: {},
globalTypes: {},
loaders: ['1', '2', '3'],
runStep: expect.any(Function),
});
});
it('combines decorators in reverse file order', () => { it('combines decorators in reverse file order', () => {
expect( expect(
composeConfigs([ composeConfigs([

View File

@ -0,0 +1,16 @@
import type { Meta, StoryObj } from '@storybook/vue3';
import Component from './define-model/component.vue';
const meta = {
component: Component,
tags: ['autodocs'],
} satisfies Meta<typeof Component>;
type Story = StoryObj<typeof meta>;
export default meta;
export const Default: Story = {
args: {
modelValue: 'Test value',
},
};

View File

@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@storybook/vue3';
import Component from './define-slots/component.vue';
const meta = {
component: Component,
tags: ['autodocs'],
} satisfies Meta<typeof Component>;
type Story = StoryObj<typeof meta>;
export default meta;
export const Default: Story = {
args: {
default: ({ num }) => `Default slot { num=${num} }`,
named: ({ str }) => `Named slot { str=${str} }`,
vbind: ({ num, str }) => `Named v-bind slot { num=${num}, str=${str} }`,
},
};

View File

@ -0,0 +1,7 @@
<script setup lang="ts">
const model = defineModel<string>();
</script>
<template>
{{ model }}
</template>

View File

@ -0,0 +1,22 @@
<script setup lang="ts">
defineSlots<{
/** Some description for "no-bind" slot. */
'no-bind'(): any;
/** Some description for "default" slot. */
default(props: { num: number }): any;
/** Some description for "named" slot. */
named(props: { str: string }): any;
/** Some description for "vbind" slot. */
vbind(props: { num: number; str: string }): any;
}>();
</script>
<template>
<slot name="no-bind"></slot>
<br />
<slot :num="123"></slot>
<br />
<slot name="named" str="str"></slot>
<br />
<slot name="vbind" v-bind="{ num: 123, str: 'str' }"></slot>
</template>

View File

@ -275,16 +275,37 @@ The definition above will generate the following controls:
![Controls generated from exposed properties and methods](./vue-component-meta-exposed-types-controls.png) ![Controls generated from exposed properties and methods](./vue-component-meta-exposed-types-controls.png)
### Limitations ### Override the default configuration
`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 you're working with a project that relies on [`tsconfig references`](https://www.typescriptlang.org/docs/handbook/project-references.html) to link to other existing configuration files (e.g. `tsconfig.app.json`, `tsconfig.node.json`), we recommend that you update your [`.storybook/main.js|ts`](../configure/index.md) configuration file and add the following:
```ts ```ts
// YourComponent.ts // .storybook/main.ts
import type { MyProps } from '@/types'; // ❌ Cannot be resolved import type { StorybookConfig } from '@storybook/vue3-vite';
import type { MyProps } from '../types'; // ✅ Can be resolved
const config: StorybookConfig = {
framework: {
name: '@storybook/vue3-vite',
options: {
docgen: {
plugin: 'vue-component-meta',
tsconfig: 'tsconfig.app.json',
},
},
},
};
export default config;
``` ```
<Callout variant="info">
This is not a limitation of Storybook, but instead how `vue-component-meta` works. For more information, refer to the appropriate [GitHub issue](https://github.com/vuejs/language-tools/issues/3896).
</Callout>
Otherwise, you might face missing component types/descriptions or unresolvable import aliases like `@/some/import`.
## Troubleshooting ## Troubleshooting
### Storybook doesn't work with my Vue 2 project ### Storybook doesn't work with my Vue 2 project

View File

@ -126,8 +126,10 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
platform: 'neutral', platform: 'neutral',
external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages], external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages],
esbuildOptions: (options) => { esbuildOptions: (options) => {
/* eslint-disable no-param-reassign */
options.platform = 'neutral'; options.platform = 'neutral';
Object.assign(options, getESBuildOptions(optimized)); Object.assign(options, getESBuildOptions(optimized));
/* eslint-enable no-param-reassign */
}, },
}) })
); );