Setup Storybook in Angular workspace via builders

This commit is contained in:
Valentin Palkovic 2023-01-10 14:23:46 +01:00
parent e204a21f65
commit eb6d514283
29 changed files with 653 additions and 290 deletions

View File

@ -40,6 +40,8 @@
- [Stories glob matches MDX files](#stories-glob-matches-mdx-files)
- [Add strict mode](#add-strict-mode)
- [Removed DLL flags](#removed-dll-flags)
- [Angular: Drop support for Angular \< 14](#angular-drop-support-for-angular--14)
- [Angular: Drop support for calling storybook directly](#angular-drop-support-for-calling-storybook-directly)
- [Docs Changes](#docs-changes)
- [Standalone docs files](#standalone-docs-files)
- [Referencing stories in docs files](#referencing-stories-in-docs-files)
@ -765,6 +767,15 @@ If user code in `.storybook/preview.js` or stories relies on "sloppy" mode behav
Earlier versions of Storybook used Webpack DLLs as a performance crutch. In 6.1, we've removed Storybook's built-in DLLs and have deprecated the command-line parameters `--no-dll` and `--ui-dll`. In 7.0 those options are removed.
#### Angular: Drop support for Angular < 14
Starting in 7.0, we drop support for Angular < 14
#### Angular: Drop support for calling storybook directly
In Storybook 6.4 we have deprecated calling Storybook directly (`npm run storybook`) and removed support for it in Storybook 7.0 entirely. Instead you have to set up
the Storybook builder in your `angular.json` and execute `ng run <your-project>:storybook` to start Storybook. Please visit https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular to set up Storybook for Angular correctly.
### Docs Changes
The information hierarchy of docs in Storybook has changed in 7.0. The main difference is that each docs is listed in the sidebar as a separate entry, rather than attached to individual stories.

View File

@ -1,5 +1,11 @@
# Storybook for Angular
- [Storybook for Angular](#storybook-for-angular)
- [Getting Started](#getting-started)
- [Setup Compodoc](#setup-compodoc)
- [Support for multi-project workspace](#support-for-multi-project-workspace)
- [Run Storybook](#run-storybook)
Storybook for Angular is a UI development environment for your Angular components.
With it, you can visualize different states of your UI components and develop them interactively.
@ -15,6 +21,66 @@ cd my-angular-app
npx storybook init
```
### Setup Compodoc
When installing, you will be given the option to set up Compodoc, which is a tool for creating documentation for Angular projects.
You can include JSDoc comments above components, directives, and other parts of your Angular code to include documentation for those elements. Compodoc uses these comments to generate documentation for your application. In Storybook, it is useful to add explanatory comments above @Inputs and @Outputs, since these are the main elements that Storybook displays in its user interface. The @Inputs and @Outputs are the elements that you can interact with in Storybook, such as controls.
## Support for multi-project workspace
Storybook supports Angular multi-project workspace. You can setup Storybook for each project in the workspace. When running `npx storybook init` you will be asked for which project Storybook should be set up. Essentially, during initialization, the `angular.json` will be edited to add the Storybook configuration for the selected project. The configuration looks approximately like this:
```json
// angular.json
{
...
"projects": {
...
"your-project": {
...
"architect": {
...
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"configDir": ".storybook",
"browserTarget": "your-project:build",
"compodoc": false,
"port": 6006
}
},
"build-storybook": {
"builder": "@storybook/angular:build-storybook",
"options": {
"configDir": ".storybook",
"browserTarget": "your-project:build",
"compodoc": false,
"outputDir": "dist/storybook/your-project"
}
}
}
}
}
}
```
## Run Storybook
To run Storybook for a particular project, please run:
```sh
ng run your-project:storybook
```
To build Storybook, run:
```sh
ng run your-project:build-storybook
```
You will find the output in `dist/storybook/your-project`.
For more information visit: [storybook.js.org](https://storybook.js.org)
---

View File

@ -1,7 +1,7 @@
import { BuilderContext } from '@angular-devkit/architect';
import { spawn } from 'child_process';
import { Observable } from 'rxjs';
import * as path from 'path';
import { JsPackageManagerFactory } from '@storybook/cli';
const hasTsConfigArg = (args: string[]) => args.indexOf('-p') !== -1;
const hasOutputArg = (args: string[]) =>
@ -20,37 +20,22 @@ export const runCompodoc = (
return new Observable<void>((observer) => {
const tsConfigPath = toRelativePath(tsconfig);
const finalCompodocArgs = [
'compodoc',
// Default options
...(hasTsConfigArg(compodocArgs) ? [] : ['-p', tsConfigPath]),
...(hasOutputArg(compodocArgs) ? [] : ['-d', `${context.workspaceRoot}`]),
...(hasOutputArg(compodocArgs) ? [] : ['-d', `${context.workspaceRoot || '.'}`]),
...compodocArgs,
];
const packageManager = JsPackageManagerFactory.getPackageManager();
try {
context.logger.info(finalCompodocArgs.join(' '));
const child = spawn('npx', finalCompodocArgs, {
cwd: context.workspaceRoot,
shell: true,
});
const stdout = packageManager.runScript('compodoc', finalCompodocArgs, context.workspaceRoot);
child.stdout.on('data', (data) => {
context.logger.info(data.toString());
});
child.stderr.on('data', (data) => {
context.logger.error(data.toString());
});
child.on('close', (code) => {
if (code === 0) {
observer.next();
observer.complete();
} else {
observer.error();
}
});
} catch (error) {
observer.error(error);
context.logger.info(stdout);
observer.next();
observer.complete();
} catch (e) {
context.logger.error(e);
observer.error();
}
});
};

View File

@ -34,7 +34,7 @@ const prepareMain = (
): AngularRenderer['storyResult'] => {
let { template } = story;
const component = story.component ?? context.component;
const { component } = context;
const userDefinedTemplate = !hasNoTemplate(template);
if (!userDefinedTemplate && component) {

View File

@ -22,6 +22,18 @@
},
"license": "MIT",
"author": "Storybook Team",
"exports": {
".": {
"node": "./dist/index.js",
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
},
"./package.json": "./package.json"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"bin": {
"getstorybook": "./bin/index.js",
"sb": "./bin/index.js"
@ -93,7 +105,8 @@
},
"bundler": {
"entries": [
"./src/generate.ts"
"./src/generate.ts",
"./src/index.ts"
],
"platform": "node"
},

View File

@ -344,14 +344,6 @@ describe('Detect', () => {
)
).toBe(false);
});
it('ALREADY_HAS_STORYBOOK if lib is present', () => {
expect(
isStorybookInstalled({
devDependencies: { '@storybook/react': '4.0.0-alpha.21' },
})
).toBe(ProjectType.ALREADY_HAS_STORYBOOK);
});
});
describe('detectFrameworkPreset should return', () => {

View File

@ -142,7 +142,7 @@ export function isStorybookInstalled(
false
)
) {
return ProjectType.ALREADY_HAS_STORYBOOK;
return true;
}
}
return false;
@ -194,9 +194,8 @@ export function detect(
return ProjectType.UNDETECTED;
}
const storyBookInstalled = isStorybookInstalled(packageJson, options.force);
if (storyBookInstalled) {
return storyBookInstalled;
if (isNxProject(packageJson)) {
return ProjectType.NX;
}
if (options.html) {
@ -205,3 +204,7 @@ export function detect(
return detectFrameworkPreset(packageJson || bowerJson);
}
function isNxProject(packageJSON: PackageJson) {
return !!packageJSON.devDependencies?.nx || fs.existsSync('nx.json');
}

View File

@ -0,0 +1,116 @@
import fs from 'fs';
import prompts from 'prompts';
import dedent from 'ts-dedent';
import { commandLog } from '../../helpers';
export const ANGULAR_JSON_PATH = 'angular.json';
export const compoDocPreviewPrefix = dedent`
import { setCompodocJson } from "@storybook/addon-docs/angular";
import docJson from "../documentation.json";
setCompodocJson(docJson);
`.trimStart();
export const promptForCompoDocs = async (): Promise<boolean> => {
const { useCompoDoc } = await prompts({
type: 'confirm',
name: 'useCompoDoc',
message: 'Do you want to use Compodoc for documentation?',
});
return useCompoDoc;
};
export class AngularJSON {
json: {
projects: Record<string, { root: string; architect: Record<string, any> }>;
};
constructor() {
if (!fs.existsSync(ANGULAR_JSON_PATH)) {
commandLog(
'An angular.json file was not found in the current directory. Storybook needs it to work properly.'
);
throw new Error('No angular.json file found');
}
const jsonContent = fs.readFileSync(ANGULAR_JSON_PATH, 'utf8');
this.json = JSON.parse(jsonContent);
}
get projects() {
return this.json.projects;
}
getProjectSettingsByName(projectName: string) {
return this.projects[projectName];
}
async getProjectName() {
const projectKeys = Object.keys(this.projects);
if (projectKeys.length > 1) {
const { projectName } = await prompts({
type: 'select',
name: 'projectName',
message: 'For which project do you want to generate Storybook configuration?',
choices: projectKeys.map((name) => ({
title: name,
value: name,
})),
});
return projectName;
}
return Object.keys(this.projects)[0];
}
addStorybookEntries({
angularProjectName,
storybookFolder,
useCompodoc,
root,
}: {
angularProjectName: string;
storybookFolder: string;
useCompodoc: boolean;
root: string;
}) {
// add an entry to the angular.json file to setup the storybook builders
const { architect } = this.projects[angularProjectName];
const baseOptions = {
configDir: storybookFolder,
browserTarget: `${angularProjectName}:build`,
compodoc: useCompodoc,
...(useCompodoc && { compodocArgs: ['-e', 'json', '-d', root || '.'] }),
};
if (!architect.storybook) {
architect.storybook = {
builder: '@storybook/angular:start-storybook',
options: {
...baseOptions,
port: 6006,
},
};
}
if (!architect['build-storybook']) {
architect['build-storybook'] = {
builder: '@storybook/angular:build-storybook',
options: {
...baseOptions,
outputDir: `dist/storybook/${angularProjectName}`,
},
};
}
}
write() {
fs.writeFileSync(ANGULAR_JSON_PATH, JSON.stringify(this.json, null, 2));
}
}

View File

@ -1,37 +1,22 @@
import path, { join } from 'path';
import { join } from 'path';
import semver from 'semver';
import {
checkForProjects,
editStorybookTsConfig,
getAngularAppTsConfigJson,
getAngularAppTsConfigPath,
getBaseTsConfigName,
} from './angular-helpers';
import { writeFileAsJson, copyTemplate } from '../../helpers';
import { getCliDir } from '../../dirs';
import fs from 'fs';
import dedent from 'ts-dedent';
import { baseGenerator } from '../baseGenerator';
import type { Generator } from '../types';
import { CoreBuilder } from '../../project_types';
import { AngularJSON, compoDocPreviewPrefix, promptForCompoDocs } from './helpers';
import { getCliDir } from '../../dirs';
import { copyTemplate } from '../../helpers';
import { isStorybookInstalled } from '../../detect';
function editAngularAppTsConfig() {
const tsConfigJson = getAngularAppTsConfigJson();
const glob = '**/*.stories.*';
if (!tsConfigJson) {
return;
}
const { exclude = [] } = tsConfigJson;
if (exclude.includes(glob)) {
return;
}
tsConfigJson.exclude = [...exclude, glob];
writeFileAsJson(getAngularAppTsConfigPath(), tsConfigJson);
}
const generator: Generator = async (packageManager, npmOptions, options) => {
checkForProjects();
const generator: Generator<{ projectName: string }> = async (
packageManager,
npmOptions,
options,
commandOptions
) => {
const packageJson = packageManager.retrievePackageJson();
const angularVersionFromDependencies = semver.coerce(
packageManager.retrievePackageJson().dependencies['@angular/core']
)?.version;
@ -44,60 +29,77 @@ const generator: Generator = async (packageManager, npmOptions, options) => {
const isWebpack5 = semver.gte(angularVersion, '12.0.0');
const updatedOptions = isWebpack5 ? { ...options, builder: CoreBuilder.Webpack5 } : options;
const angularJSON = new AngularJSON();
const angularProjectName = await angularJSON.getProjectName();
const { root } = angularJSON.getProjectSettingsByName(angularProjectName);
const { projects } = angularJSON;
const useCompodoc = commandOptions.yes ? true : await promptForCompoDocs();
const storybookFolder = root ? `${root}/.storybook` : '.storybook';
if (root !== '') {
// create a .storybook folder in the root of the Angular project
fs.mkdirSync(storybookFolder, { recursive: true });
const rootReferencePathFromStorybookFolder = root
.split('/')
.map(() => '../')
.join('');
fs.writeFileSync(
`${storybookFolder}/main.js`,
dedent(`
const mainRoot = require('${rootReferencePathFromStorybookFolder}../.storybook/main.js');
module.exports = {
...mainRoot
};
`)
);
}
angularJSON.addStorybookEntries({
angularProjectName,
storybookFolder,
useCompodoc,
root,
});
angularJSON.write();
const isSbInstalled = isStorybookInstalled(packageJson, commandOptions.force);
await baseGenerator(
packageManager,
npmOptions,
updatedOptions,
{
...updatedOptions,
...(useCompodoc && {
frameworkPreviewParts: {
prefix: compoDocPreviewPrefix,
},
}),
},
'angular',
{
extraPackages: ['@compodoc/compodoc'],
...(useCompodoc && { extraPackages: ['@compodoc/compodoc'] }),
addScripts: false,
componentsDestinationPath: root ? `${root}/src/stories` : undefined,
addMainFile: !isSbInstalled,
storybookConfigFolder: storybookFolder,
},
'angular'
);
const templateDir = join(getCliDir(), 'templates', 'angular');
copyTemplate(templateDir);
editAngularAppTsConfig();
// TODO: we need to add the following:
/*
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"browserTarget": "angular-cli:build",
"port": 4400
}
},
"build-storybook": {
"builder": "@storybook/angular:build-storybook",
"options": {
"browserTarget": "angular-cli:build"
}
if (Object.keys(projects).length === 1) {
packageManager.addScripts({
storybook: `ng run ${angularProjectName}:storybook`,
'build-storybook': `ng run ${angularProjectName}:build-storybook`,
});
}
*/
// to the user's angular.json file.
const templateDir = join(getCliDir(), 'templates', 'angular');
copyTemplate(templateDir, root || undefined);
// then we want to add these scripts to package.json
// packageManager.addScripts({
// storybook: 'ng storybook',
// 'build-storybook': 'ng build-storybook',
// });
editStorybookTsConfig(path.resolve('./.storybook/tsconfig.json'));
// edit scripts to generate docs
const tsConfigFile = await getBaseTsConfigName();
packageManager.addScripts({
'docs:json': `compodoc -p ./${tsConfigFile} -e json -d .`,
});
packageManager.addStorybookCommandInScripts({
port: 6006,
preCommand: 'docs:json',
});
return {
projectName: angularProjectName,
};
};
export default generator;

View File

@ -17,13 +17,16 @@ const defaultOptions: FrameworkOptions = {
extraAddons: [],
staticDir: undefined,
addScripts: true,
addMainFile: true,
addComponents: true,
addBabel: false,
addESLint: false,
extraMain: undefined,
framework: undefined,
extensions: undefined,
componentsDestinationPath: undefined,
commonJs: false,
storybookConfigFolder: '.storybook',
};
const getBuilderDetails = (builder: string) => {
@ -106,7 +109,13 @@ const hasFrameworkTemplates = (framework?: SupportedFrameworks) =>
export async function baseGenerator(
packageManager: JsPackageManager,
npmOptions: NpmOptions,
{ language, builder = CoreBuilder.Webpack5, pnp, commonJs }: GeneratorOptions,
{
language,
builder = CoreBuilder.Webpack5,
pnp,
commonJs,
frameworkPreviewParts,
}: GeneratorOptions,
renderer: SupportedRenderers,
options: FrameworkOptions = defaultOptions,
framework?: SupportedFrameworks
@ -116,11 +125,14 @@ export async function baseGenerator(
extraPackages,
staticDir,
addScripts,
addMainFile,
addComponents,
addBabel,
addESLint,
extraMain,
extensions,
storybookConfigFolder,
componentsDestinationPath,
} = {
...defaultOptions,
...options,
@ -194,26 +206,29 @@ export async function baseGenerator(
const versionedPackages = await packageManager.getVersionedPackages(packages);
await fse.ensureDir('./.storybook');
await fse.ensureDir(`./${storybookConfigFolder}`);
await configureMain({
framework: { name: frameworkInclude, options: options.framework || {} },
docs: { autodocs: 'tag' },
addons: pnp ? addons.map(wrapForPnp) : addons,
extensions,
commonJs,
...(staticDir ? { staticDirs: [path.join('..', staticDir)] } : null),
...extraMain,
...(type !== 'framework'
? {
core: {
builder: builderInclude,
},
}
: {}),
});
if (addMainFile) {
await configureMain({
framework: { name: frameworkInclude, options: options.framework || {} },
storybookConfigFolder,
docs: { autodocs: 'tag' },
addons: pnp ? addons.map(wrapForPnp) : addons,
extensions,
commonJs,
...(staticDir ? { staticDirs: [path.join('..', staticDir)] } : null),
...extraMain,
...(type !== 'framework'
? {
core: {
builder: builderInclude,
},
}
: {}),
});
}
await configurePreview(rendererId);
await configurePreview({ frameworkPreviewParts, storybookConfigFolder });
// FIXME: temporary workaround for https://github.com/storybookjs/storybook/issues/17516
if (
@ -226,7 +241,9 @@ export async function baseGenerator(
window.global = window;
</script>
`;
await fse.writeFile(`.storybook/preview-head.html`, previewHead, { encoding: 'utf8' });
await fse.writeFile(`${storybookConfigFolder}/preview-head.html`, previewHead, {
encoding: 'utf8',
});
}
const babelDependencies =
@ -256,6 +273,6 @@ export async function baseGenerator(
if (addComponents) {
const templateLocation = hasFrameworkTemplates(framework) ? framework : rendererId;
await copyComponents(templateLocation, language);
await copyComponents(templateLocation, language, componentsDestinationPath);
}
}

View File

@ -1,12 +1,12 @@
import fse from 'fs-extra';
import { dedent } from 'ts-dedent';
import type { SupportedRenderers, SupportedFrameworks } from '../project_types';
interface ConfigureMainOptions {
addons: string[];
extensions?: string[];
commonJs?: boolean;
staticDirs?: string[];
storybookConfigFolder: string;
/**
* Extra values for main.js
*
@ -19,10 +19,20 @@ interface ConfigureMainOptions {
[key: string]: any;
}
export interface FrameworkPreviewParts {
prefix: string;
}
interface ConfigurePreviewOptions {
frameworkPreviewParts?: FrameworkPreviewParts;
storybookConfigFolder: string;
}
export async function configureMain({
addons,
extensions = ['js', 'jsx', 'ts', 'tsx'],
commonJs = false,
storybookConfigFolder,
...custom
}: ConfigureMainOptions) {
const prefix = (await fse.pathExists('./src')) ? '../src' : '../stories';
@ -44,7 +54,7 @@ export async function configureMain({
// .replaceAll(/"(path\.dirname\(require\.resolve\(path\.join\('.*\))"/g, (_, a) => a)}`;
await fse.writeFile(
`./.storybook/main.${commonJs ? 'cjs' : 'js'}`,
`./${storybookConfigFolder}/main.${commonJs ? 'cjs' : 'js'}`,
dedent`
const path = require('path');
${stringified}
@ -53,20 +63,9 @@ export async function configureMain({
);
}
const frameworkToPreviewParts: Partial<Record<SupportedFrameworks | SupportedRenderers, any>> = {
angular: {
prefix: dedent`
import { setCompodocJson } from "@storybook/addon-docs/angular";
import docJson from "../documentation.json";
setCompodocJson(docJson);
`.trimStart(),
},
};
export async function configurePreview(framework: SupportedFrameworks | SupportedRenderers) {
const { prefix = '', extraParameters = '' } = frameworkToPreviewParts[framework] || {};
const previewPath = `./.storybook/preview.js`;
export async function configurePreview(options: ConfigurePreviewOptions) {
const { prefix = '' } = options?.frameworkPreviewParts || {};
const previewPath = `./${options.storybookConfigFolder}/preview.js`;
// If the framework template included a preview then we have nothing to do
if (await fse.pathExists(previewPath)) {
@ -83,7 +82,6 @@ export async function configurePreview(framework: SupportedFrameworks | Supporte
date: /Date$/,
},
},
${extraParameters}
}`
.replace(' \n', '')
.trim();

View File

@ -2,6 +2,7 @@ import type { NpmOptions } from '../NpmOptions';
import type { SupportedLanguage, Builder, ProjectType } from '../project_types';
import type { JsPackageManager } from '../js-package-manager/JsPackageManager';
import { type PackageManagerName } from '../js-package-manager/JsPackageManager';
import type { FrameworkPreviewParts } from './configure';
export type GeneratorOptions = {
language: SupportedLanguage;
@ -9,6 +10,8 @@ export type GeneratorOptions = {
linkable: boolean;
pnp: boolean;
commonJs: boolean;
storiesRoots: string[];
frameworkPreviewParts?: FrameworkPreviewParts;
};
export interface FrameworkOptions {
@ -16,6 +19,7 @@ export interface FrameworkOptions {
extraAddons?: string[];
staticDir?: string;
addScripts?: boolean;
addMainFile?: boolean;
addComponents?: boolean;
addBabel?: boolean;
addESLint?: boolean;
@ -23,13 +27,16 @@ export interface FrameworkOptions {
extensions?: string[];
framework?: Record<string, any>;
commonJs?: boolean;
storybookConfigFolder?: string;
componentsDestinationPath?: string;
}
export type Generator = (
export type Generator<T = void> = (
packageManagerInstance: JsPackageManager,
npmOptions: NpmOptions,
generatorOptions: GeneratorOptions
) => Promise<void>;
generatorOptions: GeneratorOptions,
commandOptions?: CommandOptions
) => Promise<T>;
export type CommandOptions = {
packageManager: PackageManagerName;
@ -40,6 +47,7 @@ export type CommandOptions = {
html?: boolean;
skipInstall?: boolean;
parser?: string;
// Automatically answer yes to prompts
yes?: boolean;
builder?: Builder;
linkable?: boolean;

View File

@ -176,19 +176,20 @@ export function addToDevDependenciesIfNotPresent(
}
}
export function copyTemplate(templateRoot: string) {
export function copyTemplate(templateRoot: string, destination = '.') {
const templateDir = path.resolve(templateRoot, `template-csf/`);
if (!fs.existsSync(templateDir)) {
throw new Error(`Couldn't find template dir`);
}
fse.copySync(templateDir, '.', { overwrite: true });
fse.copySync(templateDir, destination, { overwrite: true });
}
export async function copyComponents(
renderer: SupportedFrameworks | SupportedRenderers,
language: SupportedLanguage
language: SupportedLanguage,
destination?: string
) {
const languageFolderMapping: Record<SupportedLanguage, string> = {
[SupportedLanguage.JAVASCRIPT]: 'js',
@ -232,7 +233,7 @@ export async function copyComponents(
return './stories';
};
const destinationPath = await targetPath();
const destinationPath = destination ?? (await targetPath());
await fse.copy(join(getCliDir(), 'rendererAssets/common'), destinationPath, {
overwrite: true,
});

View File

@ -0,0 +1 @@
export * from './js-package-manager';

View File

@ -37,11 +37,11 @@ import type { CommandOptions } from './generators/types';
const logger = console;
const installStorybook = (
projectType: ProjectType,
const installStorybook = <Project extends ProjectType>(
projectType: Project,
packageManager: JsPackageManager,
options: CommandOptions
): Promise<void> => {
): Promise<any> => {
const npmOptions: NpmOptions = {
installAsDevDependencies: true,
skipInstall: options.skipInstall,
@ -64,18 +64,8 @@ const installStorybook = (
pnp: options.usePnp,
};
const runGenerator: () => Promise<void> = async () => {
const runGenerator: () => Promise<any> = async () => {
switch (projectType) {
case ProjectType.ALREADY_HAS_STORYBOOK:
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.
logger.log();
return Promise.resolve();
case ProjectType.REACT_SCRIPTS:
return reactScriptsGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Create React App" based project')
@ -135,9 +125,8 @@ const installStorybook = (
);
case ProjectType.ANGULAR:
return angularGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Angular" app\n')
);
commandLog('Adding Storybook support to your "Angular" app\n');
return angularGenerator(packageManager, npmOptions, generatorOptions, options);
case ProjectType.EMBER:
return emberGenerator(packageManager, npmOptions, generatorOptions).then(
@ -204,6 +193,13 @@ const installStorybook = (
commandLog('Adding Storybook support to your "Server" app\n')
);
case ProjectType.NX /* NX */:
paddedLog(
'We have detected Nx in your project. Please use `nx g @nrwl/storybook:configuration` to add Storybook to your project.'
);
paddedLog('For more information, please see https://nx.dev/packages/storybook');
return Promise.reject();
case ProjectType.UNSUPPORTED:
paddedLog(`We detected a project type that we don't support yet.`);
paddedLog(
@ -296,10 +292,7 @@ async function doInitiate(options: CommandOptions, pkg: PackageJson): Promise<vo
try {
if (projectTypeProvided) {
if (installableProjectTypes.includes(projectTypeProvided)) {
const storybookInstalled = isStorybookInstalled(packageJson, options.force);
projectType = storybookInstalled
? ProjectType.ALREADY_HAS_STORYBOOK
: projectTypeProvided.toUpperCase();
projectType = projectTypeProvided.toUpperCase();
} else {
done(`The provided project type was not recognized by Storybook: ${projectTypeProvided}`);
logger.log(`\nThe project types currently supported by Storybook are:\n`);
@ -316,12 +309,27 @@ async function doInitiate(options: CommandOptions, pkg: PackageJson): Promise<vo
}
done();
await installStorybook(projectType as ProjectType, packageManager, {
const storybookInstalled = isStorybookInstalled(packageJson, options.force);
if (storybookInstalled && 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.
logger.log();
return;
}
const installResult = await installStorybook(projectType as ProjectType, packageManager, {
...options,
...(isEsm ? { commonJs: true } : undefined),
}).catch((e) => {
process.exit();
});
if (!options.skipInstall) {
if (!options.skipInstall && !storybookInstalled) {
packageManager.installDependencies();
}
@ -332,7 +340,13 @@ async function doInitiate(options: CommandOptions, pkg: PackageJson): Promise<vo
await automigrate({ yes: options.yes || process.env.CI === 'true', packageManager: pkgMgr });
logger.log('\nTo run your Storybook, type:\n');
codeLog([packageManager.getRunStorybookCommand()]);
if (projectType === ProjectType.ANGULAR) {
codeLog([`ng run ${installResult.projectName}:storybook`]);
} else {
codeLog([packageManager.getRunStorybookCommand()]);
}
logger.log('\nFor more information visit:', chalk.cyan('https://storybook.js.org'));
if (projectType === ProjectType.REACT_NATIVE) {

View File

@ -376,9 +376,16 @@ export abstract class JsPackageManager {
): // Use generic and conditional type to force `string[]` if fetchAllVersions is true and `string` if false
Promise<T extends true ? string[] : string>;
public executeCommand(command: string, args: string[], stdio?: 'pipe' | 'inherit'): string {
public abstract runScript(script: string, args: string[], cwd?: string): string;
public executeCommand(
command: string,
args: string[],
stdio?: 'pipe' | 'inherit',
cwd?: string
): string {
const commandResult = spawnSync(command, args, {
cwd: this.cwd,
cwd: cwd ?? this.cwd,
stdio: stdio ?? 'pipe',
encoding: 'utf-8',
shell: true,

View File

@ -57,6 +57,37 @@ describe('NPM Proxy', () => {
});
});
describe('runScript', () => {
describe('npm6', () => {
it('should execute script `npm run compodoc -- -e json -d .`', () => {
const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('6.0.0');
npmProxy.runScript('compodoc', ['-e', 'json', '-d', '.']);
expect(executeCommandSpy).toHaveBeenLastCalledWith(
'npm',
['run', 'compodoc', '--', '-e', 'json', '-d', '.'],
undefined,
undefined
);
});
});
describe('npm7', () => {
it('should execute script `npm run compodoc -- -e json -d .`', () => {
const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('7.1.0');
npmProxy.runScript('compodoc', ['-e', 'json', '-d', '.']);
expect(executeCommandSpy).toHaveBeenLastCalledWith(
'npm',
['run', 'compodoc', '--', '-e', 'json', '-d', '.'],
undefined,
undefined
);
});
});
});
describe('addDependencies', () => {
describe('npm6', () => {
it('with devDep it should run `npm install -D @storybook/preview-api`', () => {

View File

@ -38,6 +38,10 @@ export class NPMProxy extends JsPackageManager {
return this.uninstallArgs;
}
public runScript(command: string, args: string[], cwd?: string): string {
return this.executeCommand(`npm`, ['run', command, '--', ...args], undefined, cwd);
}
protected getResolutions(packageJson: PackageJson, versions: Record<string, string>) {
return {
overrides: {

View File

@ -46,6 +46,21 @@ describe('NPM Proxy', () => {
});
});
describe('runScript', () => {
it('should execute script `yarn compodoc -- -e json -d .`', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('7.1.0');
pnpmProxy.runScript('compodoc', ['-e', 'json', '-d', '.']);
expect(executeCommandSpy).toHaveBeenLastCalledWith(
'pnpm',
['run', 'compodoc', '-e', 'json', '-d', '.'],
undefined,
undefined
);
});
});
describe('addDependencies', () => {
it('with devDep it should run `pnpm add -D @storybook/preview-api`', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('6.0.0');

View File

@ -24,6 +24,10 @@ export class PNPMProxy extends JsPackageManager {
return this.executeCommand('pnpm', ['--version']);
}
runScript(command: string, args: string[], cwd?: string): string {
return this.executeCommand(`pnpm`, ['run', command, ...args], undefined, cwd);
}
protected getResolutions(packageJson: PackageJson, versions: Record<string, string>) {
return {
overrides: {

View File

@ -46,6 +46,21 @@ describe('Yarn 1 Proxy', () => {
});
});
describe('runScript', () => {
it('should execute script `yarn compodoc -- -e json -d .`', () => {
const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue('7.1.0');
yarn1Proxy.runScript('compodoc', ['-e', 'json', '-d', '.']);
expect(executeCommandSpy).toHaveBeenLastCalledWith(
'yarn',
['compodoc', '-e', 'json', '-d', '.'],
undefined,
undefined
);
});
});
describe('addDependencies', () => {
it('with devDep it should run `yarn install -D --ignore-workspace-root-check @storybook/preview-api`', () => {
const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue('');

View File

@ -16,6 +16,10 @@ export class Yarn1Proxy extends JsPackageManager {
return `yarn ${command}`;
}
runScript(command: string, args: string[], cwd?: string): string {
return this.executeCommand(`yarn`, [command, ...args], undefined, cwd);
}
protected getResolutions(packageJson: PackageJson, versions: Record<string, string>) {
return {
resolutions: {

View File

@ -31,6 +31,21 @@ describe('Yarn 2 Proxy', () => {
});
});
describe('runScript', () => {
it('should execute script `yarn compodoc -- -e json -d .`', () => {
const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue('7.1.0');
yarn2Proxy.runScript('compodoc', ['-e', 'json', '-d', '.']);
expect(executeCommandSpy).toHaveBeenLastCalledWith(
'yarn',
['compodoc', '-e', 'json', '-d', '.'],
undefined,
undefined
);
});
});
describe('setRegistryUrl', () => {
it('should run `yarn config set npmRegistryServer https://foo.bar`', () => {
const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue('');

View File

@ -17,6 +17,10 @@ export class Yarn2Proxy extends JsPackageManager {
return `yarn ${command}`;
}
runScript(command: string, args: string[], cwd?: string): string {
return this.executeCommand(`yarn`, [command, ...args], undefined, cwd);
}
protected getResolutions(packageJson: PackageJson, versions: Record<string, string>) {
return {
resolutions: {

View File

@ -69,7 +69,6 @@ export enum ProjectType {
SFC_VUE = 'SFC_VUE',
ANGULAR = 'ANGULAR',
EMBER = 'EMBER',
ALREADY_HAS_STORYBOOK = 'ALREADY_HAS_STORYBOOK',
WEB_COMPONENTS = 'WEB_COMPONENTS',
MITHRIL = 'MITHRIL',
MARIONETTE = 'MARIONETTE',
@ -82,6 +81,7 @@ export enum ProjectType {
RAX = 'RAX',
AURELIA = 'AURELIA',
SERVER = 'SERVER',
NX = 'NX',
}
export enum CoreBuilder {
@ -192,20 +192,6 @@ export const supportedTemplates: TemplateConfiguration[] = [
return dependencies.every(Boolean) || files.every(Boolean);
},
},
{
preset: ProjectType.WEBPACK_REACT,
dependencies: ['react', 'webpack'],
matcherFunction: ({ dependencies }) => {
return dependencies.every(Boolean);
},
},
{
preset: ProjectType.REACT,
dependencies: ['react'],
matcherFunction: ({ dependencies }) => {
return dependencies.every(Boolean);
},
},
{
preset: ProjectType.ANGULAR,
dependencies: ['@angular/core'],
@ -284,6 +270,22 @@ export const supportedTemplates: TemplateConfiguration[] = [
return dependencies.every(Boolean);
},
},
// DO NOT MOVE ANY TEMPLATES BELOW THIS LINE
// React is part of every Template, after Storybook is initialized once
{
preset: ProjectType.WEBPACK_REACT,
dependencies: ['react', 'webpack'],
matcherFunction: ({ dependencies }) => {
return dependencies.every(Boolean);
},
},
{
preset: ProjectType.REACT,
dependencies: ['react'],
matcherFunction: ({ dependencies }) => {
return dependencies.every(Boolean);
},
},
];
// A TemplateConfiguration that matches unsupported frameworks
@ -300,11 +302,7 @@ export const unsupportedTemplate: TemplateConfiguration = {
},
};
const notInstallableProjectTypes: ProjectType[] = [
ProjectType.UNDETECTED,
ProjectType.UNSUPPORTED,
ProjectType.ALREADY_HAS_STORYBOOK,
];
const notInstallableProjectTypes: ProjectType[] = [ProjectType.UNDETECTED, ProjectType.UNSUPPORTED];
export const installableProjectTypes = Object.values(ProjectType)
.filter((type) => !notInstallableProjectTypes.includes(type))

View File

@ -1,10 +1,10 @@
{
"extends": "%SET_DURING_SB_INIT%",
"extends": "../tsconfig.app.json",
"compilerOptions": {
"types": ["node"],
"allowSyntheticDefaultImports": true
},
"exclude": ["../src/test.ts", "../src/**/*.spec.ts", "../projects/**/*.spec.ts"],
"include": ["../src/**/*", "../projects/**/*"],
"exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
"include": ["../src/**/*"],
"files": ["./typings.d.ts"]
}

View File

@ -22,7 +22,17 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/architect@npm:0.1500.5, @angular-devkit/architect@npm:^0.1500.4":
"@angular-devkit/architect@npm:0.1500.4":
version: 0.1500.4
resolution: "@angular-devkit/architect@npm:0.1500.4"
dependencies:
"@angular-devkit/core": 15.0.4
rxjs: 6.6.7
checksum: b6be2ddc3b656cdb86e147238501e6cc00254d9ee711f0dd9c3ae384c1faea84774dcc0b1c11dea489828307e04e081beb0045f12d4d0a879aecabb39d492584
languageName: node
linkType: hard
"@angular-devkit/architect@npm:^0.1500.4":
version: 0.1500.5
resolution: "@angular-devkit/architect@npm:0.1500.5"
dependencies:
@ -33,13 +43,13 @@ __metadata:
linkType: hard
"@angular-devkit/build-angular@npm:^15.0.4":
version: 15.0.5
resolution: "@angular-devkit/build-angular@npm:15.0.5"
version: 15.0.4
resolution: "@angular-devkit/build-angular@npm:15.0.4"
dependencies:
"@ampproject/remapping": 2.2.0
"@angular-devkit/architect": 0.1500.5
"@angular-devkit/build-webpack": 0.1500.5
"@angular-devkit/core": 15.0.5
"@angular-devkit/architect": 0.1500.4
"@angular-devkit/build-webpack": 0.1500.4
"@angular-devkit/core": 15.0.4
"@babel/core": 7.20.2
"@babel/generator": 7.20.4
"@babel/helper-annotate-as-pure": 7.18.6
@ -50,7 +60,7 @@ __metadata:
"@babel/runtime": 7.20.1
"@babel/template": 7.18.10
"@discoveryjs/json-ext": 0.5.7
"@ngtools/webpack": 15.0.5
"@ngtools/webpack": 15.0.4
ansi-colors: 4.1.3
autoprefixer: 10.4.13
babel-loader: 9.1.0
@ -124,20 +134,38 @@ __metadata:
optional: true
tailwindcss:
optional: true
checksum: 067685257b42f89ba6407846b3cac2ed28aad91541cb6cefdd55e29e6d0bc844321013bb2b47cbfe0b06495cd54e568f355ebf56ef7635301a8379be5bd68771
checksum: 2c3e835bbac7715d1cf1cb1e27af6f09ad1e8eeb389e18282e0991751e4902c05d017c9d5d836ef2e0f38cfc6540a7ef72d8ed12962d1138c8ebc7f7fee8ac20
languageName: node
linkType: hard
"@angular-devkit/build-webpack@npm:0.1500.5":
version: 0.1500.5
resolution: "@angular-devkit/build-webpack@npm:0.1500.5"
"@angular-devkit/build-webpack@npm:0.1500.4":
version: 0.1500.4
resolution: "@angular-devkit/build-webpack@npm:0.1500.4"
dependencies:
"@angular-devkit/architect": 0.1500.5
"@angular-devkit/architect": 0.1500.4
rxjs: 6.6.7
peerDependencies:
webpack: ^5.30.0
webpack-dev-server: ^4.0.0
checksum: 06438ae79c1fcebbb170a5ec27c3946dfa4f4cd8ed80588deeefc54ab4550b2d779119eb7949e1477c44c3e29e8ca85369cb430c2833295b4823495a4c725052
checksum: 5aeb788acbed900c9fa2985d178ba1248e842fd175a5f1e2f4ef5a833e812e7cb0ddc7344fe2b208e0f58ad15c9db4fc8ff6fc9408973df992478cbd84df2250
languageName: node
linkType: hard
"@angular-devkit/core@npm:15.0.4":
version: 15.0.4
resolution: "@angular-devkit/core@npm:15.0.4"
dependencies:
ajv: 8.11.0
ajv-formats: 2.1.1
jsonc-parser: 3.2.0
rxjs: 6.6.7
source-map: 0.7.4
peerDependencies:
chokidar: ^3.5.2
peerDependenciesMeta:
chokidar:
optional: true
checksum: 428a20d96e237d24a62ae3d5284e3140f906e212883e094cc64ec622a5065234f001bd21f9608ad2478088c1cc40aadf48bd53c399ad02be0c98b61d27d3ba11
languageName: node
linkType: hard
@ -159,27 +187,27 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/schematics@npm:15.0.5":
version: 15.0.5
resolution: "@angular-devkit/schematics@npm:15.0.5"
"@angular-devkit/schematics@npm:15.0.4":
version: 15.0.4
resolution: "@angular-devkit/schematics@npm:15.0.4"
dependencies:
"@angular-devkit/core": 15.0.5
"@angular-devkit/core": 15.0.4
jsonc-parser: 3.2.0
magic-string: 0.26.7
ora: 5.4.1
rxjs: 6.6.7
checksum: c008f7dcb0c722b7a7f7a2a4b69c45cf75ac0a7c0c8fbdb44453bd930ddca9692965a7d6de276201d78d10c5e1c6a04fe494f7cdfbdcd51b13186543e159e8c0
checksum: b53397b5770987bc98f697bcde56cef23b0958e4ebcdf7713c7ba8c56295869a069259fdbb480495e9ee79dae342ce88b27b64f518eeb974a38c981c4602f726
languageName: node
linkType: hard
"@angular/cli@npm:^15.0.4":
version: 15.0.5
resolution: "@angular/cli@npm:15.0.5"
version: 15.0.4
resolution: "@angular/cli@npm:15.0.4"
dependencies:
"@angular-devkit/architect": 0.1500.5
"@angular-devkit/core": 15.0.5
"@angular-devkit/schematics": 15.0.5
"@schematics/angular": 15.0.5
"@angular-devkit/architect": 0.1500.4
"@angular-devkit/core": 15.0.4
"@angular-devkit/schematics": 15.0.4
"@schematics/angular": 15.0.4
"@yarnpkg/lockfile": 1.1.0
ansi-colors: 4.1.3
ini: 3.0.1
@ -196,7 +224,7 @@ __metadata:
yargs: 17.6.2
bin:
ng: bin/ng.js
checksum: 7cb92b2f0ed2f1dea4b3c44e6cefe4ebb6e04cfb98c622daf4815888645c00bdd0d5acf1e4a12ee9a122ccc2b55864aedeabe88aeedcd83d2a33d39a27be86a2
checksum: 1443a089fd99b47ddfe53a9ebcbd10a56357a43bdb25597f0dd6e540508bea9fd74e3b640969b2c5d1eba346fd189426bc355109e2a1fedffa76702f4b8aabb5
languageName: node
linkType: hard
@ -1275,13 +1303,13 @@ __metadata:
linkType: hard
"@babel/plugin-transform-block-scoping@npm:^7.20.2, @babel/plugin-transform-block-scoping@npm:^7.8.3":
version: 7.20.11
resolution: "@babel/plugin-transform-block-scoping@npm:7.20.11"
version: 7.20.8
resolution: "@babel/plugin-transform-block-scoping@npm:7.20.8"
dependencies:
"@babel/helper-plugin-utils": ^7.20.2
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 3840c342c5ef6c53c750bf3801c30b3770b016516b4589d164e227688ed2dd0aa86496ac340b0735b9fa0cee30ff5338f1e291b2a91df5cce17e585298674e8b
checksum: 6c324f45b889e1de02f1f60b748d2de3b71dc90b9b2075e38f008e7363825fad1a4894bda8bd2eb632f68e351e11451ed86b5e97b081ed90a30390585675b27f
languageName: node
linkType: hard
@ -1421,33 +1449,33 @@ __metadata:
linkType: hard
"@babel/plugin-transform-modules-amd@npm:^7.13.0, @babel/plugin-transform-modules-amd@npm:^7.19.6":
version: 7.20.11
resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11"
version: 7.20.7
resolution: "@babel/plugin-transform-modules-amd@npm:7.20.7"
dependencies:
"@babel/helper-module-transforms": ^7.20.11
"@babel/helper-plugin-utils": ^7.20.2
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 327077cc746d2ef14d0792a970058d9b7170ff480c1d1d7acf874ef7cfeae0c680e86a45896ea27066e9ebdd82dc2be09d321385eef1e0b4255659d75ea2e008
checksum: 85973356d2183711ebe3338899d726070ea4f06c9fccdd2808d80337a791935b409605e4c610660a159bb4d495332d2e33b387c0384c165f898fe0107c1ddb88
languageName: node
linkType: hard
"@babel/plugin-transform-modules-commonjs@npm:^7.13.8, @babel/plugin-transform-modules-commonjs@npm:^7.19.6, @babel/plugin-transform-modules-commonjs@npm:^7.2.0":
version: 7.20.11
resolution: "@babel/plugin-transform-modules-commonjs@npm:7.20.11"
version: 7.20.7
resolution: "@babel/plugin-transform-modules-commonjs@npm:7.20.7"
dependencies:
"@babel/helper-module-transforms": ^7.20.11
"@babel/helper-plugin-utils": ^7.20.2
"@babel/helper-simple-access": ^7.20.2
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: f3a3281c252a978255076ff7274e4ac1ec252e0db4b3d73122c278ce9fd8318179fc804638ce726870146fa0845e2559711453ce7a391dc2a792d96dc0f6b04c
checksum: 57024919a30796a4b087e78a9ac14d31a6ba43c6fdd38f55e1fce25a932e660f4b898037480f9b4dc73faae2f2846b0faa73697a0819e0382f58759ff6b3f732
languageName: node
linkType: hard
"@babel/plugin-transform-modules-systemjs@npm:^7.19.6":
version: 7.20.11
resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11"
version: 7.19.6
resolution: "@babel/plugin-transform-modules-systemjs@npm:7.19.6"
dependencies:
"@babel/helper-hoist-variables": ^7.18.6
"@babel/helper-module-transforms": ^7.20.11
@ -1455,7 +1483,7 @@ __metadata:
"@babel/helper-validator-identifier": ^7.19.1
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 1843b2044b711765581d6130ea7901afde6e6f5af4e4219ab675033a090f4dacb6656bfada8f211a2cd9bbae256c7f4bd0b8613b750e56674feee5252de1ad76
checksum: 0f05058170f1d2027bda95ae8d57b021698f4d7f33df859c95db072ae80941079c5049ac12bde3bc87311436e9451e5edca8205754e9a4e5b54bd6e4f3ecf2ed
languageName: node
linkType: hard
@ -1996,7 +2024,25 @@ __metadata:
languageName: node
linkType: hard
"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.20.1, @babel/traverse@npm:^7.20.10, @babel/traverse@npm:^7.20.12, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.4.5, @babel/traverse@npm:^7.7.0, @babel/traverse@npm:^7.7.2, @babel/traverse@npm:^7.8.6":
"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.20.1, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.4.5, @babel/traverse@npm:^7.7.0, @babel/traverse@npm:^7.7.2, @babel/traverse@npm:^7.8.6":
version: 7.20.8
resolution: "@babel/traverse@npm:7.20.8"
dependencies:
"@babel/code-frame": ^7.18.6
"@babel/generator": ^7.20.7
"@babel/helper-environment-visitor": ^7.18.9
"@babel/helper-function-name": ^7.19.0
"@babel/helper-hoist-variables": ^7.18.6
"@babel/helper-split-export-declaration": ^7.18.6
"@babel/parser": ^7.20.7
"@babel/types": ^7.20.7
debug: ^4.1.0
globals: ^11.1.0
checksum: aef74e2b334b5c92a224dbe68357ba2383d43804fb7a5c6e76ca477d6640ddd3f428280687fdd413d6729a00d3f61f6ba86acd91a18d75126e107d9db91f008c
languageName: node
linkType: hard
"@babel/traverse@npm:^7.20.10, @babel/traverse@npm:^7.20.12":
version: 7.20.12
resolution: "@babel/traverse@npm:7.20.12"
dependencies:
@ -2014,6 +2060,24 @@ __metadata:
languageName: node
linkType: hard
"@babel/traverse@npm:^7.20.7":
version: 7.20.10
resolution: "@babel/traverse@npm:7.20.10"
dependencies:
"@babel/code-frame": ^7.18.6
"@babel/generator": ^7.20.7
"@babel/helper-environment-visitor": ^7.18.9
"@babel/helper-function-name": ^7.19.0
"@babel/helper-hoist-variables": ^7.18.6
"@babel/helper-split-export-declaration": ^7.18.6
"@babel/parser": ^7.20.7
"@babel/types": ^7.20.7
debug: ^4.1.0
globals: ^11.1.0
checksum: a712402374c2e1cdd7e7880deda0f0051123c09abc9a110e4594bf90c858211e678185b927dffe8780de981ff87ac98bcffdc3fbf46c262bd21b6d64cd1d3b58
languageName: node
linkType: hard
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.11.5, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.8, @babel/types@npm:^7.18.9, @babel/types@npm:^7.19.0, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.6.1, @babel/types@npm:^7.7.0, @babel/types@npm:^7.7.2, @babel/types@npm:^7.8.3, @babel/types@npm:^7.8.6, @babel/types@npm:^7.8.7, @babel/types@npm:^7.9.6":
version: 7.20.7
resolution: "@babel/types@npm:7.20.7"
@ -4022,14 +4086,14 @@ __metadata:
languageName: node
linkType: hard
"@ngtools/webpack@npm:15.0.5":
version: 15.0.5
resolution: "@ngtools/webpack@npm:15.0.5"
"@ngtools/webpack@npm:15.0.4":
version: 15.0.4
resolution: "@ngtools/webpack@npm:15.0.4"
peerDependencies:
"@angular/compiler-cli": ^15.0.0
typescript: ~4.8.2
webpack: ^5.54.0
checksum: 8314dbba50bdbbc396baad9408c99aa56230de833e32398821f4b7c03f9db87d03fafbc1c098780172b42e92f71c0b86a6e25b0c1acba79037d14872108f125d
checksum: 978fa56a13a86974d678c57344a5e4390cddf29fadb4adb1e1012210b77c4c91a581af9d2488f164258ce6a03918b60585c3c559523bf6c6a2bf415c11990c07
languageName: node
linkType: hard
@ -4961,14 +5025,14 @@ __metadata:
languageName: node
linkType: hard
"@schematics/angular@npm:15.0.5":
version: 15.0.5
resolution: "@schematics/angular@npm:15.0.5"
"@schematics/angular@npm:15.0.4":
version: 15.0.4
resolution: "@schematics/angular@npm:15.0.4"
dependencies:
"@angular-devkit/core": 15.0.5
"@angular-devkit/schematics": 15.0.5
"@angular-devkit/core": 15.0.4
"@angular-devkit/schematics": 15.0.4
jsonc-parser: 3.2.0
checksum: 1c978e1fde5b4028be4e5a2a632f32c8b1330bb85d27b1bf80cb62515905a77bf36bce224691c9a9b1d002c7bbf2352993181dd0c7f3eee6348ea933ec9cf991
checksum: 4b5475acaeddb31d424e3911014d12e85bf75caff267c2d9038e967ce7f3cff2d172fef63a88e3f40bbb9cf4f071d7a1fcbc2745f9ec432c5e5b96128924b576
languageName: node
linkType: hard
@ -11474,9 +11538,9 @@ __metadata:
linkType: hard
"caniuse-lite@npm:^1.0.30001400, caniuse-lite@npm:^1.0.30001406, caniuse-lite@npm:^1.0.30001426":
version: 1.0.30001442
resolution: "caniuse-lite@npm:1.0.30001442"
checksum: 02d10a1d6a83cc88771a42ea5aa0c98f5768180c69badeec2bf40d269bbcbd0239a3c197af5e5dce51d0bd5d04e069394cbe611f489082c8244ab95f597e1842
version: 1.0.30001441
resolution: "caniuse-lite@npm:1.0.30001441"
checksum: 4b91bfc03cdbb9cf54225bbc36c2c568879d05ff8f2a34bdafbd7e5acc578d913b2d169bab4bf8a0992678e308779cd5603be0928d6552acefebfc52ded73aa1
languageName: node
linkType: hard
@ -12419,11 +12483,11 @@ __metadata:
linkType: hard
"core-js-compat@npm:^3.25.1":
version: 3.27.1
resolution: "core-js-compat@npm:3.27.1"
version: 3.26.1
resolution: "core-js-compat@npm:3.26.1"
dependencies:
browserslist: ^4.21.4
checksum: 635ffcc3f40ca4cb45b10ea805ebf57bbba69873014c89bec96ae09f15cbd3012bd5102bdf87551ef2f1c629e279837632200c4888428035cf822f6e40dc383b
checksum: 2d798049758900a7fd83958e02d1d46d35157dbaa6f916c358f735bfe3095cdf5b54dc999363e654445a94417005eb548b9acc47e1d18eda3d1a43cf05b350a5
languageName: node
linkType: hard
@ -20376,11 +20440,11 @@ __metadata:
linkType: hard
"memfs@npm:^3.4.1, memfs@npm:^3.4.3":
version: 3.4.13
resolution: "memfs@npm:3.4.13"
version: 3.4.12
resolution: "memfs@npm:3.4.12"
dependencies:
fs-monkey: ^1.0.3
checksum: f14ab3ff938eacf688577d1b0f7bf77ca3a05d4df9c335b024ed6790e6b224b569cc4b61c1de604c0420a0fac6b3fbf3f283c72fd2be9ce395534539599ac63b
checksum: 60c44a3887fb02fc2f625dca0f801db5174cd9990ebb6aa5871b6800c442f2b5d1d900168db3a717c380dda76e3c891afc4623ac3a1568a2978bf05bbf727f23
languageName: node
linkType: hard

View File

@ -5,24 +5,4 @@
npx storybook init
```
- Update your `angular.json` file to include Storybook's custom builder:
```json
{
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"browserTarget": "angular-cli:build",
"port": 6006
}
},
"build-storybook": {
"builder": "@storybook/angular:build-storybook",
"options": {
"browserTarget": "angular-cli:build"
}
}
}
```
If you run into issues with the installation, check the [Troubleshooting section](#troubleshooting) below for guidance on how to solve it.

View File

@ -1,20 +1,15 @@
import { readJSON, writeJSON } from 'fs-extra';
import { join } from 'path';
const logger = console;
export async function updatePackageScripts({ cwd, prefix }: { cwd: string; prefix: string }) {
logger.info(`🔢 Adding package scripts:`);
const packageJsonPath = join(cwd, 'package.json');
const packageJson = await readJSON(packageJsonPath);
packageJson.scripts = {
...packageJson.scripts,
storybook: packageJson.scripts.storybook.replace(/(npx )?storybook/, `${prefix} storybook`),
'build-storybook': packageJson.scripts['build-storybook'].replace(
/(npx )?storybook/,
`${prefix} storybook`
),
...(packageJson.scripts.storybook && {
storybook: `${prefix} ${packageJson.scripts.storybook}`,
'build-storybook': `${prefix} ${packageJson.scripts['build-storybook']}`,
}),
// See comment in combine-compodoc as to why this is necessary
...(packageJson.scripts['docs:json'] && {
'docs:json': 'DIR=$PWD; cd ../../scripts; yarn ts-node combine-compodoc $DIR',