From 263ed62fb4034676535cc521755c679adeaf260e Mon Sep 17 00:00:00 2001 From: Ronald Rey Date: Tue, 2 Jan 2024 09:59:34 -0400 Subject: [PATCH 01/16] fix: Check optionalDependencies for storybook versions --- code/lib/cli/src/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index 33c7b6c6ccd9..4bd17d055e35 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -295,7 +295,7 @@ export async function adjustTemplate(templatePath: string, templateData: Record< // Given a package.json, finds any official storybook package within it // and if it exists, returns the version of that package from the specified package.json export function getStorybookVersionSpecifier(packageJson: PackageJsonWithDepsAndDevDeps) { - const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies }; + const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies, ...packageJson.optionalDependencies }; const storybookPackage = Object.keys(allDeps).find((name: string) => { return storybookMonorepoPackages[name as keyof typeof storybookMonorepoPackages]; }); From 6e8f823ff097dc139be772446d3a7048b9fc46de Mon Sep 17 00:00:00 2001 From: Ronald Rey Date: Wed, 3 Jan 2024 10:01:25 -0400 Subject: [PATCH 02/16] Formatting --- code/lib/cli/src/helpers.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index 4bd17d055e35..9d0570922867 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -295,7 +295,11 @@ export async function adjustTemplate(templatePath: string, templateData: Record< // Given a package.json, finds any official storybook package within it // and if it exists, returns the version of that package from the specified package.json export function getStorybookVersionSpecifier(packageJson: PackageJsonWithDepsAndDevDeps) { - const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies, ...packageJson.optionalDependencies }; + const allDeps = { + ...packageJson.dependencies, + ...packageJson.devDependencies, + ...packageJson.optionalDependencies + }; const storybookPackage = Object.keys(allDeps).find((name: string) => { return storybookMonorepoPackages[name as keyof typeof storybookMonorepoPackages]; }); From b7d72d59297287e37969fdd424b3d69d47183697 Mon Sep 17 00:00:00 2001 From: Ronald Rey Date: Wed, 3 Jan 2024 10:21:06 -0400 Subject: [PATCH 03/16] Update helpers.ts --- code/lib/cli/src/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index 9d0570922867..ed54bd765986 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -295,10 +295,10 @@ export async function adjustTemplate(templatePath: string, templateData: Record< // Given a package.json, finds any official storybook package within it // and if it exists, returns the version of that package from the specified package.json export function getStorybookVersionSpecifier(packageJson: PackageJsonWithDepsAndDevDeps) { - const allDeps = { + const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies, - ...packageJson.optionalDependencies + ...packageJson.optionalDependencies, }; const storybookPackage = Object.keys(allDeps).find((name: string) => { return storybookMonorepoPackages[name as keyof typeof storybookMonorepoPackages]; From 8206b392e9d2fe088c60654f21e42da26b42a656 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Fri, 5 Jan 2024 19:31:06 -0300 Subject: [PATCH 04/16] Doc blocks: Remove deprecated props from Primary block --- MIGRATION.md | 5 +++++ code/ui/blocks/src/blocks/Primary.stories.tsx | 20 ------------------- code/ui/blocks/src/blocks/Primary.tsx | 18 ++--------------- docs/api/doc-block-primary.md | 8 -------- 4 files changed, 7 insertions(+), 44 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 9d894c1b89cd..33d6c213033e 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -40,6 +40,7 @@ - [Deprecated docs parameters](#deprecated-docs-parameters) - [Description Doc block properties](#description-doc-block-properties) - [Manager API expandAll and collapseAll methods](#manager-api-expandall-and-collapseall-methods) + - [`Primary` Doc block properties](#primary-doc-block-properties) - [From version 7.5.0 to 7.6.0](#from-version-750-to-760) - [CommonJS with Vite is deprecated](#commonjs-with-vite-is-deprecated) - [Using implicit actions during rendering is deprecated](#using-implicit-actions-during-rendering-is-deprecated) @@ -690,6 +691,10 @@ api.collapseAll() // becomes api.emit(STORIES_COLLAPSE_ALL) api.expandAll() // becomes api.emit(STORIES_EXPAND_ALL) ``` +#### `Primary` Doc block properties + +The `name` prop is now removed in favor of the `of` property. [More info](#doc-blocks). + ## From version 7.5.0 to 7.6.0 #### CommonJS with Vite is deprecated diff --git a/code/ui/blocks/src/blocks/Primary.stories.tsx b/code/ui/blocks/src/blocks/Primary.stories.tsx index 7c4747d53a3b..4412881fd702 100644 --- a/code/ui/blocks/src/blocks/Primary.stories.tsx +++ b/code/ui/blocks/src/blocks/Primary.stories.tsx @@ -26,26 +26,6 @@ export const WithoutToolbar: Story = { }, }; -export const DefaultWithName: Story = { - name: 'Name', - args: { - name: 'Primary', - }, - parameters: { - relativeCsfPaths: ['../examples/Button.stories'], - }, -}; - -export const WithoutToolbarWithName: Story = { - name: 'Name Without Toolbar', - args: { - name: 'Without Toolbar', - }, - parameters: { - relativeCsfPaths: ['../examples/StoriesParameters.stories'], - }, -}; - export const DefaultWithOf: Story = { name: 'Of', args: { diff --git a/code/ui/blocks/src/blocks/Primary.tsx b/code/ui/blocks/src/blocks/Primary.tsx index 7d1364f2dc51..d2fdb6db311e 100644 --- a/code/ui/blocks/src/blocks/Primary.tsx +++ b/code/ui/blocks/src/blocks/Primary.tsx @@ -1,17 +1,11 @@ import type { FC } from 'react'; import React, { useContext } from 'react'; -import dedent from 'ts-dedent'; -import { deprecate } from '@storybook/client-logger'; import type { Of } from './useOf'; import { useOf } from './useOf'; import { DocsContext } from './DocsContext'; import { DocsStory } from './DocsStory'; interface PrimaryProps { - /** - * @deprecated Primary block should only be used to render the primary story, which is automatically found. - */ - name?: string; /** * Specify where to get the primary story from. */ @@ -19,8 +13,7 @@ interface PrimaryProps { } export const Primary: FC = (props) => { - const { name, of } = props; - + const { of } = props; if ('of' in props && of === undefined) { throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?'); } @@ -34,14 +27,7 @@ export const Primary: FC = (props) => { } if (!story) { - const storyId = name && docsContext.storyIdByName(name); - story = docsContext.storyById(storyId); - } - - if (name) { - deprecate(dedent`\`name\` prop is deprecated on the Primary block. - The Primary block should only be used to render the primary story, which is automatically found. - `); + story = docsContext.storyById(undefined); } return story ? ( diff --git a/docs/api/doc-block-primary.md b/docs/api/doc-block-primary.md index c4993c2db2a0..4214dcc2c1bd 100644 --- a/docs/api/doc-block-primary.md +++ b/docs/api/doc-block-primary.md @@ -34,11 +34,3 @@ import { Primary } from '@storybook/blocks'; Type: CSF file exports Specifies which CSF file is used to find the first story, which is then rendered by this block. Pass the full set of exports from the CSF file (not the default export!). - -### `name` - -(⛔️ **Deprecated**) - -Type: `string` - -Primary block should only be used to render the primary story, which is automatically found. From e66c43eee5d5113f4569d80f875e8509b4de7571 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 9 Jan 2024 09:55:49 +0100 Subject: [PATCH 05/16] get versioned packages from monorepo if not-latest, bail on init if called without version modifier --- code/lib/cli/src/generate.ts | 19 +++++++++- code/lib/cli/src/generators/NEXTJS/index.ts | 2 +- code/lib/cli/src/generators/REACT/index.ts | 2 +- .../cli/src/generators/REACT_NATIVE/index.ts | 4 +- .../cli/src/generators/WEBPACK_REACT/index.ts | 2 +- code/lib/cli/src/generators/baseGenerator.ts | 11 +++--- .../js-package-manager/JsPackageManager.ts | 18 ++++++++- .../src/utils/validate-cli-version.test.ts | 37 +++++++++++++++++++ .../lib/cli/src/utils/validate-cli-version.ts | 30 +++++++++++++++ 9 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 code/lib/cli/src/utils/validate-cli-version.test.ts create mode 100644 code/lib/cli/src/utils/validate-cli-version.ts diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 5ee5c9d89abf..1071bfd61ca6 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -8,6 +8,7 @@ import { logger } from '@storybook/node-logger'; import { addToGlobalContext } from '@storybook/telemetry'; import invariant from 'tiny-invariant'; +import dedent from 'ts-dedent'; import type { CommandOptions } from './generators/types'; import { initiate } from './initiate'; import { add } from './add'; @@ -23,6 +24,7 @@ import { parseList, getEnvConfig } from './utils'; import versions from './versions'; import { JsPackageManagerFactory } from './js-package-manager'; import { doctor } from './doctor'; +import { validateCLIVersioningFromProcessArgs } from './utils/validate-cli-version'; addToGlobalContext('cliVersion', versions.storybook); @@ -55,7 +57,22 @@ command('init') .option('-b --builder ', 'Builder library') .option('-l --linkable', 'Prepare installation for link (contributor helper)') .action((options: CommandOptions) => { - initiate(options, pkg).catch(() => process.exit(1)); + const proceed = validateCLIVersioningFromProcessArgs(process.argv); + + if (proceed) { + initiate(options, pkg).catch(() => process.exit(1)); + } else { + throw new Error(dedent` + Sorry, but you seem to have invoked the Storybook CLI without specifying the version. + + Please invoke the CLI with a version, e.g.: + npx storybook@latest init + yarn dlx storybook@latest init + pnpm dlx storybook@latest init + + This is to ensure that you are using the correct version of the CLI for your project. + `); + } }); command('add ') diff --git a/code/lib/cli/src/generators/NEXTJS/index.ts b/code/lib/cli/src/generators/NEXTJS/index.ts index 27d220321dd1..46add3e925ef 100644 --- a/code/lib/cli/src/generators/NEXTJS/index.ts +++ b/code/lib/cli/src/generators/NEXTJS/index.ts @@ -15,7 +15,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { 'react', { staticDir, - extraAddons: ['@storybook/addon-onboarding'], + extraAddons: ['@storybook/addon-onboarding@^2.0.0'], }, 'nextjs' ); diff --git a/code/lib/cli/src/generators/REACT/index.ts b/code/lib/cli/src/generators/REACT/index.ts index 046860356c56..b592327a159f 100644 --- a/code/lib/cli/src/generators/REACT/index.ts +++ b/code/lib/cli/src/generators/REACT/index.ts @@ -11,7 +11,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { extraPackages, useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, - extraAddons: ['@storybook/addon-onboarding'], + extraAddons: ['@storybook/addon-onboarding@^2.0.0'], }); }; diff --git a/code/lib/cli/src/generators/REACT_NATIVE/index.ts b/code/lib/cli/src/generators/REACT_NATIVE/index.ts index e3b8dcfa50c0..d87cd187e55b 100644 --- a/code/lib/cli/src/generators/REACT_NATIVE/index.ts +++ b/code/lib/cli/src/generators/REACT_NATIVE/index.ts @@ -30,14 +30,14 @@ const generator = async ( '@storybook/addon-controls@^6.5.16', ]; - const resolvedPackages = await packageManager.getVersionedPackages(packagesToResolve); + const versionedPackages = await packageManager.getVersionedPackages(packagesToResolve); const babelDependencies = await getBabelDependencies(packageManager, packageJson); const packages: string[] = []; packages.push(...babelDependencies); packages.push(...packagesWithFixedVersion); - packages.push(...resolvedPackages); + packages.push(...versionedPackages); if (missingReactDom && reactVersion) { packages.push(`react-dom@${reactVersion}`); } diff --git a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts index 94e8babee466..a4b058f0eaf9 100644 --- a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts +++ b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts @@ -4,7 +4,7 @@ import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { - extraAddons: ['@storybook/addon-onboarding'], + extraAddons: ['@storybook/addon-onboarding@^2.0.0'], useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, }); }; diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 75d12dc01499..e1e00df3a30f 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -320,8 +320,6 @@ export async function baseGenerator( const versionedPackages = await packageManager.getVersionedPackages(packages as string[]); versionedPackagesSpinner.succeed(); - const depsToInstall = [...versionedPackages]; - // Add basic babel config for a select few frameworks that need it, if they do not have a babel config file already if (builder !== CoreBuilder.Vite && !skipBabel) { const frameworksThatNeedBabelConfig = [ @@ -340,7 +338,7 @@ export async function baseGenerator( if (hasNoBabelFile && needsBabelConfig) { const isTypescript = language !== SupportedLanguage.JAVASCRIPT; const isReact = rendererId === 'react'; - depsToInstall.push( + versionedPackages.push( ...getBabelPresets({ typescript: isTypescript, jsx: isReact, @@ -361,7 +359,7 @@ export async function baseGenerator( if (hasEslint && !isStorybookPluginInstalled) { if (skipPrompts || (await suggestESLintPlugin())) { - depsToInstall.push('eslint-plugin-storybook'); + versionedPackages.push('eslint-plugin-storybook'); await configureEslintPlugin(eslintConfigFile ?? undefined, packageManager); } } @@ -370,12 +368,13 @@ export async function baseGenerator( // any failure regarding configuring the eslint plugin should not fail the whole generator } - if (depsToInstall.length > 0) { + if (versionedPackages.length > 0) { const addDependenciesSpinner = ora({ indent: 2, text: 'Installing Storybook dependencies', }).start(); - await packageManager.addDependencies({ ...npmOptions, packageJson }, depsToInstall); + + await packageManager.addDependencies({ ...npmOptions, packageJson }, versionedPackages); addDependenciesSpinner.succeed(); } diff --git a/code/lib/cli/src/js-package-manager/JsPackageManager.ts b/code/lib/cli/src/js-package-manager/JsPackageManager.ts index 5a3e82be3c43..9c0df207d78d 100644 --- a/code/lib/cli/src/js-package-manager/JsPackageManager.ts +++ b/code/lib/cli/src/js-package-manager/JsPackageManager.ts @@ -330,13 +330,29 @@ export abstract class JsPackageManager { /** * Return an array of strings matching following format: `@` * + * For packages in the storybook monorepo, when the latest version is equal to the version of the current CLI + * the version is not added to the string. + * + * When a package is in the monorepo, and the version is not equal to the CLI version, the version is taken from the versions.ts file and added to the string. + * * @param packages */ public getVersionedPackages(packages: string[]): Promise { return Promise.all( packages.map(async (pkg) => { const [packageName, packageVersion] = getPackageDetails(pkg); - return `${packageName}@${await this.getVersion(packageName, packageVersion)}`; + const latestInRange = await this.getVersion(packageName, packageVersion); + + const k = packageName as keyof typeof storybookPackagesVersions; + const fromData = storybookPackagesVersions[k]; + + if (fromData === latestInRange) { + return `${packageName}`; + } + if (fromData) { + return `${packageName}@${fromData}`; + } + return `${packageName}@${latestInRange}`; }) ); } diff --git a/code/lib/cli/src/utils/validate-cli-version.test.ts b/code/lib/cli/src/utils/validate-cli-version.test.ts new file mode 100644 index 000000000000..a4482db76a4b --- /dev/null +++ b/code/lib/cli/src/utils/validate-cli-version.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, test } from 'vitest'; +import { validateCLIVersioningFromProcessArgs } from './validate-cli-version'; + +describe('success case', () => { + test.each([ + [['npx', 'storybook@6.3.0', 'init'], true], + [['npx', 'sb@6.3.0', 'init'], true], + [['npx', 'storybook@^8.0.0', 'init'], true], + [['npx', 'sb@^8.0.0', 'init'], true], + [['npx', 'storybook@next', 'init'], true], + [['npx', 'sb@next', 'init'], true], + [['npx', 'storybook@latest', 'init'], true], + [['npx', 'sb@latest', 'init'], true], + [['npx', '-p', '@storybook/cli@latest', 'sb', 'init'], true], + ])('%s', async (args, expected) => { + expect(validateCLIVersioningFromProcessArgs(args)).toBe(expected); + }); +}); + +describe('fail case', () => { + test.each([ + [['npx', 'storybook', 'init'], false], + [['npx', 'sb', 'init'], false], + [['npx', '-p', '@storybook/cli', 'sb', 'init'], false], + ])('%s', async (args, expected) => { + await expect(validateCLIVersioningFromProcessArgs(args)).toBe(expected); + }); +}); + +describe('direct invocation', () => { + test.each([ + [['node', '../../mono/code/cli/bin/index.js', 'init'], true], + [['ts-node', '../../mono/code/cli/src/index.ts', 'init'], true], + ])('%s', async (args, expected) => { + await expect(validateCLIVersioningFromProcessArgs(args)).toBe(expected); + }); +}); diff --git a/code/lib/cli/src/utils/validate-cli-version.ts b/code/lib/cli/src/utils/validate-cli-version.ts new file mode 100644 index 000000000000..fe2a6aa32c1a --- /dev/null +++ b/code/lib/cli/src/utils/validate-cli-version.ts @@ -0,0 +1,30 @@ +export const validateCLIVersioningFromProcessArgs = (args: string[]) => { + const versioned = args.reduce((acc, arg) => { + if ( + arg.startsWith('storybook@') || + arg.startsWith('@storybook/cli@') || + arg.startsWith('sb@') + ) { + return true; + } + return acc; + }, false); + + const command = args.reduce((acc, arg) => { + if (arg === 'storybook' || arg === '@storybook/cli' || arg === 'sb') { + return true; + } + return acc; + }, false); + + if (versioned) { + return true; + } + + if (command) { + return false; + } + + // when we cannot detect either, we assume it's a direct invocation of the CLI + return true; +}; From 37ea07e8a78f8c467064a208b42a15095f9225df Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 9 Jan 2024 12:49:30 +0100 Subject: [PATCH 06/16] remove simple-update-notifier and replace it with actual checking --- code/lib/cli/package.json | 1 - code/lib/cli/src/generate.ts | 19 +--------- code/lib/cli/src/initiate.ts | 33 +++++++++++++---- .../src/utils/validate-cli-version.test.ts | 37 ------------------- .../lib/cli/src/utils/validate-cli-version.ts | 30 --------------- code/yarn.lock | 10 ----- 6 files changed, 26 insertions(+), 104 deletions(-) delete mode 100644 code/lib/cli/src/utils/validate-cli-version.test.ts delete mode 100644 code/lib/cli/src/utils/validate-cli-version.ts diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 671a4f309bdf..96c48162ded6 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -91,7 +91,6 @@ "prompts": "^2.4.0", "read-pkg-up": "^7.0.1", "semver": "^7.3.7", - "simple-update-notifier": "^2.0.0", "strip-json-comments": "^3.0.1", "tempy": "^1.0.1", "tiny-invariant": "^1.3.1", diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 1071bfd61ca6..5ee5c9d89abf 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -8,7 +8,6 @@ import { logger } from '@storybook/node-logger'; import { addToGlobalContext } from '@storybook/telemetry'; import invariant from 'tiny-invariant'; -import dedent from 'ts-dedent'; import type { CommandOptions } from './generators/types'; import { initiate } from './initiate'; import { add } from './add'; @@ -24,7 +23,6 @@ import { parseList, getEnvConfig } from './utils'; import versions from './versions'; import { JsPackageManagerFactory } from './js-package-manager'; import { doctor } from './doctor'; -import { validateCLIVersioningFromProcessArgs } from './utils/validate-cli-version'; addToGlobalContext('cliVersion', versions.storybook); @@ -57,22 +55,7 @@ command('init') .option('-b --builder ', 'Builder library') .option('-l --linkable', 'Prepare installation for link (contributor helper)') .action((options: CommandOptions) => { - const proceed = validateCLIVersioningFromProcessArgs(process.argv); - - if (proceed) { - initiate(options, pkg).catch(() => process.exit(1)); - } else { - throw new Error(dedent` - Sorry, but you seem to have invoked the Storybook CLI without specifying the version. - - Please invoke the CLI with a version, e.g.: - npx storybook@latest init - yarn dlx storybook@latest init - pnpm dlx storybook@latest init - - This is to ensure that you are using the correct version of the CLI for your project. - `); - } + initiate(options, pkg).catch(() => process.exit(1)); }); command('add ') diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index c0dbf5a3c7b3..aa0276b154ef 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -8,6 +8,7 @@ import { NxProjectDetectedError } from '@storybook/core-events/server-errors'; import dedent from 'ts-dedent'; import boxen from 'boxen'; +import { lt } from 'semver'; import type { Builder } from './project_types'; import { installableProjectTypes, ProjectType } from './project_types'; import { detect, isStorybookInstantiated, detectLanguage, detectPnp } from './detect'; @@ -241,15 +242,31 @@ async function doInitiate( force: pkgMgr, }); - const welcomeMessage = 'storybook init - the simplest way to add a Storybook to your project.'; - logger.log(chalk.inverse(`\n ${welcomeMessage} \n`)); + const latestVersion = await packageManager.getVersion('@storybook/cli'); + const currentVersion = versions['@storybook/cli']; + const isPrerelease = !!currentVersion.match(/(beta|rc|canary|future|next)/); + const isOutdated = lt(currentVersion, latestVersion); + const borderColor = isOutdated ? '#FC521F' : '#F1618C'; + + const welcome = `Adding storybook version ${currentVersion} to your project..`; + const notLatest = chalk.red(dedent` + Which is behind the latest release: ${latestVersion}! + You likely ran the init command through npx, which can use a locally cached version, to get the latest please run: + npx storybook@latest init + + You may want to CTRL+C to stop, and run with the latest version instead. + `); + const prelease = chalk.yellow('This is a pre-release version.'); - // Update notify code. - const { default: updateNotifier } = await import('simple-update-notifier'); - await updateNotifier({ - pkg: pkg as any, - updateCheckInterval: 1000 * 60 * 60, // every hour (we could increase this later on.) - }); + logger.log( + boxen( + [welcome] + .concat(isOutdated ? [notLatest] : []) + .concat(isPrerelease ? [prelease] : []) + .join('\n'), + { borderStyle: 'round', padding: 1, borderColor } + ) + ); // Check if the current directory is empty. if (options.force !== true && currentDirectoryIsEmpty(packageManager.type)) { diff --git a/code/lib/cli/src/utils/validate-cli-version.test.ts b/code/lib/cli/src/utils/validate-cli-version.test.ts deleted file mode 100644 index a4482db76a4b..000000000000 --- a/code/lib/cli/src/utils/validate-cli-version.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, test } from 'vitest'; -import { validateCLIVersioningFromProcessArgs } from './validate-cli-version'; - -describe('success case', () => { - test.each([ - [['npx', 'storybook@6.3.0', 'init'], true], - [['npx', 'sb@6.3.0', 'init'], true], - [['npx', 'storybook@^8.0.0', 'init'], true], - [['npx', 'sb@^8.0.0', 'init'], true], - [['npx', 'storybook@next', 'init'], true], - [['npx', 'sb@next', 'init'], true], - [['npx', 'storybook@latest', 'init'], true], - [['npx', 'sb@latest', 'init'], true], - [['npx', '-p', '@storybook/cli@latest', 'sb', 'init'], true], - ])('%s', async (args, expected) => { - expect(validateCLIVersioningFromProcessArgs(args)).toBe(expected); - }); -}); - -describe('fail case', () => { - test.each([ - [['npx', 'storybook', 'init'], false], - [['npx', 'sb', 'init'], false], - [['npx', '-p', '@storybook/cli', 'sb', 'init'], false], - ])('%s', async (args, expected) => { - await expect(validateCLIVersioningFromProcessArgs(args)).toBe(expected); - }); -}); - -describe('direct invocation', () => { - test.each([ - [['node', '../../mono/code/cli/bin/index.js', 'init'], true], - [['ts-node', '../../mono/code/cli/src/index.ts', 'init'], true], - ])('%s', async (args, expected) => { - await expect(validateCLIVersioningFromProcessArgs(args)).toBe(expected); - }); -}); diff --git a/code/lib/cli/src/utils/validate-cli-version.ts b/code/lib/cli/src/utils/validate-cli-version.ts deleted file mode 100644 index fe2a6aa32c1a..000000000000 --- a/code/lib/cli/src/utils/validate-cli-version.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const validateCLIVersioningFromProcessArgs = (args: string[]) => { - const versioned = args.reduce((acc, arg) => { - if ( - arg.startsWith('storybook@') || - arg.startsWith('@storybook/cli@') || - arg.startsWith('sb@') - ) { - return true; - } - return acc; - }, false); - - const command = args.reduce((acc, arg) => { - if (arg === 'storybook' || arg === '@storybook/cli' || arg === 'sb') { - return true; - } - return acc; - }, false); - - if (versioned) { - return true; - } - - if (command) { - return false; - } - - // when we cannot detect either, we assume it's a direct invocation of the CLI - return true; -}; diff --git a/code/yarn.lock b/code/yarn.lock index d97a6fb453c3..aaa982c1c791 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5266,7 +5266,6 @@ __metadata: prompts: "npm:^2.4.0" read-pkg-up: "npm:^7.0.1" semver: "npm:^7.3.7" - simple-update-notifier: "npm:^2.0.0" slash: "npm:^5.0.0" strip-json-comments: "npm:^3.1.1" tempy: "npm:^1.0.1" @@ -26202,15 +26201,6 @@ __metadata: languageName: node linkType: hard -"simple-update-notifier@npm:^2.0.0": - version: 2.0.0 - resolution: "simple-update-notifier@npm:2.0.0" - dependencies: - semver: "npm:^7.5.3" - checksum: 2a00bd03bfbcbf8a737c47ab230d7920f8bfb92d1159d421bdd194479f6d01ebc995d13fbe13d45dace23066a78a3dc6642999b4e3b38b847e6664191575b20c - languageName: node - linkType: hard - "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" From aa02264395b484885f5ddf37b9752ea7860324fc Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 9 Jan 2024 13:23:10 +0100 Subject: [PATCH 07/16] fix version detection --- code/lib/cli/src/generators/NEXTJS/index.ts | 2 +- code/lib/cli/src/generators/REACT/index.ts | 2 +- .../cli/src/generators/WEBPACK_REACT/index.ts | 2 +- code/lib/cli/src/initiate.ts | 30 ++++++++++--------- .../js-package-manager/JsPackageManager.ts | 2 +- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/code/lib/cli/src/generators/NEXTJS/index.ts b/code/lib/cli/src/generators/NEXTJS/index.ts index 46add3e925ef..174d23c7cb95 100644 --- a/code/lib/cli/src/generators/NEXTJS/index.ts +++ b/code/lib/cli/src/generators/NEXTJS/index.ts @@ -15,7 +15,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { 'react', { staticDir, - extraAddons: ['@storybook/addon-onboarding@^2.0.0'], + extraAddons: ['@storybook/addon-onboarding@^1.0.0'], }, 'nextjs' ); diff --git a/code/lib/cli/src/generators/REACT/index.ts b/code/lib/cli/src/generators/REACT/index.ts index b592327a159f..436c0dabd730 100644 --- a/code/lib/cli/src/generators/REACT/index.ts +++ b/code/lib/cli/src/generators/REACT/index.ts @@ -11,7 +11,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { extraPackages, useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, - extraAddons: ['@storybook/addon-onboarding@^2.0.0'], + extraAddons: ['@storybook/addon-onboarding@^1.0.0'], }); }; diff --git a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts index a4b058f0eaf9..b3d317376d0e 100644 --- a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts +++ b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts @@ -4,7 +4,7 @@ import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { - extraAddons: ['@storybook/addon-onboarding@^2.0.0'], + extraAddons: ['@storybook/addon-onboarding@^1.0.0'], useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, }); }; diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index aa0276b154ef..5084ec0a88a2 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -242,27 +242,29 @@ async function doInitiate( force: pkgMgr, }); - const latestVersion = await packageManager.getVersion('@storybook/cli'); + const latestVersion = await packageManager.latestVersion('@storybook/cli'); const currentVersion = versions['@storybook/cli']; - const isPrerelease = !!currentVersion.match(/(beta|rc|canary|future|next)/); + const isPrerelease = !!currentVersion.match(/(alpha|beta|rc|canary|future|next)/); const isOutdated = lt(currentVersion, latestVersion); const borderColor = isOutdated ? '#FC521F' : '#F1618C'; - const welcome = `Adding storybook version ${currentVersion} to your project..`; - const notLatest = chalk.red(dedent` - Which is behind the latest release: ${latestVersion}! - You likely ran the init command through npx, which can use a locally cached version, to get the latest please run: - npx storybook@latest init - - You may want to CTRL+C to stop, and run with the latest version instead. - `); - const prelease = chalk.yellow('This is a pre-release version.'); + const messages = { + welcome: `Adding storybook version ${chalk.bold(currentVersion)} to your project..`, + notLatest: chalk.red(dedent` + Which is behind the latest release: ${chalk.bold(latestVersion)}! + You likely ran the init command through npx, which can use a locally cached version, to get the latest please run: + npx storybook@latest init + + You may want to CTRL+C to stop, and run with the latest version instead. + `), + prelease: chalk.yellow('This is a pre-release version.'), + }; logger.log( boxen( - [welcome] - .concat(isOutdated ? [notLatest] : []) - .concat(isPrerelease ? [prelease] : []) + [messages.welcome] + .concat(isOutdated && !isPrerelease ? [messages.notLatest] : []) + .concat(isPrerelease ? [messages.prelease] : []) .join('\n'), { borderStyle: 'round', padding: 1, borderColor } ) diff --git a/code/lib/cli/src/js-package-manager/JsPackageManager.ts b/code/lib/cli/src/js-package-manager/JsPackageManager.ts index 9c0df207d78d..9fead3473d5f 100644 --- a/code/lib/cli/src/js-package-manager/JsPackageManager.ts +++ b/code/lib/cli/src/js-package-manager/JsPackageManager.ts @@ -341,7 +341,7 @@ export abstract class JsPackageManager { return Promise.all( packages.map(async (pkg) => { const [packageName, packageVersion] = getPackageDetails(pkg); - const latestInRange = await this.getVersion(packageName, packageVersion); + const latestInRange = await this.latestVersion(packageName, packageVersion); const k = packageName as keyof typeof storybookPackagesVersions; const fromData = storybookPackagesVersions[k]; From 3140c35e5a1a97ccfea2fe1424c10e5bcc58962a Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 9 Jan 2024 13:43:00 +0100 Subject: [PATCH 08/16] Refactor Primary component in blocks --- code/ui/blocks/src/blocks/Primary.tsx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/code/ui/blocks/src/blocks/Primary.tsx b/code/ui/blocks/src/blocks/Primary.tsx index d2fdb6db311e..337182fa574c 100644 --- a/code/ui/blocks/src/blocks/Primary.tsx +++ b/code/ui/blocks/src/blocks/Primary.tsx @@ -1,8 +1,7 @@ import type { FC } from 'react'; -import React, { useContext } from 'react'; +import React from 'react'; import type { Of } from './useOf'; import { useOf } from './useOf'; -import { DocsContext } from './DocsContext'; import { DocsStory } from './DocsStory'; interface PrimaryProps { @@ -18,17 +17,7 @@ export const Primary: FC = (props) => { throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?'); } - const docsContext = useContext(DocsContext); - - let story; - if (of) { - const resolvedOf = useOf(of || 'meta', ['meta']); - story = resolvedOf.csfFile.stories[0] || null; - } - - if (!story) { - story = docsContext.storyById(undefined); - } + const story = useOf(of || 'meta', ['meta']).csfFile.stories[0]; return story ? ( From ac23207481488358bccb6002b9516124ab91f860 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 9 Jan 2024 14:19:00 +0100 Subject: [PATCH 09/16] apply review comments --- code/lib/cli/src/initiate.ts | 10 +++++----- .../lib/cli/src/js-package-manager/JsPackageManager.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index 5084ec0a88a2..f32b824ab97b 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -8,7 +8,7 @@ import { NxProjectDetectedError } from '@storybook/core-events/server-errors'; import dedent from 'ts-dedent'; import boxen from 'boxen'; -import { lt } from 'semver'; +import { lt, prerelease } from 'semver'; import type { Builder } from './project_types'; import { installableProjectTypes, ProjectType } from './project_types'; import { detect, isStorybookInstantiated, detectLanguage, detectPnp } from './detect'; @@ -244,16 +244,16 @@ async function doInitiate( const latestVersion = await packageManager.latestVersion('@storybook/cli'); const currentVersion = versions['@storybook/cli']; - const isPrerelease = !!currentVersion.match(/(alpha|beta|rc|canary|future|next)/); + const isPrerelease = prerelease(currentVersion); const isOutdated = lt(currentVersion, latestVersion); const borderColor = isOutdated ? '#FC521F' : '#F1618C'; const messages = { - welcome: `Adding storybook version ${chalk.bold(currentVersion)} to your project..`, + welcome: `Adding Storybook version ${chalk.bold(currentVersion)} to your project..`, notLatest: chalk.red(dedent` - Which is behind the latest release: ${chalk.bold(latestVersion)}! + This version is behind the latest release, which is: ${chalk.bold(latestVersion)}! You likely ran the init command through npx, which can use a locally cached version, to get the latest please run: - npx storybook@latest init + ${chalk.bold('npx storybook@latest init')} You may want to CTRL+C to stop, and run with the latest version instead. `), diff --git a/code/lib/cli/src/js-package-manager/JsPackageManager.ts b/code/lib/cli/src/js-package-manager/JsPackageManager.ts index 9fead3473d5f..17ec76ad34a3 100644 --- a/code/lib/cli/src/js-package-manager/JsPackageManager.ts +++ b/code/lib/cli/src/js-package-manager/JsPackageManager.ts @@ -344,15 +344,15 @@ export abstract class JsPackageManager { const latestInRange = await this.latestVersion(packageName, packageVersion); const k = packageName as keyof typeof storybookPackagesVersions; - const fromData = storybookPackagesVersions[k]; + const currentVersion = storybookPackagesVersions[k]; - if (fromData === latestInRange) { + if (currentVersion === latestInRange) { return `${packageName}`; } - if (fromData) { - return `${packageName}@${fromData}`; + if (currentVersion) { + return `${packageName}@${currentVersion}`; } - return `${packageName}@${latestInRange}`; + return `${packageName}@^${latestInRange}`; }) ); } From 835feb515f874f9bad97fdd53e0b9ded8636aa18 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 10 Jan 2024 16:59:36 +0800 Subject: [PATCH 10/16] CLI: Add addon `remove` command --- code/lib/cli/src/generate.ts | 9 +++++ code/lib/cli/src/remove.ts | 46 ++++++++++++++++++++++ code/lib/csf-tools/src/ConfigFile.test.ts | 48 +++++++++++++++++++++++ code/lib/csf-tools/src/ConfigFile.ts | 28 +++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 code/lib/cli/src/remove.ts diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 5ee5c9d89abf..be5d8cc5ed80 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -11,6 +11,7 @@ import invariant from 'tiny-invariant'; import type { CommandOptions } from './generators/types'; import { initiate } from './initiate'; import { add } from './add'; +import { remove } from './remove'; import { migrate } from './migrate'; import { upgrade, type UpgradeOptions } from './upgrade'; import { sandbox } from './sandbox'; @@ -67,6 +68,14 @@ command('add ') .option('-s --skip-postinstall', 'Skip package specific postinstall config modifications') .action((addonName: string, options: any) => add(addonName, options)); +command('remove ') + .description('Remove an addon from your Storybook') + .option( + '--package-manager ', + 'Force package manager for installing dependencies' + ) + .action((addonName: string, options: any) => remove(addonName, options)); + command('babelrc') .description('generate the default storybook babel config into your current working directory') .action(() => generateStorybookBabelConfigInCWD()); diff --git a/code/lib/cli/src/remove.ts b/code/lib/cli/src/remove.ts new file mode 100644 index 000000000000..47c556eb578f --- /dev/null +++ b/code/lib/cli/src/remove.ts @@ -0,0 +1,46 @@ +import { getStorybookInfo } from '@storybook/core-common'; +import { readConfig, writeConfig } from '@storybook/csf-tools'; +import dedent from 'ts-dedent'; + +import { JsPackageManagerFactory, type PackageManagerName } from './js-package-manager'; + +const logger = console; + +/** + * Remove the given addon package and remove it from main.js + * + * Usage: + * - sb remove @storybook/addon-links + */ +export async function remove(addon: string, options: { packageManager: PackageManagerName }) { + const { packageManager: pkgMgr } = options; + + const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); + const packageJson = await packageManager.retrievePackageJson(); + const { mainConfig, configDir } = getStorybookInfo(packageJson); + + if (typeof configDir === 'undefined') { + throw new Error(dedent` + Unable to find storybook config directory + `); + } + + if (!mainConfig) { + logger.error('Unable to find storybook main.js config'); + return; + } + const main = await readConfig(mainConfig); + + // remove from package.json + logger.log(`Uninstalling ${addon}`); + await packageManager.removeDependencies({ packageJson }, [addon]); + + // add to main.js + logger.log(`Removing '${addon}' from main.js addons field.`); + try { + main.removeEntryFromArray(['addons'], addon); + await writeConfig(main); + } catch (err) { + logger.warn(`Failed to remove '${addon}' from main.js addons field.`); + } +} diff --git a/code/lib/csf-tools/src/ConfigFile.test.ts b/code/lib/csf-tools/src/ConfigFile.test.ts index 2ae59f8a75ac..c79dbce188e9 100644 --- a/code/lib/csf-tools/src/ConfigFile.test.ts +++ b/code/lib/csf-tools/src/ConfigFile.test.ts @@ -1139,4 +1139,52 @@ describe('ConfigFile', () => { `); }); }); + + describe('removeEntryFromArray', () => { + it('removes a string literal entry', () => { + const source = dedent` + export default { + addons: ['a', 'b', 'c'], + } + `; + const config = loadConfig(source).parse(); + config.removeEntryFromArray(['addons'], 'b'); + expect(config.getFieldValue(['addons'])).toMatchInlineSnapshot(`a,c`); + }); + + it('removes a preset-style object entry', () => { + const source = dedent` + export default { + addons: ['a', { name: 'b', options: {} }, 'c'], + } + `; + const config = loadConfig(source).parse(); + config.removeEntryFromArray(['addons'], 'b'); + expect(config.getFieldValue(['addons'])).toMatchInlineSnapshot(`a,c`); + }); + + it('throws when entry is missing', () => { + const source = dedent` + export default { + addons: ['a', { name: 'b', options: {} }, 'c'], + } + `; + const config = loadConfig(source).parse(); + expect(() => config.removeEntryFromArray(['addons'], 'x')).toThrowErrorMatchingInlineSnapshot( + `Error: Could not find 'x' in array at 'addons'` + ); + }); + + it('throws when target array is not an arral', () => { + const source = dedent` + export default { + addons: {}, + } + `; + const config = loadConfig(source).parse(); + expect(() => config.removeEntryFromArray(['addons'], 'x')).toThrowErrorMatchingInlineSnapshot( + `Error: Expected array at 'addons', got 'ObjectExpression'` + ); + }); + }); }); diff --git a/code/lib/csf-tools/src/ConfigFile.ts b/code/lib/csf-tools/src/ConfigFile.ts index eb2921b4e06d..45eae49ee61d 100644 --- a/code/lib/csf-tools/src/ConfigFile.ts +++ b/code/lib/csf-tools/src/ConfigFile.ts @@ -506,6 +506,34 @@ export class ConfigFile { } } + /** + * Specialized helper to remove addons or other array entries + * that can either be strings or objects with a name property. + */ + removeEntryFromArray(path: string[], value: string) { + const current = this.getFieldNode(path); + if (!current) return; + if (t.isArrayExpression(current)) { + const index = current.elements.findIndex((element) => { + if (t.isStringLiteral(element)) { + return element.value === value; + } + if (t.isObjectExpression(element)) { + const name = this._getPresetValue(element, 'name'); + return name === value; + } + return false; + }); + if (index >= 0) { + current.elements.splice(index, 1); + } else { + throw new Error(`Could not find '${value}' in array at '${path.join('.')}'`); + } + } else { + throw new Error(`Expected array at '${path.join('.')}', got '${current.type}'`); + } + } + _inferQuotes() { if (!this._quotes) { // first 500 tokens for efficiency From 7ca83ab560f6a4e401d5dbb5a6153438beb21cf5 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 10 Jan 2024 19:40:10 +0800 Subject: [PATCH 11/16] ConfigFile: Fix PNP wrapping logic --- code/lib/csf-tools/src/ConfigFile.test.ts | 22 ++++++++++++++++++++++ code/lib/csf-tools/src/ConfigFile.ts | 14 +++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/code/lib/csf-tools/src/ConfigFile.test.ts b/code/lib/csf-tools/src/ConfigFile.test.ts index c79dbce188e9..5fc75ff41f26 100644 --- a/code/lib/csf-tools/src/ConfigFile.test.ts +++ b/code/lib/csf-tools/src/ConfigFile.test.ts @@ -1163,6 +1163,28 @@ describe('ConfigFile', () => { expect(config.getFieldValue(['addons'])).toMatchInlineSnapshot(`a,c`); }); + it('removes a pnp-wrapped string entry', () => { + const source = dedent` + export default { + addons: ['a', getAbsolutePath('b'), 'c'], + } + `; + const config = loadConfig(source).parse(); + config.removeEntryFromArray(['addons'], 'b'); + expect(config.getFieldValue(['addons'])).toMatchInlineSnapshot(`a,c`); + }); + + it('removes a pnp-wrapped object entry', () => { + const source = dedent` + export default { + addons: ['a', { name: getAbsolutePath('b'), options: {} }, 'c'], + } + `; + const config = loadConfig(source).parse(); + config.removeEntryFromArray(['addons'], 'b'); + expect(config.getFieldValue(['addons'])).toMatchInlineSnapshot(`a,c`); + }); + it('throws when entry is missing', () => { const source = dedent` export default { diff --git a/code/lib/csf-tools/src/ConfigFile.ts b/code/lib/csf-tools/src/ConfigFile.ts index 45eae49ee61d..410bbcc1fee7 100644 --- a/code/lib/csf-tools/src/ConfigFile.ts +++ b/code/lib/csf-tools/src/ConfigFile.ts @@ -390,6 +390,16 @@ export class ConfigFile { return pathNames; } + _getPnpWrappedValue(node: t.Node) { + if (t.isCallExpression(node)) { + const arg = node.arguments[0]; + if (t.isStringLiteral(arg)) { + return arg.value; + } + } + return undefined; + } + /** * Given a node and a fallback property, returns a **non-evaluated** string value of the node. * 1. { node: 'value' } @@ -409,6 +419,8 @@ export class ConfigFile { ) { if (t.isStringLiteral(prop.value)) { value = prop.value.value; + } else { + value = this._getPnpWrappedValue(prop.value); } } @@ -522,7 +534,7 @@ export class ConfigFile { const name = this._getPresetValue(element, 'name'); return name === value; } - return false; + return this._getPnpWrappedValue(element as t.Node) === value; }); if (index >= 0) { current.elements.splice(index, 1); From 992148f1ba99d384a6d3a45a9d1667bd26c80045 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 10 Jan 2024 13:13:26 +0100 Subject: [PATCH 12/16] ensure CsfFile.stories follows the order of __namedExportsOrder --- .../src/modules/store/csf/processCSFFile.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts index 55fc8895eecd..f95b4c0db3a0 100644 --- a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts +++ b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts @@ -67,13 +67,14 @@ export function processCSFFile( const csfFile: CSFFile = { meta, stories: {}, moduleExports }; - Object.keys(namedExports).forEach((key) => { - if (isExportStory(key, meta)) { - const storyMeta = normalizeStory(key, namedExports[key], meta); - checkDisallowedParameters(storyMeta.parameters); - - csfFile.stories[storyMeta.id] = storyMeta; + (__namedExportsOrder as string[]).forEach((key) => { + if (!isExportStory(key, meta)) { + return; } + const storyMeta = normalizeStory(key, namedExports[key], meta); + checkDisallowedParameters(storyMeta.parameters); + + csfFile.stories[storyMeta.id] = storyMeta; }); return csfFile; From 44608bd579de333e254c43b8bfda7a140f6cf333 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 10 Jan 2024 13:13:26 +0100 Subject: [PATCH 13/16] correctly get primary story from CsfFile --- .../src/modules/store/csf/processCSFFile.ts | 13 +++++++------ code/ui/blocks/src/blocks/Primary.tsx | 7 ++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts index 55fc8895eecd..f95b4c0db3a0 100644 --- a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts +++ b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts @@ -67,13 +67,14 @@ export function processCSFFile( const csfFile: CSFFile = { meta, stories: {}, moduleExports }; - Object.keys(namedExports).forEach((key) => { - if (isExportStory(key, meta)) { - const storyMeta = normalizeStory(key, namedExports[key], meta); - checkDisallowedParameters(storyMeta.parameters); - - csfFile.stories[storyMeta.id] = storyMeta; + (__namedExportsOrder as string[]).forEach((key) => { + if (!isExportStory(key, meta)) { + return; } + const storyMeta = normalizeStory(key, namedExports[key], meta); + checkDisallowedParameters(storyMeta.parameters); + + csfFile.stories[storyMeta.id] = storyMeta; }); return csfFile; diff --git a/code/ui/blocks/src/blocks/Primary.tsx b/code/ui/blocks/src/blocks/Primary.tsx index 337182fa574c..49c2b6e7c03c 100644 --- a/code/ui/blocks/src/blocks/Primary.tsx +++ b/code/ui/blocks/src/blocks/Primary.tsx @@ -17,9 +17,10 @@ export const Primary: FC = (props) => { throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?'); } - const story = useOf(of || 'meta', ['meta']).csfFile.stories[0]; + const { stories } = useOf(of || 'meta', ['meta']).csfFile; + const primaryStory = Object.values(stories ?? {})[0]; - return story ? ( - + return primaryStory ? ( + ) : null; }; From 5d970c780ed377b7391bdeaf72cfe23754d2d8e3 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 10 Jan 2024 13:47:05 +0100 Subject: [PATCH 14/16] oops --- code/lib/cli/src/generators/NEXTJS/index.ts | 2 +- code/lib/cli/src/generators/REACT/index.ts | 2 +- code/lib/cli/src/generators/WEBPACK_REACT/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/lib/cli/src/generators/NEXTJS/index.ts b/code/lib/cli/src/generators/NEXTJS/index.ts index fc25750b6067..fe6672b669a7 100644 --- a/code/lib/cli/src/generators/NEXTJS/index.ts +++ b/code/lib/cli/src/generators/NEXTJS/index.ts @@ -15,7 +15,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { 'react', { staticDir, - extraAddons: ['@storybook/addon-onboarding^1.0.0'], + extraAddons: ['@storybook/addon-onboarding@^1.0.0'], webpackCompiler: ({ builder }) => undefined, }, 'nextjs' diff --git a/code/lib/cli/src/generators/REACT/index.ts b/code/lib/cli/src/generators/REACT/index.ts index c99527fcf3a9..967ed7b5531b 100644 --- a/code/lib/cli/src/generators/REACT/index.ts +++ b/code/lib/cli/src/generators/REACT/index.ts @@ -11,7 +11,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { extraPackages, webpackCompiler: ({ builder }) => (builder === CoreBuilder.Webpack5 ? 'swc' : undefined), - extraAddons: ['@storybook/addon-onboarding^1.0.0'], + extraAddons: ['@storybook/addon-onboarding@^1.0.0'], }); }; diff --git a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts index e081f541c580..a6f0293248f4 100644 --- a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts +++ b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts @@ -4,7 +4,7 @@ import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { - extraAddons: ['@storybook/addon-onboarding^1.0.0'], + extraAddons: ['@storybook/addon-onboarding@^1.0.0'], webpackCompiler: ({ builder }) => (builder === CoreBuilder.Webpack5 ? 'swc' : undefined), }); }; From 4e3ef87c6eb11ea6fec6819605bbf1efeb997b6c Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 10 Jan 2024 14:08:07 +0100 Subject: [PATCH 15/16] get primary story via componentStoriesFromCSFFile --- .../modules/preview-web/docs-context/DocsContext.ts | 4 ++++ .../src/modules/store/csf/processCSFFile.ts | 13 ++++++------- code/lib/types/src/modules/docs.ts | 5 +++++ code/ui/blocks/src/blocks/Primary.tsx | 9 ++++++--- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts index 40ea0ccbaa03..da41240756d5 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts @@ -201,6 +201,10 @@ export class DocsContext implements DocsContextProps return this.componentStoriesValue; }; + componentStoriesFromCSFFile = (csfFile: CSFFile) => { + return this.store.componentStoriesFromCSFFile({ csfFile }); + }; + storyById = (storyId?: StoryId) => { if (!storyId) { if (!this.primaryStory) diff --git a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts index f95b4c0db3a0..55fc8895eecd 100644 --- a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts +++ b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts @@ -67,14 +67,13 @@ export function processCSFFile( const csfFile: CSFFile = { meta, stories: {}, moduleExports }; - (__namedExportsOrder as string[]).forEach((key) => { - if (!isExportStory(key, meta)) { - return; - } - const storyMeta = normalizeStory(key, namedExports[key], meta); - checkDisallowedParameters(storyMeta.parameters); + Object.keys(namedExports).forEach((key) => { + if (isExportStory(key, meta)) { + const storyMeta = normalizeStory(key, namedExports[key], meta); + checkDisallowedParameters(storyMeta.parameters); - csfFile.stories[storyMeta.id] = storyMeta; + csfFile.stories[storyMeta.id] = storyMeta; + } }); return csfFile; diff --git a/code/lib/types/src/modules/docs.ts b/code/lib/types/src/modules/docs.ts index ba52f2b7c139..571478250143 100644 --- a/code/lib/types/src/modules/docs.ts +++ b/code/lib/types/src/modules/docs.ts @@ -83,6 +83,11 @@ export interface DocsContextProps { */ componentStories: () => PreparedStory[]; + /** + * Syncronously find all stories by CSF file. + */ + componentStoriesFromCSFFile: (csfFile: CSFFile) => PreparedStory[]; + /** * Get the story context of the referenced story. */ diff --git a/code/ui/blocks/src/blocks/Primary.tsx b/code/ui/blocks/src/blocks/Primary.tsx index 49c2b6e7c03c..7a7cd53fbe36 100644 --- a/code/ui/blocks/src/blocks/Primary.tsx +++ b/code/ui/blocks/src/blocks/Primary.tsx @@ -1,8 +1,9 @@ import type { FC } from 'react'; -import React from 'react'; +import React, { useContext } from 'react'; import type { Of } from './useOf'; import { useOf } from './useOf'; import { DocsStory } from './DocsStory'; +import { DocsContext } from './DocsContext'; interface PrimaryProps { /** @@ -17,8 +18,10 @@ export const Primary: FC = (props) => { throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?'); } - const { stories } = useOf(of || 'meta', ['meta']).csfFile; - const primaryStory = Object.values(stories ?? {})[0]; + const { csfFile } = useOf(of || 'meta', ['meta']); + const context = useContext(DocsContext); + + const primaryStory = context.componentStoriesFromCSFFile(csfFile)[0]; return primaryStory ? ( From d35bd03683bb0282edb3465ddf99133d39ea6575 Mon Sep 17 00:00:00 2001 From: storybook-bot <32066757+storybook-bot@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:03:27 +0000 Subject: [PATCH 16/16] Write changelog for 8.0.0-alpha.9 [skip ci] --- CHANGELOG.prerelease.md | 17 +++++++++++++++++ code/package.json | 3 ++- docs/versions/next.json | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 05f683fc4658..81c30d40624b 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,20 @@ +## 8.0.0-alpha.9 + +- AutoTitle: Fix case-insensitive trailing duplicate - [#25452](https://github.com/storybookjs/storybook/pull/25452), thanks [@ksugawara61](https://github.com/ksugawara61)! +- CLI: Add addon `remove` command - [#25538](https://github.com/storybookjs/storybook/pull/25538), thanks [@shilman](https://github.com/shilman)! +- CLI: Check optionalDependencies for storybook versions - [#25406](https://github.com/storybookjs/storybook/pull/25406), thanks [@reyronald](https://github.com/reyronald)! +- CLI: Fix using wrong package managers in existing projects - [#25474](https://github.com/storybookjs/storybook/pull/25474), thanks [@JReinhold](https://github.com/JReinhold)! +- CLI: Never prompt for ESLint plugin - [#25289](https://github.com/storybookjs/storybook/pull/25289), thanks [@shilman](https://github.com/shilman)! +- CLI: Versioned installation of monorepo packages - [#25517](https://github.com/storybookjs/storybook/pull/25517), thanks [@ndelangen](https://github.com/ndelangen)! +- CSF-tools: Allow type checking in storySort - [#25265](https://github.com/storybookjs/storybook/pull/25265), thanks [@honzahruby](https://github.com/honzahruby)! +- Core: Remove `storyStoreV7` feature flag - [#24658](https://github.com/storybookjs/storybook/pull/24658), thanks [@ndelangen](https://github.com/ndelangen)! +- Core: Remove deprecated createChannel APIs - [#25487](https://github.com/storybookjs/storybook/pull/25487), thanks [@yannbf](https://github.com/yannbf)! +- Doc blocks: Remove deprecated props from Primary block - [#25461](https://github.com/storybookjs/storybook/pull/25461), thanks [@yannbf](https://github.com/yannbf)! +- Node.js: Update version requirement to >= 18.0.0 - [#25516](https://github.com/storybookjs/storybook/pull/25516), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Storysource: Fix import error - [#25391](https://github.com/storybookjs/storybook/pull/25391), thanks [@unional](https://github.com/unional)! +- UI: Fix sidebar top and bottom addon slots - [#25426](https://github.com/storybookjs/storybook/pull/25426), thanks [@ndelangen](https://github.com/ndelangen)! +- Webpack5: Remove babel and SWC compiler from builder - [#25379](https://github.com/storybookjs/storybook/pull/25379), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! + ## 8.0.0-alpha.8 - Addon Links: Remove LinkTo from direct import - [#25418](https://github.com/storybookjs/storybook/pull/25418), thanks [@yannbf](https://github.com/yannbf)! diff --git a/code/package.json b/code/package.json index e41a777c6d44..1e2cd8bee675 100644 --- a/code/package.json +++ b/code/package.json @@ -298,5 +298,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "8.0.0-alpha.9" } diff --git a/docs/versions/next.json b/docs/versions/next.json index 7de16aa408a1..9cbedfd1eb57 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"8.0.0-alpha.8","info":{"plain":"- Addon Links: Remove LinkTo from direct import - [#25418](https://github.com/storybookjs/storybook/pull/25418), thanks [@yannbf](https://github.com/yannbf)!\n- Addon docs: Remove deprecated parameters - [#25469](https://github.com/storybookjs/storybook/pull/25469), thanks [@yannbf](https://github.com/yannbf)!\n- Builder Vite: Remove StorybookViteConfig type in favor of StorybookConfig - [#25441](https://github.com/storybookjs/storybook/pull/25441), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Error on explicit actions while rendering or playing - [#25238](https://github.com/storybookjs/storybook/pull/25238), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Core: Remove collapseAll and expandAll methods - [#25486](https://github.com/storybookjs/storybook/pull/25486), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Remove storyIndexers in favor of experimental_indexers - [#25468](https://github.com/storybookjs/storybook/pull/25468), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Remove unused staticDir type - [#25415](https://github.com/storybookjs/storybook/pull/25415), thanks [@yannbf](https://github.com/yannbf)!\n- Doc blocks: Remove deprecated props from Description block - [#25457](https://github.com/storybookjs/storybook/pull/25457), thanks [@yannbf](https://github.com/yannbf)!\n- Manager API: Remove deprecated navigateToSettingsPage method - [#25467](https://github.com/storybookjs/storybook/pull/25467), thanks [@yannbf](https://github.com/yannbf)!\n- React: Remove deprecated setGlobalConfig portable stories api - [#25442](https://github.com/storybookjs/storybook/pull/25442), thanks [@yannbf](https://github.com/yannbf)!\n- TypeScript: Remove deprecated addons module types - [#25485](https://github.com/storybookjs/storybook/pull/25485), thanks [@yannbf](https://github.com/yannbf)!\n- Types: Remove DecoratorFn, Story, ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta types - [#25477](https://github.com/storybookjs/storybook/pull/25477), thanks [@yannbf](https://github.com/yannbf)!\n- Types: Remove Framework in favor of Renderer types - [#25476](https://github.com/storybookjs/storybook/pull/25476), thanks [@yannbf](https://github.com/yannbf)!\n- UI: Remove deprecated WithTooltip props - [#25440](https://github.com/storybookjs/storybook/pull/25440), thanks [@yannbf](https://github.com/yannbf)!"}} +{"version":"8.0.0-alpha.9","info":{"plain":"- AutoTitle: Fix case-insensitive trailing duplicate - [#25452](https://github.com/storybookjs/storybook/pull/25452), thanks [@ksugawara61](https://github.com/ksugawara61)!\n- CLI: Add addon `remove` command - [#25538](https://github.com/storybookjs/storybook/pull/25538), thanks [@shilman](https://github.com/shilman)!\n- CLI: Check optionalDependencies for storybook versions - [#25406](https://github.com/storybookjs/storybook/pull/25406), thanks [@reyronald](https://github.com/reyronald)!\n- CLI: Fix using wrong package managers in existing projects - [#25474](https://github.com/storybookjs/storybook/pull/25474), thanks [@JReinhold](https://github.com/JReinhold)!\n- CLI: Never prompt for ESLint plugin - [#25289](https://github.com/storybookjs/storybook/pull/25289), thanks [@shilman](https://github.com/shilman)!\n- CLI: Versioned installation of monorepo packages - [#25517](https://github.com/storybookjs/storybook/pull/25517), thanks [@ndelangen](https://github.com/ndelangen)!\n- CSF-tools: Allow type checking in storySort - [#25265](https://github.com/storybookjs/storybook/pull/25265), thanks [@honzahruby](https://github.com/honzahruby)!\n- Core: Remove `storyStoreV7` feature flag - [#24658](https://github.com/storybookjs/storybook/pull/24658), thanks [@ndelangen](https://github.com/ndelangen)!\n- Core: Remove deprecated createChannel APIs - [#25487](https://github.com/storybookjs/storybook/pull/25487), thanks [@yannbf](https://github.com/yannbf)!\n- Doc blocks: Remove deprecated props from Primary block - [#25461](https://github.com/storybookjs/storybook/pull/25461), thanks [@yannbf](https://github.com/yannbf)!\n- Node.js: Update version requirement to >= 18.0.0 - [#25516](https://github.com/storybookjs/storybook/pull/25516), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Storysource: Fix import error - [#25391](https://github.com/storybookjs/storybook/pull/25391), thanks [@unional](https://github.com/unional)!\n- UI: Fix sidebar top and bottom addon slots - [#25426](https://github.com/storybookjs/storybook/pull/25426), thanks [@ndelangen](https://github.com/ndelangen)!\n- Webpack5: Remove babel and SWC compiler from builder - [#25379](https://github.com/storybookjs/storybook/pull/25379), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!"}}