Skip to content

Commit

Permalink
Merge pull request #22392 from storybookjs/valentin/angular-already-i…
Browse files Browse the repository at this point in the history
…nstalled-error

Prompt to force initialization when storybook folder is detected
  • Loading branch information
valentinpalkovic committed May 11, 2023
2 parents e1be2e8 + d6ad6fc commit 142d58b
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 91 deletions.
34 changes: 2 additions & 32 deletions code/lib/cli/src/detect.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as fs from 'fs';
import { logger } from '@storybook/node-logger';
import { getBowerJson } from './helpers';
import { detect, detectFrameworkPreset, detectLanguage, isStorybookInstalled } from './detect';
import { ProjectType, SUPPORTED_RENDERERS, SupportedLanguage } from './project_types';
import { detect, detectFrameworkPreset, detectLanguage } from './detect';
import { ProjectType, SupportedLanguage } from './project_types';
import type { PackageJsonWithMaybeDeps } from './js-package-manager';

jest.mock('./helpers', () => ({
Expand Down Expand Up @@ -346,36 +346,6 @@ describe('Detect', () => {
expect(detectLanguage()).toBe(SupportedLanguage.JAVASCRIPT);
});

describe('isStorybookInstalled should return', () => {
it('false if empty devDependency', () => {
expect(isStorybookInstalled({ devDependencies: {} }, false)).toBe(false);
});

it('false if no devDependency', () => {
expect(isStorybookInstalled({}, false)).toBe(false);
});

SUPPORTED_RENDERERS.forEach((framework) => {
it(`true if devDependencies has ${framework} Storybook version`, () => {
const devDependencies = {
[`@storybook/${framework}`]: '4.0.0-alpha.21',
};
expect(isStorybookInstalled({ devDependencies }, false)).toBeTruthy();
});
});

it('false if forced flag', () => {
expect(
isStorybookInstalled(
{
devDependencies: { '@storybook/react': '4.0.0-alpha.21' },
},
true
)
).toBe(false);
});
});

describe('detectFrameworkPreset should return', () => {
afterEach(() => {
jest.clearAllMocks();
Expand Down
31 changes: 6 additions & 25 deletions code/lib/cli/src/detect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import semver from 'semver';
import { logger } from '@storybook/node-logger';

import { pathExistsSync } from 'fs-extra';
import { join } from 'path';
import { join, resolve } from 'path';
import type { TemplateConfiguration, TemplateMatcher } from './project_types';
import {
ProjectType,
supportedTemplates,
SUPPORTED_RENDERERS,
SupportedLanguage,
unsupportedTemplate,
CoreBuilder,
} from './project_types';
import { getBowerJson, isNxProject, paddedLog } from './helpers';
import { commandLog, getBowerJson, isNxProject } from './helpers';
import type { JsPackageManager, PackageJson, PackageJsonWithMaybeDeps } from './js-package-manager';
import { detectWebpack } from './detect-webpack';

Expand Down Expand Up @@ -112,12 +111,12 @@ export function detectFrameworkPreset(
export function detectBuilder(packageManager: JsPackageManager, projectType: ProjectType) {
const viteConfig = findUp.sync(viteConfigFiles);
if (viteConfig) {
paddedLog('Detected Vite project. Setting builder to Vite');
commandLog('Detected Vite project. Setting builder to Vite')();
return CoreBuilder.Vite;
}

if (detectWebpack(packageManager)) {
paddedLog('Detected webpack project. Setting builder to webpack');
commandLog('Detected webpack project. Setting builder to webpack')();
return CoreBuilder.Webpack5;
}

Expand All @@ -134,26 +133,8 @@ export function detectBuilder(packageManager: JsPackageManager, projectType: Pro
}
}

export function isStorybookInstalled(
dependencies: Pick<PackageJson, 'devDependencies'> | false,
force?: boolean
) {
if (!dependencies) {
return false;
}

if (!force && dependencies.devDependencies) {
if (
SUPPORTED_RENDERERS.reduce(
(storybookPresent, framework) =>
storybookPresent || !!dependencies.devDependencies[`@storybook/${framework}`],
false
)
) {
return true;
}
}
return false;
export function isStorybookInstantiated(configDir = resolve(process.cwd(), '.storybook')) {
return fs.existsSync(configDir);
}

export function detectPnp() {
Expand Down
83 changes: 49 additions & 34 deletions code/lib/cli/src/initiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import { telemetry } from '@storybook/telemetry';
import { withTelemetry } from '@storybook/core-server';

import { installableProjectTypes, ProjectType } from './project_types';
import { detect, isStorybookInstalled, detectLanguage, detectBuilder, detectPnp } from './detect';
import {
detect,
isStorybookInstantiated,
detectLanguage,
detectBuilder,
detectPnp,
} from './detect';
import { commandLog, codeLog, paddedLog } from './helpers';
import angularGenerator from './generators/ANGULAR';
import aureliaGenerator from './generators/AURELIA';
Expand Down Expand Up @@ -77,118 +83,118 @@ const installStorybook = async <Project extends ProjectType>(

case ProjectType.REACT:
return reactGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "React" app\n')
commandLog('Adding Storybook support to your "React" app')
);

case ProjectType.REACT_NATIVE: {
return reactNativeGenerator(packageManager, npmOptions).then(
commandLog('Adding Storybook support to your "React Native" app\n')
commandLog('Adding Storybook support to your "React Native" app')
);
}

case ProjectType.QWIK: {
return qwikGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Qwik" app\n')
commandLog('Adding Storybook support to your "Qwik" app')
);
}

case ProjectType.WEBPACK_REACT:
return webpackReactGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Webpack React" app\n')
commandLog('Adding Storybook support to your "Webpack React" app')
);

case ProjectType.REACT_PROJECT:
return reactGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "React" library\n')
commandLog('Adding Storybook support to your "React" library')
);

case ProjectType.NEXTJS:
return nextjsGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Next" app\n')
commandLog('Adding Storybook support to your "Next" app')
);

case ProjectType.SFC_VUE:
return sfcVueGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Single File Components Vue" app\n')
commandLog('Adding Storybook support to your "Single File Components Vue" app')
);

case ProjectType.VUE:
return vueGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Vue" app\n')
commandLog('Adding Storybook support to your "Vue" app')
);

case ProjectType.VUE3:
return vue3Generator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Vue 3" app\n')
commandLog('Adding Storybook support to your "Vue 3" app')
);

case ProjectType.ANGULAR:
commandLog('Adding Storybook support to your "Angular" app\n');
commandLog('Adding Storybook support to your "Angular" app');
return angularGenerator(packageManager, npmOptions, generatorOptions, options);

case ProjectType.EMBER:
return emberGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Ember" app\n')
commandLog('Adding Storybook support to your "Ember" app')
);

case ProjectType.MITHRIL:
return mithrilGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Mithril" app\n')
commandLog('Adding Storybook support to your "Mithril" app')
);

case ProjectType.MARIONETTE:
return marionetteGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Marionette.js" app\n')
commandLog('Adding Storybook support to your "Marionette.js" app')
);

case ProjectType.MARKO:
return markoGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Marko" app\n')
commandLog('Adding Storybook support to your "Marko" app')
);

case ProjectType.HTML:
return htmlGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "HTML" app\n')
commandLog('Adding Storybook support to your "HTML" app')
);

case ProjectType.WEB_COMPONENTS:
return webComponentsGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "web components" app\n')
commandLog('Adding Storybook support to your "web components" app')
);

case ProjectType.RIOT:
return riotGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "riot.js" app\n')
commandLog('Adding Storybook support to your "riot.js" app')
);

case ProjectType.PREACT:
return preactGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Preact" app\n')
commandLog('Adding Storybook support to your "Preact" app')
);

case ProjectType.SVELTE:
return svelteGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Svelte" app\n')
commandLog('Adding Storybook support to your "Svelte" app')
);

case ProjectType.SVELTEKIT:
return svelteKitGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "SvelteKit" app\n')
commandLog('Adding Storybook support to your "SvelteKit" app')
);

case ProjectType.RAX:
return raxGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Rax" app\n')
commandLog('Adding Storybook support to your "Rax" app')
);

case ProjectType.AURELIA:
return aureliaGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Aurelia" app\n')
commandLog('Adding Storybook support to your "Aurelia" app')
);

case ProjectType.SERVER:
return serverGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Server" app\n')
commandLog('Adding Storybook support to your "Server" app')
);

case ProjectType.NX /* NX */:
Expand All @@ -200,7 +206,7 @@ const installStorybook = async <Project extends ProjectType>(

case ProjectType.SOLID:
return solidGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "SolidJS" app\n')
commandLog('Adding Storybook support to your "SolidJS" app')
);

case ProjectType.UNSUPPORTED:
Expand Down Expand Up @@ -313,22 +319,31 @@ async function doInitiate(options: CommandOptions, pkg: PackageJson): Promise<vo
}
done();

const storybookInstalled = isStorybookInstalled(packageJson, options.force);
const storybookInstantiated = isStorybookInstantiated();

if (storybookInstalled && projectType !== ProjectType.ANGULAR) {
if (storybookInstantiated && projectType !== ProjectType.ANGULAR) {
logger.log();
paddedLog('There seems to be a Storybook already available in this project.');
paddedLog('Apply following command to force:\n');
codeLog(['sb init [options] -f']);

// Add a new line for the clear visibility.
const { force } = await prompts([
{
type: 'confirm',
name: 'force',
message:
'We found a .storybook config directory in your project. Therefore we assume that Storybook is already instantiated for your project. Do you still want to continue and force the initialization?',
},
]);
logger.log();
throw new HandledError(`Angular project already installed`);

if (force) {
// eslint-disable-next-line no-param-reassign
options.force = true;
} else {
process.exit(0);
}
}

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

if (!options.skipInstall && !storybookInstalled) {
if (!options.skipInstall) {
await packageManager.installDependencies();
}

Expand Down

0 comments on commit 142d58b

Please sign in to comment.