Skip to content
2 changes: 1 addition & 1 deletion code/lib/create-storybook/src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const createStorybookProgram = program
// default value is false, but if the user sets STORYBOOK_DISABLE_TELEMETRY, it can be true
process.env.STORYBOOK_DISABLE_TELEMETRY && process.env.STORYBOOK_DISABLE_TELEMETRY !== 'false'
)
.option('--features <...list>', 'What features of storybook are you interested in?')
.option('--features <list...>', 'What features of storybook are you interested in?')
.option('--debug', 'Get more logs in debug mode')
.option('--enable-crash-reports', 'Enable sending crash reports to telemetry data')
.option('-f --force', 'Force add Storybook')
Expand Down
4 changes: 3 additions & 1 deletion code/lib/create-storybook/src/generators/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ export type Generator<T = void> = (
commandOptions?: CommandOptions
) => Promise<T>;

export type GeneratorFeature = 'docs' | 'test';

export type CommandOptions = {
packageManager: PackageManagerName;
usePnp?: boolean;
features: string[];
features: GeneratorFeature[];
type?: ProjectType;
force?: any;
html?: boolean;
Expand Down
69 changes: 48 additions & 21 deletions code/lib/create-storybook/src/initiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import svelteKitGenerator from './generators/SVELTEKIT';
import vue3Generator from './generators/VUE3';
import webComponentsGenerator from './generators/WEB-COMPONENTS';
import webpackReactGenerator from './generators/WEBPACK_REACT';
import type { CommandOptions, GeneratorOptions } from './generators/types';
import type { CommandOptions, GeneratorFeature, GeneratorOptions } from './generators/types';
import { packageVersions } from './ink/steps/checks/packageVersions';
import { vitestConfigFiles } from './ink/steps/checks/vitestConfigFiles';
import { currentDirectoryIsEmpty, scaffoldNewProject } from './scaffold-new-project';
Expand Down Expand Up @@ -294,27 +294,49 @@ export async function doInitiate(options: CommandOptions): Promise<

const isInteractive = process.stdout.isTTY && !process.env.CI;

let features = options.features || isInteractive ? ['dev', 'docs', 'test'] : ['dev', 'docs'];
const selectableFeatures: Record<GeneratorFeature, string> = {
docs: 'Documentation',
test: 'Testing',
};
let selectedFeatures = new Set<GeneratorFeature>();
selectedFeatures.toString = () =>
selectedFeatures.size === 0
? 'none'
: Array.from(selectedFeatures)
.map((f) => selectableFeatures[f])
.join(', ');

if (options.features?.length > 0) {
if (options.features.includes('docs')) {
selectedFeatures.add('docs');
}
if (options.features.includes('test')) {
selectedFeatures.add('test');
}
logger.log(`Selected features: ${selectedFeatures}`);
} else if (options.yes || !isInteractive) {
selectedFeatures.add('docs');

if (isInteractive && !options.features) {
if (isInteractive) {
// Don't automatically add test feature in CI
selectedFeatures.add('test');
}
logger.log(`Selected features: ${selectedFeatures}`);
} else {
const out = await prompts({
type: 'multiselect',
name: 'features',
message: `What are you using Storybook for?`,
choices: [
{ title: 'Development', value: 'dev', selected: true, disabled: true },
{ title: 'Documentation', value: 'docs', selected: true },
{ title: 'Testing', value: 'test', selected: true },
],
choices: Object.entries(selectableFeatures).map(([value, title]) => ({
title,
value,
selected: true,
})),
});
features = out.features;
selectedFeatures = new Set(out.features);
}

if (!features.includes('dev')) {
features.push('dev');
}

const telemetryFeatures = [...features];
const telemetryFeatures = ['dev', ...selectedFeatures];

// Check if the current directory is empty.
if (options.force !== true && currentDirectoryIsEmpty(packageManager.type)) {
Expand Down Expand Up @@ -377,7 +399,7 @@ export async function doInitiate(options: CommandOptions): Promise<
}
}

if (features.includes('test')) {
if (selectedFeatures.has('test')) {
const packageVersionsData = await packageVersions.condition({ packageManager }, {} as any);
if (packageVersionsData.type === 'incompatible') {
const { ignorePackageVersions } = isInteractive
Expand All @@ -393,14 +415,14 @@ export async function doInitiate(options: CommandOptions): Promise<
])
: { ignorePackageVersions: true };
if (ignorePackageVersions) {
features.splice(features.indexOf('test'), 1);
selectedFeatures.delete('test');
} else {
process.exit(0);
}
}
}

if (features.includes('test')) {
if (selectedFeatures.has('test')) {
const vitestConfigFilesData = await vitestConfigFiles.condition(
{ babel, findUp, fs } as any,
{ directory: process.cwd() } as any
Expand All @@ -419,7 +441,7 @@ export async function doInitiate(options: CommandOptions): Promise<
])
: { ignoreVitestConfigFiles: true };
if (ignoreVitestConfigFiles) {
features.splice(features.indexOf('test'), 1);
selectedFeatures.delete('test');
} else {
process.exit(0);
}
Expand All @@ -430,11 +452,14 @@ export async function doInitiate(options: CommandOptions): Promise<
await packageManager.installDependencies();
}

// update the mutated value
options.features = features;
// Update the options object with the selected features before passing it down to the generator
options.features = Array.from(selectedFeatures);

const installResult = await installStorybook(projectType as ProjectType, packageManager, options);

// Sync features back because they may have been mutated by the generator (e.g. in case of undetected project type)
selectedFeatures = new Set(options.features);

if (!options.skipInstall) {
await packageManager.installDependencies();
}
Expand Down Expand Up @@ -484,7 +509,7 @@ export async function doInitiate(options: CommandOptions): Promise<
? `ng run ${installResult.projectName}:storybook`
: packageManager.getRunStorybookCommand();

if (features.includes('test')) {
if (selectedFeatures.has('test')) {
logger.log(
`> npx storybook@${versions.storybook} add @storybook/experimental-addon-test@${versions['@storybook/experimental-addon-test']}`
);
Expand All @@ -498,6 +523,8 @@ export async function doInitiate(options: CommandOptions): Promise<
boxen(
dedent`
Storybook was successfully installed in your project! 🎉
Additional features: ${selectedFeatures}

To run Storybook manually, run ${picocolors.yellow(
picocolors.bold(storybookCommand)
)}. CTRL+C to stop.
Expand Down