diff --git a/package.json b/package.json index d4b07f26..537591e6 100755 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dev": "pnpm run --filter=./playground/** dev", "dev:prepare": "pnpm run --recursive --filter=./packages/* --parallel dev:prepare && pnpm run prepare", "dev:build": "pnpm run --filter=./playground/** build", + "playground:storybook:dev": "pnpm run --filter=./playground/** storybook", "playground:storybook:build": "cd playground && pnpm run build-storybook", "playground:storybook:publish": "chromatic --exit-zero-on-changes --build-script-name playground:storybook:build --project-token=chpt_d7cf5e98426e11e", "example:starter:dev": "pnpm run --filter=./examples/starter/** examples/starter dev", diff --git a/packages/storybook-addon/package.json b/packages/storybook-addon/package.json index 51c81851..05693ffb 100644 --- a/packages/storybook-addon/package.json +++ b/packages/storybook-addon/package.json @@ -74,6 +74,7 @@ "@storybook/vue3": "^8.2.7", "@storybook/vue3-vite": "^8.2.7", "json-stable-stringify": "^1.1.1", + "mlly": "^1.7.1", "ofetch": "^1.3.4", "pathe": "^1.1.2", "unctx": "^2.3.1", @@ -88,8 +89,7 @@ "typescript": "5.5.4", "unbuild": "2.0.0", "nuxt": "3.12.4", - "vue": "3.4.35", - "vite": "5.3.5" + "vue": "3.4.35" }, "publishConfig": { "access": "public" diff --git a/packages/storybook-addon/src/preset.ts b/packages/storybook-addon/src/preset.ts index c48a5e5d..37a7fcef 100644 --- a/packages/storybook-addon/src/preset.ts +++ b/packages/storybook-addon/src/preset.ts @@ -1,8 +1,10 @@ -import { dirname, join, resolve } from 'node:path' +import { dirname, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' import { createRequire } from 'node:module' +import { resolve, normalize } from 'pathe' +import { resolvePath } from 'mlly' -import type { PresetProperty } from '@storybook/types' +import type { PresetProperty, PreviewAnnotation } from '@storybook/types' import { type UserConfig as ViteConfig, mergeConfig, @@ -212,14 +214,92 @@ export const core: PresetProperty<'core', StorybookConfig> = async ( renderer: await getPackageDir('@storybook/vue3'), } } + +export interface Resolver { + /** + * Resolves the given path segments to an absolute path, using the provided base path. + * + * The resulting path is normalized, and trailing slashes are removed unless the path gets resolved to the root directory. + * + * @param path A sequence of paths or path segments. + * @throws {TypeError} if any of the arguments is not a string. + */ + resolve(...path: string[]): string + /** + * Asynchronously resolves a module path to a local file path, using the provided base path. + * + * @param id - The identifier or path of the module to resolve. + * @returns A promise to resolve to the file path, or `null` if the module could not be resolved. + */ + resolveModule( + id: string, + options?: { paths?: string[] }, + ): Promise +} +/** + * Creates a resolver that can resolve paths and modules relative to a base path. + * + * @example + * ```js + * const resolver = createResolver(import.meta.url) + * const path = resolver.resolve('preview') + * const modulePath = await resolver.resolveModule('module-name') + * ``` + * + * @param base - The base path to resolve paths and modules relative to. + * @returns A resolver object. + */ +function createResolver(base: string | URL): Resolver { + if (!base) { + throw new Error('`base` argument is missing for createResolver(base)!') + } + + base = base.toString() + if (base.startsWith('file://')) { + base = dirname(fileURLToPath(base)) + } + + return { + resolve: (...path) => resolve(base, ...path), + async resolveModule(id, options) { + const paths = options?.paths ?? [base] + paths.concat([base as string]) + return await resolvePath(id, { url: paths }).catch(() => null) + }, + } +} + /** * This is needed to correctly load the `preview.js` file, * see https://github.com/storybookjs/storybook/blob/main/docs/contribute/framework.md#4-author-the-framework-itself */ -export const previewAnnotations: StorybookConfig['previewAnnotations'] = async ( - entry = [], -) => { - return [...entry, resolve(packageDir, 'preview')] +export const previewAnnotations = async ( + entry: PreviewAnnotation[] = [], +): Promise => { + const resolver = createResolver(import.meta.url) + + // Problem: Storybook does not correctly resolve some modules to an absolute path to the correct deep path in node_modules. + // Solution: + // We need to make sure that they are resolved as dependencies of this package, since they are not installed in the root. + // We need to use bare here otherwise storybook will strip the absolute path, leading to a wrong import + // https://github.com/storybookjs/storybook/blob/3590a1cade2fe24608b3ce0246d5d58692c89883/code/builders/builder-vite/src/utils/process-preview-annotation.ts#L30-L35 + return [ + ...entry.map((entry) => { + // Handle @storybook/vue3 + if (typeof entry === 'string' && entry.includes('vue3')) { + return { + bare: normalize(entry), + absolute: '', + } + } else { + return entry + } + }), + { + bare: resolver.resolve('preview'), + absolute: '', + }, + ] } export const viteFinal: StorybookConfig['viteFinal'] = async ( @@ -263,17 +343,12 @@ export const viteFinal: StorybookConfig['viteFinal'] = async ( return finalViteConfig } -async function getPackageDir(frameworkPackageName: string) { +async function getPackageDir(packageName: string) { try { const require = createRequire(import.meta.url) - const packageDir = dirname( - require.resolve(join(frameworkPackageName, 'package.json'), { - paths: [process.cwd()], - }), - ) - return packageDir + return dirname(require.resolve(join(packageName, 'package.json'))) } catch (e) { - throw new Error(`Cannot find ${frameworkPackageName}`, { cause: e }) + throw new Error(`Cannot find ${packageName}`, { cause: e }) } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1df03f2e..2f6bfc0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -268,6 +268,9 @@ importers: json-stable-stringify: specifier: ^1.1.1 version: 1.1.1 + mlly: + specifier: ^1.7.1 + version: 1.7.1 ofetch: specifier: ^1.3.4 version: 1.3.4 @@ -277,6 +280,9 @@ importers: unctx: specifier: ^2.3.1 version: 2.3.1 + vite: + specifier: ^5.2.0 + version: 5.3.5(@types/node@22.1.0)(sass@1.77.7)(terser@5.31.3) vue-router: specifier: ^4.3.0 version: 4.4.2(vue@3.4.35(typescript@5.5.4)) @@ -305,9 +311,6 @@ importers: unbuild: specifier: 2.0.0 version: 2.0.0(sass@1.77.7)(typescript@5.5.4)(vue-tsc@2.0.29(typescript@5.5.4)) - vite: - specifier: 5.3.5 - version: 5.3.5(@types/node@22.1.0)(sass@1.77.7)(terser@5.31.3) vue: specifier: 3.4.35 version: 3.4.35(typescript@5.5.4)