284 lines
7.3 KiB
TypeScript
284 lines
7.3 KiB
TypeScript
import {
|
|
addDependenciesToPackageJson,
|
|
formatFiles,
|
|
GeneratorCallback,
|
|
logger,
|
|
readNxJson,
|
|
readProjectConfiguration,
|
|
runTasksInSerial,
|
|
Tree,
|
|
} from '@nx/devkit';
|
|
import { initGenerator as jsInitGenerator } from '@nx/js';
|
|
|
|
import { cypressProjectGenerator } from '../cypress-project/cypress-project';
|
|
import { StorybookConfigureSchema } from './schema';
|
|
import { initGenerator } from '../init/init';
|
|
|
|
import {
|
|
addAngularStorybookTarget,
|
|
addBuildStorybookToCacheableOperations,
|
|
addStaticTarget,
|
|
addStorybookTarget,
|
|
addStorybookToNamedInputs,
|
|
addStorybookToTargetDefaults,
|
|
configureTsProjectConfig,
|
|
configureTsSolutionConfig,
|
|
createProjectStorybookDir,
|
|
createStorybookTsconfigFile,
|
|
editTsconfigBaseJson,
|
|
findMetroConfig,
|
|
findNextConfig,
|
|
findViteConfig,
|
|
getE2EProjectName,
|
|
projectIsRootProjectInStandaloneWorkspace,
|
|
updateLintConfig,
|
|
} from './lib/util-functions';
|
|
import { Linter } from '@nx/eslint';
|
|
import {
|
|
findStorybookAndBuildTargetsAndCompiler,
|
|
pleaseUpgrade,
|
|
storybookMajorVersion,
|
|
} from '../../utils/utilities';
|
|
import {
|
|
coreJsVersion,
|
|
nxVersion,
|
|
storybookVersion,
|
|
tsLibVersion,
|
|
tsNodeVersion,
|
|
} from '../../utils/versions';
|
|
import { interactionTestsDependencies } from './lib/interaction-testing.utils';
|
|
import { ensureDependencies } from './lib/ensure-dependencies';
|
|
import { editRootTsConfig } from './lib/edit-root-tsconfig';
|
|
|
|
export function configurationGenerator(
|
|
tree: Tree,
|
|
schema: StorybookConfigureSchema
|
|
) {
|
|
return configurationGeneratorInternal(tree, { addPlugin: false, ...schema });
|
|
}
|
|
|
|
export async function configurationGeneratorInternal(
|
|
tree: Tree,
|
|
rawSchema: StorybookConfigureSchema
|
|
) {
|
|
if (storybookMajorVersion() === 6) {
|
|
throw new Error(pleaseUpgrade());
|
|
}
|
|
|
|
const schema = normalizeSchema(tree, rawSchema);
|
|
|
|
const tasks: GeneratorCallback[] = [];
|
|
|
|
const { projectType, targets, root } = readProjectConfiguration(
|
|
tree,
|
|
schema.project
|
|
);
|
|
const { compiler } = findStorybookAndBuildTargetsAndCompiler(targets);
|
|
|
|
const viteConfig = findViteConfig(tree, root);
|
|
const viteConfigFilePath = viteConfig?.fullConfigPath;
|
|
const viteConfigFileName = viteConfig?.viteConfigFileName;
|
|
const nextConfigFilePath = findNextConfig(tree, root);
|
|
const metroConfigFilePath = findMetroConfig(tree, root);
|
|
|
|
if (viteConfigFilePath) {
|
|
if (schema.uiFramework === '@storybook/react-webpack5') {
|
|
logger.info(
|
|
`Your project ${schema.project} uses Vite as a bundler.
|
|
Nx will configure Storybook for this project to use Vite as well.`
|
|
);
|
|
schema.uiFramework = '@storybook/react-vite';
|
|
}
|
|
if (schema.uiFramework === '@storybook/web-components-webpack5') {
|
|
logger.info(
|
|
`Your project ${schema.project} uses Vite as a bundler.
|
|
Nx will configure Storybook for this project to use Vite as well.`
|
|
);
|
|
schema.uiFramework = '@storybook/web-components-vite';
|
|
}
|
|
}
|
|
|
|
if (nextConfigFilePath) {
|
|
schema.uiFramework = '@storybook/nextjs';
|
|
}
|
|
|
|
const jsInitTask = await jsInitGenerator(tree, {
|
|
...schema,
|
|
skipFormat: true,
|
|
});
|
|
tasks.push(jsInitTask);
|
|
const initTask = await initGenerator(tree, {
|
|
skipFormat: true,
|
|
addPlugin: schema.addPlugin,
|
|
});
|
|
tasks.push(initTask);
|
|
tasks.push(ensureDependencies(tree, { uiFramework: schema.uiFramework }));
|
|
|
|
editRootTsConfig(tree);
|
|
|
|
const nxJson = readNxJson(tree);
|
|
const hasPlugin = nxJson.plugins?.some((p) =>
|
|
typeof p === 'string'
|
|
? p === '@nx/storybook/plugin'
|
|
: p.plugin === '@nx/storybook/plugin'
|
|
);
|
|
|
|
const mainDir =
|
|
!!nextConfigFilePath && projectType === 'application'
|
|
? 'components'
|
|
: 'src';
|
|
|
|
const usesVite =
|
|
!!viteConfigFilePath || schema.uiFramework?.endsWith('-vite');
|
|
const useReactNative = !!metroConfigFilePath;
|
|
|
|
createProjectStorybookDir(
|
|
tree,
|
|
schema.project,
|
|
schema.uiFramework,
|
|
schema.js,
|
|
schema.tsConfiguration,
|
|
root,
|
|
projectType,
|
|
projectIsRootProjectInStandaloneWorkspace(root),
|
|
schema.interactionTests,
|
|
mainDir,
|
|
!!nextConfigFilePath,
|
|
compiler === 'swc',
|
|
usesVite,
|
|
viteConfigFilePath,
|
|
hasPlugin,
|
|
viteConfigFileName,
|
|
useReactNative
|
|
);
|
|
|
|
if (schema.uiFramework !== '@storybook/angular') {
|
|
createStorybookTsconfigFile(
|
|
tree,
|
|
root,
|
|
schema.uiFramework,
|
|
projectIsRootProjectInStandaloneWorkspace(root),
|
|
mainDir
|
|
);
|
|
}
|
|
configureTsProjectConfig(tree, schema);
|
|
editTsconfigBaseJson(tree);
|
|
configureTsSolutionConfig(tree, schema);
|
|
updateLintConfig(tree, schema);
|
|
|
|
addBuildStorybookToCacheableOperations(tree);
|
|
addStorybookToNamedInputs(tree);
|
|
if (!hasPlugin) {
|
|
addStorybookToTargetDefaults(tree);
|
|
}
|
|
|
|
let devDeps = {};
|
|
|
|
if (!hasPlugin || schema.addExplicitTargets) {
|
|
if (schema.uiFramework === '@storybook/angular') {
|
|
addAngularStorybookTarget(tree, schema.project, schema.interactionTests);
|
|
} else {
|
|
addStorybookTarget(
|
|
tree,
|
|
schema.project,
|
|
schema.uiFramework,
|
|
schema.interactionTests
|
|
);
|
|
}
|
|
if (schema.configureStaticServe) {
|
|
addStaticTarget(tree, schema);
|
|
}
|
|
} else {
|
|
devDeps['storybook'] = storybookVersion;
|
|
}
|
|
|
|
// TODO(katerina): Nx 19 -> remove Cypress
|
|
if (schema.configureCypress) {
|
|
const e2eProject = await getE2EProjectName(tree, schema.project);
|
|
if (!e2eProject) {
|
|
const cypressTask = await cypressProjectGenerator(tree, {
|
|
name: schema.project,
|
|
js: schema.js,
|
|
linter: schema.linter,
|
|
directory: schema.cypressDirectory,
|
|
standaloneConfig: schema.standaloneConfig,
|
|
ciTargetName: schema.configureStaticServe
|
|
? 'static-storybook'
|
|
: undefined,
|
|
skipFormat: true,
|
|
});
|
|
tasks.push(cypressTask);
|
|
} else {
|
|
logger.warn(
|
|
`There is already an e2e project setup for ${schema.project}, called ${e2eProject}.`
|
|
);
|
|
}
|
|
}
|
|
|
|
if (schema.tsConfiguration) {
|
|
devDeps['ts-node'] = tsNodeVersion;
|
|
}
|
|
|
|
if (usesVite && !viteConfigFilePath) {
|
|
devDeps['tslib'] = tsLibVersion;
|
|
}
|
|
|
|
if (schema.interactionTests) {
|
|
devDeps = {
|
|
...devDeps,
|
|
...interactionTestsDependencies(),
|
|
};
|
|
}
|
|
|
|
if (schema.configureStaticServe) {
|
|
devDeps['@nx/web'] = nxVersion;
|
|
}
|
|
|
|
if (
|
|
projectType !== 'application' &&
|
|
schema.uiFramework === '@storybook/react-webpack5'
|
|
) {
|
|
devDeps['core-js'] = coreJsVersion;
|
|
}
|
|
|
|
if (schema.uiFramework?.endsWith('-vite') && !viteConfigFilePath) {
|
|
// This means that the user has selected a Vite framework
|
|
// but the project does not have Vite configuration.
|
|
// We need to install the @nx/vite plugin in order to be able to use
|
|
// the nxViteTsPaths plugin to register the tsconfig paths in Vite.
|
|
devDeps['@nx/vite'] = nxVersion;
|
|
}
|
|
|
|
tasks.push(addDependenciesToPackageJson(tree, {}, devDeps));
|
|
|
|
if (!schema.skipFormat) {
|
|
await formatFiles(tree);
|
|
}
|
|
|
|
return runTasksInSerial(...tasks);
|
|
}
|
|
|
|
function normalizeSchema(
|
|
tree: Tree,
|
|
schema: StorybookConfigureSchema
|
|
): StorybookConfigureSchema {
|
|
const nxJson = readNxJson(tree);
|
|
const addPlugin =
|
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
nxJson.useInferencePlugins !== false;
|
|
|
|
const defaults = {
|
|
interactionTests: true,
|
|
linter: Linter.EsLint,
|
|
js: false,
|
|
tsConfiguration: true,
|
|
addPlugin,
|
|
};
|
|
return {
|
|
...defaults,
|
|
...schema,
|
|
};
|
|
}
|
|
|
|
export default configurationGenerator;
|