From 9d9a92d78aa37092d39bc12f2e44ffd8ae53ce77 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 8 Nov 2022 19:21:27 -0500 Subject: [PATCH] Use an object instead of tuple for previewAnnotations --- .../builder-vite/src/codegen-iframe-script.ts | 16 +++++----- .../src/codegen-modern-iframe-script.ts | 3 +- .../src/utils/process-preview-annotation.ts | 30 +++++++++++++------ .../src/preview/iframe-webpack.config.ts | 21 +++++++------ code/lib/core-common/src/presets.ts | 12 +++++--- code/lib/types/src/modules/core-common.ts | 4 ++- 6 files changed, 54 insertions(+), 32 deletions(-) diff --git a/code/lib/builder-vite/src/codegen-iframe-script.ts b/code/lib/builder-vite/src/codegen-iframe-script.ts index 6df6d55d22cc..fdfe0249bce5 100644 --- a/code/lib/builder-vite/src/codegen-iframe-script.ts +++ b/code/lib/builder-vite/src/codegen-iframe-script.ts @@ -1,4 +1,5 @@ import { getRendererName, getFrameworkName } from '@storybook/core-common'; +import type { PreviewAnnotation } from '@storybook/types'; import { virtualPreviewFile, virtualStoriesFile } from './virtual-file-names'; import type { ExtendedOptions } from './types'; import { processPreviewAnnotation } from './utils/process-preview-annotation'; @@ -8,16 +9,15 @@ export async function generateIframeScriptCode(options: ExtendedOptions) { const rendererName = await getRendererName(options); const frameworkName = await getFrameworkName(options); - const previewAnnotations = await presets.apply('previewAnnotations', [], options); - const configEntries = [...previewAnnotations].filter(Boolean); + const previewAnnotations = await presets.apply( + 'previewAnnotations', + [], + options + ); + const configEntries = [...previewAnnotations].filter(Boolean).map(processPreviewAnnotation); const filesToImport = (files: string[], name: string) => - files - .map( - (el, i) => - `import ${name ? `* as ${name}_${i} from ` : ''}'${processPreviewAnnotation(el)}'` - ) - .join('\n'); + files.map((el, i) => `import ${name ? `* as ${name}_${i} from ` : ''}'${el}'`).join('\n'); const importArray = (name: string, length: number) => new Array(length).fill(0).map((_, i) => `${name}_${i}`); diff --git a/code/lib/builder-vite/src/codegen-modern-iframe-script.ts b/code/lib/builder-vite/src/codegen-modern-iframe-script.ts index 82ea182f2c2f..c1c7aa6a4444 100644 --- a/code/lib/builder-vite/src/codegen-modern-iframe-script.ts +++ b/code/lib/builder-vite/src/codegen-modern-iframe-script.ts @@ -1,4 +1,5 @@ import { loadPreviewOrConfigFile, getFrameworkName } from '@storybook/core-common'; +import type { PreviewAnnotation } from '@storybook/types'; import { virtualStoriesFile, virtualAddonSetupFile } from './virtual-file-names'; import type { ExtendedOptions } from './types'; import { processPreviewAnnotation } from './utils/process-preview-annotation'; @@ -8,7 +9,7 @@ export async function generateModernIframeScriptCode(options: ExtendedOptions) { const frameworkName = await getFrameworkName(options); const previewOrConfigFile = loadPreviewOrConfigFile({ configDir }); - const previewAnnotations = await presets.apply<(string | string[])[]>( + const previewAnnotations = await presets.apply( 'previewAnnotations', [], options diff --git a/code/lib/builder-vite/src/utils/process-preview-annotation.ts b/code/lib/builder-vite/src/utils/process-preview-annotation.ts index 6d7c51746453..ecf811293ce5 100644 --- a/code/lib/builder-vite/src/utils/process-preview-annotation.ts +++ b/code/lib/builder-vite/src/utils/process-preview-annotation.ts @@ -1,21 +1,33 @@ +import type { PreviewAnnotation } from '@storybook/types'; import { resolve } from 'path'; /** - * Preview annotations can take several forms, and vite needs them to be a bit more restrained. - * For node_modules, we want bare imports (so vite can process them), and for files in the user's source, + * Preview annotations can take several forms, and vite needs them to be + * a bit more restrained. + * + * For node_modules, we want bare imports (so vite can process them), + * and for files in the user's source, * we want absolute paths. */ -export function processPreviewAnnotation(path: string | string[] | undefined) { - // If entry is a tuple, take the first, which is the non-absolute path. - // This is so that webpack can use an absolute path (the second item in the tuple), and - // continue supporting super-addons in pnp/pnpm without requiring them to re-export their - // sub-addons as we do in addon-essentials. - if (Array.isArray(path)) { - return path[0]; +export function processPreviewAnnotation(path: PreviewAnnotation | undefined) { + // If entry is an object, take the first, which is the + // bare (non-absolute) specifier. + // This is so that webpack can use an absolute path, and + // continue supporting super-addons in pnp/pnpm without + // requiring them to re-export their sub-addons as we do + // in addon-essentials. + if (typeof path === 'object') { + return path.bare; } // resolve relative paths into absolute paths, but don't resolve "bare" imports if (path?.startsWith('./') || path?.startsWith('../')) { return resolve(path); } + // This should not occur, since we use `.filter(Boolean)` prior to + // calling this function, but this makes typescript happy + if (!path) { + throw new Error('Could not determine path for previewAnnotation'); + } + return path; } diff --git a/code/lib/builder-webpack5/src/preview/iframe-webpack.config.ts b/code/lib/builder-webpack5/src/preview/iframe-webpack.config.ts index efc2f555e50b..a16e9ba8c91e 100644 --- a/code/lib/builder-webpack5/src/preview/iframe-webpack.config.ts +++ b/code/lib/builder-webpack5/src/preview/iframe-webpack.config.ts @@ -7,7 +7,7 @@ import TerserWebpackPlugin from 'terser-webpack-plugin'; import VirtualModulePlugin from 'webpack-virtual-modules'; import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; -import type { Options, CoreConfig, DocsOptions } from '@storybook/types'; +import type { Options, CoreConfig, DocsOptions, PreviewAnnotation } from '@storybook/types'; import { getRendererName, stringifyProcessEnvs, @@ -82,15 +82,18 @@ export default async ( const docsOptions = await presets.apply('docs'); const previewAnnotations = [ - ...(await presets.apply('previewAnnotations', [], options)).map((entry) => { - // If entry is a tuple, take the second, which is the absolute path. - // This is to maintain back-compat with community addons that bundle other addons. - // The vite builder uses the first element of the tuple, which is the bare import. - if (Array.isArray(entry)) { - return entry[1]; + ...(await presets.apply('previewAnnotations', [], options)).map( + (entry) => { + // If entry is an object, use the absolute import specifier. + // This is to maintain back-compat with community addons that bundle other addons + // and package managers that "hide" sub dependencies (e.g. pnpm / yarn pnp) + // The vite builder uses the bare import specifier. + if (typeof entry === 'object') { + return entry.absolute; + } + return entry; } - return entry; - }), + ), loadPreviewOrConfigFile(options), ].filter(Boolean); const entries = (await presets.apply('entries', [], options)) as string[]; diff --git a/code/lib/core-common/src/presets.ts b/code/lib/core-common/src/presets.ts index 48151ad7d55e..7a3f2ba529ff 100644 --- a/code/lib/core-common/src/presets.ts +++ b/code/lib/core-common/src/presets.ts @@ -86,9 +86,11 @@ export const resolveAddonName = ( return undefined; }; - // This is used to maintain back-compat with community addons that do not re-export their sub-addons but reference - // the sub-addon name directly. We need to turn it into an absolute path so that webpack can serve it up correctly - // when yarn pnp or pnpm is being used. Vite will be broken in such cases, because it does not process absolute paths, + // This is used to maintain back-compat with community addons that do not + // re-export their sub-addons but reference the sub-addon name directly. + // We need to turn it into an absolute path so that webpack can + // serve it up correctly when yarn pnp or pnpm is being used. + // Vite will be broken in such cases, because it does not process absolute paths, // and it will try to import from the bare import, breaking in pnp/pnpm. const absolutizeExport = (exportName: string) => { return resolve(`${name}${exportName}`); @@ -125,7 +127,9 @@ export const resolveAddonName = ( ...(previewFile ? { previewAnnotations: [ - previewFileAbsolute ? [previewFile, previewFileAbsolute] : [previewFile], + previewFileAbsolute + ? { bare: previewFile, absolute: previewFileAbsolute } + : previewFile, ], } : {}), diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts index 381cd337ba95..c18b53afb1a7 100644 --- a/code/lib/types/src/modules/core-common.ts +++ b/code/lib/types/src/modules/core-common.ts @@ -462,11 +462,13 @@ export interface CoreCommon_ResolvedAddonPreset { name: string; } +export type PreviewAnnotation = string | { bare: string; absolute: string }; + export interface CoreCommon_ResolvedAddonVirtual { type: 'virtual'; name: string; managerEntries?: string[]; - previewAnnotations?: (string | string[])[]; + previewAnnotations?: PreviewAnnotation[]; presets?: (string | { name: string; options?: any })[]; }