cleanup CLI a bit. change CLI so it tries to match to a known framework

add a --local flag for testing the CLI locally without needing verdaccio
This commit is contained in:
Norbert de Langen 2022-05-30 17:55:44 +02:00
parent 2a37cf0c1f
commit 250b03250b
No known key found for this signature in database
GPG Key ID: FD0E78AF9A837762
29 changed files with 225 additions and 277 deletions

View File

@ -35,6 +35,7 @@
"dependencies": {
"@storybook/addons": "6.5.0-rc.1",
"@storybook/api": "6.5.0-rc.1",
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/client-logger": "6.5.0-rc.1",
"@storybook/core-client": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",

View File

@ -29,6 +29,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-client": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",

View File

@ -32,6 +32,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/html": "6.5.0-rc.1",

View File

@ -32,6 +32,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/preact": "6.5.0-rc.1",

View File

@ -33,6 +33,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/preset-react-webpack": "6.5.0-rc.1",
"@storybook/react": "6.5.0-rc.1",

View File

@ -32,6 +32,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/preset-server-webpack": "6.5.0-rc.1",

View File

@ -33,6 +33,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/preset-svelte-webpack": "6.5.0-rc.1",

View File

@ -32,6 +32,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/preset-vue-webpack": "6.5.0-rc.1",

View File

@ -32,6 +32,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/preset-vue3-webpack": "6.5.0-rc.1",

View File

@ -35,6 +35,7 @@
},
"dependencies": {
"@babel/preset-env": "^7.12.11",
"@storybook/builder-webpack5": "6.5.0-rc.1",
"@storybook/core-common": "6.5.0-rc.1",
"@storybook/core-server": "6.5.0-rc.1",
"@storybook/preset-web-components-webpack": "6.5.0-rc.1",

View File

@ -60,6 +60,7 @@
"core-js": "^3.8.2",
"cross-spawn": "^7.0.3",
"envinfo": "^7.7.3",
"execa": "^5.0.0",
"express": "^4.17.1",
"find-up": "^5.0.0",
"fs-extra": "^9.0.1",

View File

@ -26,12 +26,6 @@ jest.mock('path', () => ({
}));
const MOCK_FRAMEWORK_FILES = [
{
name: ProjectType.METEOR,
files: {
'.meteor': 'file content',
},
},
{
name: ProjectType.SFC_VUE,
files: {
@ -337,14 +331,6 @@ describe('Detect', () => {
})
).toBe(ProjectType.ALREADY_HAS_STORYBOOK);
});
it('UPDATE_PACKAGE_ORGANIZATIONS if legacy lib is detected', () => {
expect(
isStorybookInstalled({
devDependencies: { '@kadira/storybook': '4.0.0-alpha.21' },
})
).toBe(ProjectType.UPDATE_PACKAGE_ORGANIZATIONS);
});
});
describe('detectFrameworkPreset should return', () => {

View File

@ -5,7 +5,7 @@ import findUp from 'find-up';
import {
ProjectType,
supportedTemplates,
SUPPORTED_FRAMEWORKS,
SUPPORTED_RENDERERS,
SupportedLanguage,
TemplateConfiguration,
TemplateMatcher,
@ -131,7 +131,7 @@ export function isStorybookInstalled(dependencies: PackageJson | false, force?:
if (!force && dependencies.devDependencies) {
if (
SUPPORTED_FRAMEWORKS.reduce(
SUPPORTED_RENDERERS.reduce(
(storybookPresent, framework) =>
storybookPresent || !!dependencies.devDependencies[`@storybook/${framework}`],
false
@ -139,13 +139,6 @@ export function isStorybookInstalled(dependencies: PackageJson | false, force?:
) {
return ProjectType.ALREADY_HAS_STORYBOOK;
}
if (
dependencies.devDependencies['@kadira/storybook'] ||
dependencies.devDependencies['@kadira/react-native-storybook']
) {
return ProjectType.UPDATE_PACKAGE_ORGANIZATIONS;
}
}
return false;
}

View File

@ -117,14 +117,15 @@ program
program
.command('repro [outputDirectory]')
.description('Create a reproduction from a set of possible templates')
.option('-f --framework <framework>', 'Filter on given framework')
.option('-f --renderer <renderer>', 'Filter on given renderer')
.option('-t --template <template>', 'Use the given template')
.option('-l --list', 'List available templates')
.option('-g --generator <generator>', 'Use custom generator command')
.option('--pnp', "Use Yarn Plug'n'Play mode instead of node_modules one")
.option('--local', "use storybook's local packages instead of yarn's registry")
.option('--e2e', 'Used in e2e context')
.action((outputDirectory, { framework, template, list, e2e, generator, pnp }) =>
repro({ outputDirectory, framework, template, list, e2e, generator, pnp }).catch((e) => {
.action((outputDirectory, { renderer, template, list, e2e, generator, pnp, local }) =>
repro({ outputDirectory, renderer, template, list, e2e, local, generator, pnp }).catch((e) => {
logger.error(e);
process.exit(1);
})

View File

@ -1,29 +0,0 @@
import fs from 'fs';
import JSON5 from 'json5';
import { baseGenerator, Generator } from '../baseGenerator';
const generator: Generator = async (packageManager, npmOptions, options) => {
await baseGenerator(packageManager, npmOptions, options, 'react', {
extraPackages: ['react', 'react-dom', '@babel/preset-env', '@babel/preset-react'],
staticDir: 'dist',
});
// create or update .babelrc
let babelrc = null;
if (fs.existsSync('.babelrc')) {
const babelrcContent = fs.readFileSync('.babelrc', 'utf8');
babelrc = JSON5.parse(babelrcContent);
babelrc.plugins = babelrc.plugins || [];
} else {
babelrc = {
presets: [
['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3' }],
'@babel/preset-react',
],
};
}
fs.writeFileSync('.babelrc', JSON.stringify(babelrc, null, 2), 'utf8');
};
export default generator;

View File

@ -48,7 +48,7 @@ const generator = async (
installServer && '@storybook/react-native-server',
].filter(Boolean);
const resolvedPackages = await packageManager.getVersionedPackages(...packagesToResolve);
const resolvedPackages = await packageManager.getVersionedPackages(packagesToResolve);
const babelDependencies = await getBabelDependencies(packageManager, packageJson);

View File

@ -6,14 +6,14 @@ import { baseGenerator, Generator } from '../baseGenerator';
import { CoreBuilder } from '../../project_types';
const generator: Generator = async (packageManager, npmOptions, options) => {
const monorepoRootPath = path.join(__dirname, '..', '..', '..', '..', '..', '..');
const extraMain = options.linkable
? {
webpackFinal: `%%(config) => {
const path = require('path');
// add monorepo root as a valid directory to import modules from
config.resolve.plugins.forEach((p) => {
if (Array.isArray(p.appSrcs)) {
p.appSrcs.push(path.join(__dirname, '..', '..', '..', 'storybook'));
p.appSrcs.push('${monorepoRootPath}');
}
});
return config;

View File

@ -1,74 +0,0 @@
/* eslint-disable no-param-reassign */
import path from 'path';
import { sync as spawnSync } from 'cross-spawn';
import { packageNames } from '@storybook/codemod';
import { getBabelDependencies } from '../../helpers';
import { NpmOptions } from '../../NpmOptions';
import { JsPackageManager, PackageJson, writePackageJson } from '../../js-package-manager';
async function updatePackage(
packageManager: JsPackageManager,
devDependencies: PackageJson['devDependencies'],
oldName: string,
newName: string
) {
if (devDependencies[oldName]) {
delete devDependencies[oldName];
devDependencies[newName] = await packageManager.getVersion(newName);
}
}
async function updatePackageJson(packageManager: JsPackageManager, npmOptions: NpmOptions) {
const packageJson = packageManager.retrievePackageJson();
const { devDependencies } = packageJson;
const [actionsVersion, linksVersion] = await packageManager.getVersions(
'@storybook/addon-actions',
'@storybook/addon-links'
);
devDependencies['@storybook/addon-actions'] = actionsVersion;
devDependencies['@storybook/addon-links'] = linksVersion;
await Promise.all(
Object.keys(packageNames).map((oldName) => {
const newName = packageNames[oldName];
return updatePackage(packageManager, devDependencies, oldName, newName);
})
);
if (!devDependencies['@storybook/react'] && !devDependencies['@storybook/react-native']) {
throw new Error('Expected to find `@kadira/[react-native]-storybook` in devDependencies');
}
writePackageJson(packageJson);
const babelDependencies = await getBabelDependencies(packageManager, packageJson);
if (babelDependencies.length > 0) {
packageManager.addDependencies({ ...npmOptions, packageJson }, babelDependencies);
}
}
function updateSourceCode(parser: string) {
const jscodeshiftPath = path.dirname(require.resolve('jscodeshift'));
const jscodeshiftCommand = path.join(jscodeshiftPath, 'bin', 'jscodeshift.sh');
['update-organisation-name.js', 'move-builtin-addons.js'].forEach((codemod) => {
const codemodPath = path.join(
path.dirname(require.resolve('@storybook/codemod')),
'transforms',
codemod
);
const args = ['-t', codemodPath, '--silent', '--ignore-pattern', '"node_modules|dist"', '.'];
if (parser) args.push('--parser', parser);
spawnSync(jscodeshiftCommand, args, { stdio: 'inherit' });
});
}
export default async (packageManager: JsPackageManager, parser: string, npmOptions: NpmOptions) => {
await updatePackageJson(packageManager, npmOptions);
updateSourceCode(parser);
};

View File

@ -1,7 +1,7 @@
import fse from 'fs-extra';
import dedent from 'ts-dedent';
import { NpmOptions } from '../NpmOptions';
import { SupportedLanguage, SupportedFrameworks, Builder, CoreBuilder } from '../project_types';
import { SupportedLanguage, SupportedRenderers, Builder, CoreBuilder } from '../project_types';
import { getBabelDependencies, copyComponents } from '../helpers';
import { configure } from './configure';
import { getPackageDetails, JsPackageManager } from '../js-package-manager';
@ -46,31 +46,66 @@ const defaultOptions: FrameworkOptions = {
commonJs: false,
};
const builderDependencies = (builder: Builder) => {
switch (builder) {
case CoreBuilder.Webpack5:
return ['@storybook/builder-webpack5', '@storybook/manager-webpack5'];
case CoreBuilder.Vite:
return ['@storybook/builder-vite'];
default:
return [builder];
const getBuilderDetails = (builder: string) => {
const map = packageVersions as Record<string, string>;
if (map[builder]) {
return builder;
}
const builderPackage = `@storybook/${builder}`;
if (map[builderPackage]) {
return builderPackage;
}
return builder;
};
const getFrameworkDetails = (
renderer: SupportedRenderers,
builder: Builder
): { type: 'framework' | 'renderer'; package: string; builder: string } => {
const frameworkPackage = `@storybook/${renderer}-${builder}`;
const rendererPackage = `@storybook/${renderer}`;
const isKnownFramework = !!(packageVersions as Record<string, string>)[frameworkPackage];
const isKnownRenderer = !!(packageVersions as Record<string, string>)[rendererPackage];
const builderPackage = getBuilderDetails(builder);
if (isKnownFramework) {
return {
package: frameworkPackage,
builder: builderPackage,
type: 'framework',
};
}
if (isKnownRenderer) {
return {
package: rendererPackage,
builder: builderPackage,
type: 'renderer',
};
}
throw new Error(
`Could not find the framework (${frameworkPackage}) or renderer (${rendererPackage}) package`
);
};
const stripVersions = (addons: string[]) => addons.map((addon) => getPackageDetails(addon)[0]);
const hasInteractiveStories = (framework: SupportedFrameworks) =>
const hasInteractiveStories = (framework: SupportedRenderers) =>
['react', 'angular', 'preact', 'svelte', 'vue', 'vue3', 'html'].includes(framework);
export async function baseGenerator(
packageManager: JsPackageManager,
npmOptions: NpmOptions,
{ language, builder = 'webpack5' }: GeneratorOptions,
framework: SupportedFrameworks,
{ language, builder = CoreBuilder.Webpack5 }: GeneratorOptions,
renderer: SupportedRenderers,
options: FrameworkOptions = defaultOptions
) {
const {
extraAddons,
extraAddons: extraAddonPackages,
extraPackages,
staticDir,
addScripts,
@ -90,79 +125,78 @@ export async function baseGenerator(
// added to package.json
const addonPackages = [...addons, '@storybook/addon-actions'];
if (hasInteractiveStories(framework)) {
if (hasInteractiveStories(renderer)) {
addons.push('@storybook/addon-interactions');
addonPackages.push('@storybook/addon-interactions', '@storybook/testing-library');
}
const yarn2Dependencies =
const yarn2ExtraPackages =
packageManager.type === 'yarn2' ? ['@storybook/addon-docs', '@mdx-js/react@1.x.x'] : [];
const files = await fse.readdir(process.cwd());
const isNewFolder = !files.some(
(fname) => fname.startsWith('.babel') || fname.startsWith('babel') || fname === 'package.json'
);
const packageJson = packageManager.retrievePackageJson();
const installedDependencies = new Set(Object.keys(packageJson.dependencies));
const frameworkPackage = (packageVersions as Record<string, string>)[
`@storybook/${framework}-${builder}`
]
? `@storybook/${framework}-${builder}`
: framework;
const cliPackage = 'sb';
const installedDependencies = new Set(
Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies })
);
const {
package: frameworkPackage,
type,
builder: builderPackage,
} = getFrameworkDetails(renderer, builder);
// temp
if (type === 'renderer') {
throw new Error(
dedent`
Sorry, for now, you can not do this, please use a framework such as @storybook/react-webpack5
https://github.com/storybookjs/storybook/issues/18360
`
);
}
const packages = [
cliPackage,
'sb',
frameworkPackage,
...addonPackages,
...extraPackages,
...extraAddons,
...yarn2Dependencies,
...builderDependencies(builder),
...extraAddonPackages,
...yarn2ExtraPackages,
...(type === 'framework' ? [] : [builderPackage]),
]
.filter(Boolean)
.filter(
(packageToInstall) => !installedDependencies.has(getPackageDetails(packageToInstall)[0])
);
const versionedPackages = await packageManager.getVersionedPackages(...packages);
const versionedPackages = await packageManager.getVersionedPackages(packages);
console.log({ versionedPackages });
const coreBuilders = [CoreBuilder.Webpack5, CoreBuilder.Vite] as string[];
const expandedBuilder = coreBuilders.includes(builder)
? `@storybook/builder-${builder}`
: builder;
const mainOptions =
builder !== CoreBuilder.Webpack5
type !== 'framework'
? {
core: {
builder: expandedBuilder,
builder: builderPackage,
},
...extraMain,
}
: extraMain;
// Default vite builder to storyStoreV7
if (expandedBuilder === '@storybook/builder-vite') {
mainOptions.features = {
...mainOptions.features,
storyStoreV7: true,
};
}
configure(framework, {
configure(renderer, {
framework: { name: frameworkPackage, options: {} },
addons: [...addons, ...stripVersions(extraAddons)],
addons: [...addons, ...stripVersions(extraAddonPackages)],
extensions,
commonJs: options.commonJs,
...mainOptions,
});
if (addComponents) {
copyComponents(framework, language);
copyComponents(renderer, language);
}
// FIXME: temporary workaround for https://github.com/storybookjs/storybook/issues/17516
if (expandedBuilder === '@storybook/builder-vite') {
if (builderPackage === '@storybook/builder-vite') {
const previewHead = dedent`
<script>
window.global = window;
@ -172,6 +206,9 @@ export async function baseGenerator(
}
const babelDependencies = addBabel ? await getBabelDependencies(packageManager, packageJson) : [];
const isNewFolder = !files.some(
(fname) => fname.startsWith('.babel') || fname.startsWith('babel') || fname === 'package.json'
);
if (isNewFolder) {
await generateStorybookBabelConfigInCWD();
}

View File

@ -1,6 +1,6 @@
import fse from 'fs-extra';
import dedent from 'ts-dedent';
import { SupportedFrameworks } from '../project_types';
import { SupportedRenderers } from '../project_types';
interface ConfigureMainOptions {
addons: string[];
@ -44,7 +44,7 @@ function configureMain({
});
}
const frameworkToPreviewParts: Partial<Record<SupportedFrameworks, any>> = {
const frameworkToPreviewParts: Partial<Record<SupportedRenderers, any>> = {
angular: {
prefix: dedent`
import { setCompodocJson } from "@storybook/addon-docs/angular";
@ -56,7 +56,7 @@ const frameworkToPreviewParts: Partial<Record<SupportedFrameworks, any>> = {
},
};
function configurePreview(framework: SupportedFrameworks, commonJs: boolean) {
function configurePreview(framework: SupportedRenderers, commonJs: boolean) {
const { prefix = '', extraParameters = '' } = frameworkToPreviewParts[framework] || {};
const previewPath = `./.storybook/preview.${commonJs ? 'cjs' : 'js'}`;
@ -83,7 +83,7 @@ function configurePreview(framework: SupportedFrameworks, commonJs: boolean) {
fse.writeFileSync(previewPath, preview, { encoding: 'utf8' });
}
export function configure(framework: SupportedFrameworks, mainOptions: ConfigureMainOptions) {
export function configure(framework: SupportedRenderers, mainOptions: ConfigureMainOptions) {
fse.ensureDirSync('./.storybook');
configureMain(mainOptions);

View File

@ -6,7 +6,7 @@ import chalk from 'chalk';
import { satisfies } from '@storybook/semver';
import stripJsonComments from 'strip-json-comments';
import { SupportedFrameworks, SupportedLanguage } from './project_types';
import { SupportedRenderers, SupportedLanguage } from './project_types';
import { JsPackageManager, PackageJson, PackageJsonWithDepsAndDevDeps } from './js-package-manager';
const logger = console;
@ -179,7 +179,7 @@ export function copyTemplate(templateRoot: string) {
fse.copySync(templateDir, '.', { overwrite: true });
}
export function copyComponents(framework: SupportedFrameworks, language: SupportedLanguage) {
export function copyComponents(framework: SupportedRenderers, language: SupportedLanguage) {
const languageFolderMapping: Record<SupportedLanguage, string> = {
javascript: 'js',
typescript: 'ts',

View File

@ -8,12 +8,10 @@ import { commandLog, codeLog, paddedLog } from './helpers';
import angularGenerator from './generators/ANGULAR';
import aureliaGenerator from './generators/AURELIA';
import emberGenerator from './generators/EMBER';
import meteorGenerator from './generators/METEOR';
import reactGenerator from './generators/REACT';
import reactNativeGenerator from './generators/REACT_NATIVE';
import reactScriptsGenerator from './generators/REACT_SCRIPTS';
import sfcVueGenerator from './generators/SFC_VUE';
import updateOrganisationsGenerator from './generators/UPDATE_PACKAGE_ORGANIZATIONS';
import vueGenerator from './generators/VUE';
import vue3Generator from './generators/VUE3';
import webpackReactGenerator from './generators/WEBPACK_REACT';
@ -35,7 +33,7 @@ const logger = console;
type CommandOptions = {
useNpm?: boolean;
type?: any;
type?: ProjectType;
force?: any;
html?: boolean;
skipInstall?: boolean;
@ -66,7 +64,7 @@ const installStorybook = (
commonJs: options.commonJs,
};
const runGenerator: () => Promise<void> = () => {
const runGenerator: () => Promise<void> = async () => {
switch (projectType) {
case ProjectType.ALREADY_HAS_STORYBOOK:
logger.log();
@ -78,11 +76,6 @@ const installStorybook = (
logger.log();
return Promise.resolve();
case ProjectType.UPDATE_PACKAGE_ORGANIZATIONS:
return updateOrganisationsGenerator(packageManager, options.parser, npmOptions)
.then(() => null) // commandLog doesn't like to see output
.then(commandLog('Upgrading your project to the new Storybook packages.\n'));
case ProjectType.REACT_SCRIPTS:
return reactScriptsGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Create React App" based project')
@ -111,11 +104,6 @@ const installStorybook = (
.then(commandLog('Adding Storybook support to your "React Native" app\n'));
}
case ProjectType.METEOR:
return meteorGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Meteor" app\n')
);
case ProjectType.WEBPACK_REACT:
return webpackReactGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Webpack React" app\n')
@ -315,7 +303,7 @@ export async function initiate(options: CommandOptions, pkg: Package): Promise<v
}
done();
await installStorybook(projectType, packageManager, {
await installStorybook(projectType as ProjectType, packageManager, {
...options,
...(isEsm ? { commonJs: true } : undefined),
});

View File

@ -135,7 +135,7 @@ export abstract class JsPackageManager {
*
* @param packages
*/
public getVersionedPackages(...packages: string[]): Promise<string[]> {
public getVersionedPackages(packages: string[]): Promise<string[]> {
return Promise.all(
packages.map(async (pkg) => {
const [packageName, packageVersion] = getPackageDetails(pkg);
@ -151,7 +151,11 @@ export abstract class JsPackageManager {
* @param packageNames
*/
public getVersions(...packageNames: string[]): Promise<string[]> {
return Promise.all(packageNames.map((packageName) => this.getVersion(packageName)));
return Promise.all(
packageNames.map((packageName) => {
return this.getVersion(packageName);
})
);
}
/**

View File

@ -10,8 +10,8 @@ function eqMajor(versionRange: string, major: number) {
return validRange(versionRange) && minVersion(versionRange).major === major;
}
// Should match @storybook/<framework>
export type SupportedFrameworks =
// Should match @storybook/<renderer>
export type SupportedRenderers =
| 'react'
| 'react-native'
| 'vue'
@ -22,7 +22,6 @@ export type SupportedFrameworks =
| 'ember'
| 'marionette'
| 'marko'
| 'meteor'
| 'preact'
| 'svelte'
| 'rax'
@ -31,11 +30,27 @@ export type SupportedFrameworks =
| 'web-components'
| 'server';
export const SUPPORTED_RENDERERS: SupportedRenderers[] = [
'react',
'react-native',
'vue',
'vue3',
'angular',
'mithril',
'riot',
'ember',
'marionette',
'marko',
'preact',
'svelte',
'rax',
'aurelia',
];
export enum ProjectType {
UNDETECTED = 'UNDETECTED',
UNSUPPORTED = 'UNSUPPORTED',
REACT_SCRIPTS = 'REACT_SCRIPTS',
METEOR = 'METEOR',
REACT = 'REACT',
REACT_NATIVE = 'REACT_NATIVE',
REACT_PROJECT = 'REACT_PROJECT',
@ -46,7 +61,6 @@ export enum ProjectType {
ANGULAR = 'ANGULAR',
EMBER = 'EMBER',
ALREADY_HAS_STORYBOOK = 'ALREADY_HAS_STORYBOOK',
UPDATE_PACKAGE_ORGANIZATIONS = 'UPDATE_PACKAGE_ORGANIZATIONS',
WEB_COMPONENTS = 'WEB_COMPONENTS',
MITHRIL = 'MITHRIL',
MARIONETTE = 'MARIONETTE',
@ -60,24 +74,6 @@ export enum ProjectType {
SERVER = 'SERVER',
}
export const SUPPORTED_FRAMEWORKS: SupportedFrameworks[] = [
'react',
'react-native',
'vue',
'vue3',
'angular',
'mithril',
'riot',
'ember',
'marionette',
'marko',
'meteor',
'preact',
'svelte',
'rax',
'aurelia',
];
export enum CoreBuilder {
Webpack5 = 'webpack5',
Vite = 'vite',
@ -114,13 +110,6 @@ export type TemplateConfiguration = {
* therefore WEBPACK_REACT has to come first, as it's more specific.
*/
export const supportedTemplates: TemplateConfiguration[] = [
{
preset: ProjectType.METEOR,
files: ['.meteor'],
matcherFunction: ({ files }) => {
return files.every(Boolean);
},
},
{
preset: ProjectType.SFC_VUE,
dependencies: {
@ -287,7 +276,6 @@ const notInstallableProjectTypes: ProjectType[] = [
ProjectType.UNDETECTED,
ProjectType.UNSUPPORTED,
ProjectType.ALREADY_HAS_STORYBOOK,
ProjectType.UPDATE_PACKAGE_ORGANIZATIONS,
];
export const installableProjectTypes = Object.values(ProjectType)

View File

@ -1,9 +1,9 @@
/* eslint-disable camelcase */
import type { StorybookConfig } from '@storybook/core-common';
import type { SupportedFrameworks } from '../project_types';
import type { SupportedRenderers } from '../project_types';
export interface Parameters {
framework: SupportedFrameworks;
renderer: SupportedRenderers;
/** E2E configuration name */
name: string;
/** framework version */
@ -35,7 +35,7 @@ const fromDeps = (...args: string[]): string =>
// #region React
export const cra: Parameters = {
framework: 'react',
renderer: 'react',
name: 'cra',
version: 'latest',
generator: [
@ -48,7 +48,7 @@ export const cra: Parameters = {
};
export const cra_typescript: Parameters = {
framework: 'react',
renderer: 'react',
name: 'cra_typescript',
version: 'latest',
generator: [
@ -58,7 +58,7 @@ export const cra_typescript: Parameters = {
};
export const react: Parameters = {
framework: 'react',
renderer: 'react',
name: 'react',
version: 'latest',
generator: fromDeps('react', 'react-dom'),
@ -66,7 +66,7 @@ export const react: Parameters = {
};
export const react_legacy_root_api: Parameters = {
framework: 'react',
renderer: 'react',
name: 'react_legacy_root_api',
version: 'latest',
generator: fromDeps('react', 'react-dom'),
@ -79,7 +79,7 @@ export const react_legacy_root_api: Parameters = {
};
export const react_typescript: Parameters = {
framework: 'react',
renderer: 'react',
name: 'react_typescript',
version: 'latest',
generator: fromDeps('react', 'react-dom'),
@ -87,21 +87,21 @@ export const react_typescript: Parameters = {
};
export const webpack_react: Parameters = {
framework: 'react',
renderer: 'react',
name: 'webpack_react',
version: 'latest',
generator: fromDeps('react', 'react-dom', 'webpack@webpack-4'),
};
export const vite_react: Parameters = {
framework: 'react',
renderer: 'react',
name: 'vite_react',
version: 'latest',
generator: 'npx -p create-vite@{{version}} create-vite {{appName}} --template react-ts',
};
export const react_in_yarn_workspace: Parameters = {
framework: 'react',
renderer: 'react',
name: 'react_in_yarn_workspace',
version: 'latest',
generator: [
@ -117,7 +117,7 @@ export const react_in_yarn_workspace: Parameters = {
// #region Angular
const baseAngular: Parameters = {
framework: 'angular',
renderer: 'angular',
name: 'angular',
version: 'latest',
generator: `npx -p @angular/cli@{{version}} ng new {{appName}} --routing=true --minimal=true --style=scss --skipInstall=true --strict`,
@ -170,7 +170,7 @@ export const angular: Parameters = baseAngular;
// #region web components
export const web_components: Parameters = {
framework: 'web-components',
renderer: 'web-components',
name: 'web_components',
version: '2',
generator: fromDeps('lit-element'),
@ -195,7 +195,7 @@ export const web_components_lit2: Parameters = {
// #region vue
export const vue: Parameters = {
framework: 'vue',
renderer: 'vue',
name: 'vue',
// Be careful here, the latest versions of vue cli are bootstrapping a vue 3 project
version: '4',
@ -206,7 +206,7 @@ export const vue: Parameters = {
};
export const vue3: Parameters = {
framework: 'vue3',
renderer: 'vue3',
name: 'vue3',
version: 'next',
// Vue CLI v4 utilizes webpack 4, and the 5-alpha uses webpack 5 so we force ^4 here
@ -219,7 +219,7 @@ export const vue3: Parameters = {
// #endregion
export const html: Parameters = {
framework: 'html',
renderer: 'html',
name: 'html',
version: 'latest',
generator: fromDeps(),
@ -227,7 +227,7 @@ export const html: Parameters = {
};
export const preact: Parameters = {
framework: 'preact',
renderer: 'preact',
name: 'preact',
version: 'latest',
generator:
@ -235,7 +235,7 @@ export const preact: Parameters = {
};
export const sfcVue: Parameters = {
framework: 'vue',
renderer: 'vue',
name: 'sfcVue',
version: 'latest',
//
@ -248,7 +248,7 @@ export const sfcVue: Parameters = {
};
export const svelte: Parameters = {
framework: 'svelte',
renderer: 'svelte',
name: 'svelte',
version: 'latest',
generator: 'npx degit sveltejs/template {{appName}}',

View File

@ -3,6 +3,7 @@ import path from 'path';
import { readJSON, writeJSON } from 'fs-extra';
import shell, { ExecOptions } from 'shelljs';
import chalk from 'chalk';
import { command } from 'execa';
import { cra, cra_typescript } from './configs';
import storybookVersions from '../versions';
@ -30,6 +31,7 @@ export interface Parameters {
interface Configuration {
e2e: boolean;
pnp: boolean;
local: boolean;
}
const useLocalSbCli = true;
@ -80,6 +82,26 @@ const addPackageResolutions = async ({ cwd }: Options) => {
await writeJSON(packageJsonPath, packageJson, { spaces: 2 });
};
const addLocalPackageResolutions = async ({ cwd }: Options) => {
logger.info(`🔢 Adding package resolutions:`);
const packageJsonPath = path.join(cwd, 'package.json');
const packageJson = await readJSON(packageJsonPath);
const workspaceDir = path.join(__dirname, '..', '..', '..', '..', '..');
const { stdout } = await command('yarn workspaces list --json', { cwd: workspaceDir });
console.log({ stdout, workspaceDir });
const workspaces = JSON.parse(`[${stdout.split('\n').join(',')}]`);
console.log({ workspaces });
packageJson.resolutions = Object.keys(storybookVersions).reduce((acc, key) => {
return {
...acc,
[key]: path.join(workspaceDir, workspaces.find((item: any) => item.name === key).location),
};
}, {});
await writeJSON(packageJsonPath, packageJson, { spaces: 2 });
};
const installYarn2 = async ({ cwd, pnp, name }: Options) => {
const command = [
`yarn set version berry`,
@ -211,7 +233,7 @@ const doTask = async (
export const createAndInit = async (
cwd: string,
{ name, version, ...rest }: Parameters,
{ e2e, pnp }: Configuration
{ e2e, pnp, local }: Configuration
) => {
const options: Options = {
name,
@ -232,8 +254,13 @@ export const createAndInit = async (
if (e2e) {
await doTask(addPackageResolutions, options);
}
if (local) {
await doTask(addLocalPackageResolutions, options);
}
await doTask(installYarn2, options);
await doTask(configureYarn2ForE2E, options, e2e);
if (e2e) {
await doTask(configureYarn2ForE2E, options, e2e);
}
await doTask(addTypescript, options, !!options.typescript);
await doTask(addRequiredDeps, options);
await doTask(initStorybook, options);

View File

@ -7,16 +7,17 @@ import dedent from 'ts-dedent';
import { createAndInit, exec } from './repro-generators/scripts';
import * as configs from './repro-generators/configs';
import type { Parameters } from './repro-generators/configs';
import { SupportedFrameworks } from './project_types';
import { SupportedRenderers } from './project_types';
const logger = console;
interface ReproOptions {
outputDirectory: string;
framework?: SupportedFrameworks;
renderer?: SupportedRenderers;
list?: boolean;
template?: string;
e2e?: boolean;
local?: boolean;
generator?: string;
pnp?: boolean;
}
@ -29,20 +30,22 @@ const CURATED_TEMPLATES = Object.fromEntries(
Object.entries(configs).filter((entry) => entry[0] !== 'react_in_yarn_workspace')
) as Record<string, Parameters>;
const FRAMEWORKS = Object.values(CURATED_TEMPLATES).reduce<
Record<SupportedFrameworks, Parameters[]>
>((acc, cur) => {
acc[cur.framework] = [...(acc[cur.framework] || []), cur];
return acc;
}, {} as Record<SupportedFrameworks, Parameters[]>);
const RENDERERS = Object.values(CURATED_TEMPLATES).reduce<Record<SupportedRenderers, Parameters[]>>(
(acc, cur) => {
acc[cur.renderer] = [...(acc[cur.renderer] || []), cur];
return acc;
},
{} as Record<SupportedRenderers, Parameters[]>
);
export const repro = async ({
outputDirectory,
list,
template,
framework,
renderer,
generator,
e2e,
local,
pnp,
}: ReproOptions) => {
logger.info(
@ -62,10 +65,10 @@ export const repro = async ({
);
if (list) {
logger.info('🌈 Available templates');
Object.entries(FRAMEWORKS).forEach(([fmwrk, templates]) => {
logger.info(fmwrk);
Object.entries(RENDERERS).forEach(([r, templates]) => {
logger.info(r);
templates.forEach((t) => logger.info(`- ${t.name}`));
if (fmwrk === 'other') {
if (r === 'other') {
logger.info('- blank');
}
});
@ -73,26 +76,26 @@ export const repro = async ({
}
let selectedTemplate = template;
let selectedFramework = framework;
let selectedRenderer = renderer;
if (!selectedTemplate && !generator) {
if (!selectedFramework) {
const { framework: frameworkOpt } = await prompts({
if (!selectedRenderer) {
const { renderer: rendererOpt } = await prompts({
type: 'select',
message: '🌈 Select the repro framework',
name: 'framework',
choices: Object.keys(FRAMEWORKS).map((f) => ({ title: f, value: f })),
message: '🌈 Select the repro renderer',
name: 'renderer',
choices: Object.keys(RENDERERS).map((f) => ({ title: f, value: f })),
});
selectedFramework = frameworkOpt;
selectedRenderer = rendererOpt;
}
if (!selectedFramework) {
throw new Error('🚨 Repro: please select a framework!');
if (!selectedRenderer) {
throw new Error('🚨 Repro: please select a renderer!');
}
selectedTemplate = (
await prompts({
type: 'select',
message: '📝 Select the repro base template',
name: 'template',
choices: FRAMEWORKS[selectedFramework as SupportedFrameworks].map((f) => ({
choices: RENDERERS[selectedRenderer as SupportedRenderers].map((f) => ({
title: f.name,
value: f.name,
})),
@ -137,6 +140,7 @@ export const repro = async ({
await createAndInit(cwd, selectedConfig, {
e2e: !!e2e,
pnp: !!pnp,
local: !!local,
});
if (!e2e) {
@ -164,7 +168,7 @@ export const repro = async ({
);
} catch (error) {
logger.error('🚨 Failed to create repro');
throw new Error(error);
throw error;
}
};

View File

@ -377,6 +377,7 @@
"verdaccio": "^4.10.0",
"verdaccio-auth-memory": "^9.7.2"
},
"packageManager": "yarn@3.2.1",
"engines": {
"node": ">=10.13.0",
"yarn": ">=1.3.2"

View File

@ -7321,6 +7321,7 @@ __metadata:
"@nrwl/workspace": ^14.1.4
"@storybook/addons": 6.5.0-rc.1
"@storybook/api": 6.5.0-rc.1
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/client-logger": 6.5.0-rc.1
"@storybook/core-client": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
@ -7651,6 +7652,7 @@ __metadata:
core-js: ^3.8.2
cross-spawn: ^7.0.3
envinfo: ^7.7.3
execa: ^5.0.0
express: ^4.17.1
find-up: ^5.0.0
fs-extra: ^9.0.1
@ -8143,6 +8145,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/ember@workspace:frameworks/ember"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-client": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
@ -8251,6 +8254,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/html-webpack5@workspace:frameworks/html-webpack5"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/html": 6.5.0-rc.1
@ -8476,6 +8480,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/preact-webpack5@workspace:frameworks/preact-webpack5"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/preact": 6.5.0-rc.1
@ -8808,6 +8813,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/react-webpack5@workspace:frameworks/react-webpack5"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/preset-react-webpack": 6.5.0-rc.1
"@storybook/react": 6.5.0-rc.1
@ -9211,6 +9217,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/server-webpack5@workspace:frameworks/server-webpack5"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/preset-server-webpack": 6.5.0-rc.1
@ -9310,6 +9317,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/svelte-webpack5@workspace:frameworks/svelte-webpack5"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/preset-svelte-webpack": 6.5.0-rc.1
@ -9502,6 +9510,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/vue-webpack5@workspace:frameworks/vue-webpack5"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/preset-vue-webpack": 6.5.0-rc.1
@ -9528,6 +9537,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/vue3-webpack5@workspace:frameworks/vue3-webpack5"
dependencies:
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/preset-vue3-webpack": 6.5.0-rc.1
@ -9600,6 +9610,7 @@ __metadata:
resolution: "@storybook/web-components-webpack5@workspace:frameworks/web-components-webpack5"
dependencies:
"@babel/preset-env": ^7.12.11
"@storybook/builder-webpack5": 6.5.0-rc.1
"@storybook/core-common": 6.5.0-rc.1
"@storybook/core-server": 6.5.0-rc.1
"@storybook/preset-web-components-webpack": 6.5.0-rc.1