Skip to content

Commit

Permalink
feat(storybook): storybook 7 executors
Browse files Browse the repository at this point in the history
  • Loading branch information
mandarini committed Jan 10, 2023
1 parent a806fee commit 32411e8
Show file tree
Hide file tree
Showing 7 changed files with 567 additions and 165 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"@storybook/core-server": "^6.5.15",
"@storybook/manager-webpack5": "^6.5.15",
"@storybook/react": "^6.5.15",
"@storybook/types": "^7.0.0-alpha.44",
"@svgr/rollup": "^6.1.2",
"@svgr/webpack": "^6.1.2",
"@swc-node/register": "^1.4.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,109 @@
import { ExecutorContext, logger } from '@nrwl/devkit';
import * as build from '@storybook/core-server/standalone';
import {
ExecutorContext,
logger,
readJsonFile,
workspaceRoot,
} from '@nrwl/devkit';
import * as build from '@storybook/core-server';
import {
CLIOptions,
LoadOptions,
BuilderOptions,
PackageJson,
} from '@storybook/types';
import 'dotenv/config';
import path = require('path');
import { storybookConfigExists } from '../../utils/utilities';
import { CommonNxStorybookConfig } from '../models';
import {
getStorybookFrameworkPath,
isStorybookV7,
resolveCommonStorybookOptionMapper,
runStorybookSetupCheck,
} from '../utils';

export interface StorybookBuilderOptions extends CommonNxStorybookConfig {
quiet?: boolean;
outputPath?: string;
docsMode?: boolean;
docsMode: boolean;
}

export default async function buildStorybookExecutor(
options: StorybookBuilderOptions,
context: ExecutorContext
) {
logger.info(`NX ui framework: ${options.uiFramework}`);
const storybook7 = isStorybookV7();

if (storybook7) {
storybookConfigExists(options.config, context.projectName);
const packageJson = readJsonFile(
path.join(workspaceRoot, 'package.json')
) as PackageJson;
const buildOptions = {
...options,
workspaceRoot: context.root,
configDir: options.config.configFolder,
packageJson,
watch: false,
mode: options?.['mode'] ?? 'static',
outputDir:
(options?.['outputDir'] || options?.['output-dir']) ??
options.outputPath,
ignorePreview: options['ignorePreview'] ?? false,
cache: options['cache'] ?? false,
} as CLIOptions &
LoadOptions &
BuilderOptions & {
outputDir: string;
};

const frameworkPath = getStorybookFrameworkPath(options.uiFramework);
const { default: frameworkOptions } = await import(frameworkPath);
logger.info(`NX Storybook builder starting ...`);
await runInstance(buildOptions);
logger.info(`NX Storybook builder finished ...`);
logger.info(`NX Storybook files available in ${options.outputPath}`);
return { success: true };
} else {
logger.info(`NX ui framework: ${options.uiFramework}`);

const option = storybookOptionMapper(options, frameworkOptions, context);
const frameworkPath = getStorybookFrameworkPath(options.uiFramework);
const { default: frameworkOptions } = await import(frameworkPath);

// print warnings
runStorybookSetupCheck(options);
const option = storybookOptionMapper(
options,
frameworkOptions,
context
) as CLIOptions &
LoadOptions &
BuilderOptions & {
outputDir: string;
};

logger.info(`NX Storybook builder starting ...`);
await runInstance(option);
logger.info(`NX Storybook builder finished ...`);
logger.info(`NX Storybook files available in ${options.outputPath}`);
return { success: true };
// print warnings
runStorybookSetupCheck(options);

logger.info(`NX Storybook builder starting ...`);
await runInstance(option);
logger.info(`NX Storybook builder finished ...`);
logger.info(`NX Storybook files available in ${options.outputPath}`);
return { success: true };
}
}

function runInstance(options: StorybookBuilderOptions): Promise<void> {
const env = process.env.NODE_ENV ?? 'production';
function runInstance(
options: CLIOptions &
LoadOptions &
BuilderOptions & {
outputDir: string;
}
): Promise<void> {
const env = process.env.NODE_ENV ?? 'PRODUCTION';
process.env.NODE_ENV = env;
return build({

return build.buildStaticStandalone({
...options,
ci: true,
configType: env.toUpperCase(),
});
configType: 'PRODUCTION',
ignorePreview: options['ignorePreview'] ?? false,
} as any);
}

function storybookOptionMapper(
Expand Down
38 changes: 29 additions & 9 deletions packages/storybook/src/executors/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,34 @@ export interface StorybookConfig {
}

export interface CommonNxStorybookConfig {
uiFramework:
| '@storybook/angular'
| '@storybook/react'
| '@storybook/html'
| '@storybook/web-components'
| '@storybook/vue'
| '@storybook/vue3'
| '@storybook/svelte'
| '@storybook/react-native';
uiFramework: UiFramework;
uiFramework7: UiFramework7;
config: StorybookConfig;
}

export type UiFramework7 =
| '@storybook/angular'
| '@storybook/html-webpack5'
| '@storybook/preact-webpack5'
| '@storybook/react-webpack5'
| '@storybook/react-vite'
| '@storybook/server-webpack5'
| '@storybook/svelte-webpack5'
| '@storybook/svelte-vite'
| '@storybook/sveltekit'
| '@storybook/vue-webpack5'
| '@storybook/vue-vite'
| '@storybook/vue3-webpack5'
| '@storybook/vue3-vite'
| '@storybook/web-components-webpack5'
| '@storybook/web-components-vite';

export type UiFramework =
| '@storybook/angular'
| '@storybook/react'
| '@storybook/html'
| '@storybook/web-components'
| '@storybook/vue'
| '@storybook/vue3'
| '@storybook/svelte'
| '@storybook/react-native';
76 changes: 60 additions & 16 deletions packages/storybook/src/executors/storybook/storybook.impl.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { ExecutorContext } from '@nrwl/devkit';
import { buildDev } from '@storybook/core-server';
import { ExecutorContext, readJsonFile, workspaceRoot } from '@nrwl/devkit';
import * as build from '@storybook/core-server';
import 'dotenv/config';
import { storybookConfigExists } from '../../utils/utilities';
import { CommonNxStorybookConfig } from '../models';
import {
getStorybookFrameworkPath,
resolveCommonStorybookOptionMapper,
runStorybookSetupCheck,
isStorybookV7,
} from '../utils';
import {
CLIOptions,
LoadOptions,
BuilderOptions,
PackageJson,
} from '@storybook/types';
import path = require('path');

export interface StorybookExecutorOptions extends CommonNxStorybookConfig {
host?: string;
port?: number;
Expand All @@ -23,29 +33,63 @@ export default async function* storybookExecutor(
options: StorybookExecutorOptions,
context: ExecutorContext
): AsyncGenerator<{ success: boolean }> {
let frameworkPath = getStorybookFrameworkPath(options.uiFramework);
const frameworkOptions = (await import(frameworkPath)).default;
const storybook7 = isStorybookV7();
if (storybook7) {
storybookConfigExists(options.config, context.projectName);
const packageJson = readJsonFile(
path.join(workspaceRoot, 'package.json')
) as PackageJson;
const buildOptions = {
...options,
workspaceRoot: context.root,
configDir: options.config.configFolder,
mode: 'dev',
packageJson,
watch: true,
ignorePreview: options['ignorePreview'] ?? false,
cache: options['cache'] ?? false,
} as CLIOptions & LoadOptions & BuilderOptions;

await runInstance(buildOptions, storybook7);
yield { success: true };
// This Promise intentionally never resolves, leaving the process running
await new Promise<{ success: boolean }>(() => {});
} else {
let frameworkPath = getStorybookFrameworkPath(options.uiFramework);
const frameworkOptions = (await import(frameworkPath)).default;

const option = storybookOptionMapper(options, frameworkOptions, context);
const option = storybookOptionMapper(options, frameworkOptions, context);

// print warnings
runStorybookSetupCheck(options);
// print warnings
runStorybookSetupCheck(options);

await runInstance(option);
await runInstance(option, storybook7);

yield { success: true };
yield { success: true };

// This Promise intentionally never resolves, leaving the process running
await new Promise<{ success: boolean }>(() => {});
// This Promise intentionally never resolves, leaving the process running
await new Promise<{ success: boolean }>(() => {});
}
}

function runInstance(options: StorybookExecutorOptions) {
function runInstance(
options: CLIOptions & LoadOptions & BuilderOptions,
storybook7: boolean
) {
const env = process.env.NODE_ENV ?? 'development';
process.env.NODE_ENV = env;
return buildDev({
...options,
configType: env.toUpperCase(),
} as any);

if (storybook7) {
return build.buildDevStandalone({
...options,
configType: env.toUpperCase(),
} as any);
} else {
return build.buildDev({
...options,
configType: env.toUpperCase(),
} as any);
}
}

function storybookOptionMapper(
Expand Down
19 changes: 16 additions & 3 deletions packages/storybook/src/executors/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import { join } from 'path';
import { gte } from 'semver';
import ts = require('typescript');
import { findOrCreateConfig } from '../utils/utilities';
import { CommonNxStorybookConfig } from './models';
import {
CommonNxStorybookConfig,
StorybookConfig,
UiFramework,
UiFramework7,
} from './models';

export interface NodePackage {
name: string;
version: string;
}

export function getStorybookFrameworkPath(uiFramework) {
export function getStorybookFrameworkPath(uiFramework: UiFramework) {
const serverOptionsPaths = {
'@storybook/react': '@storybook/react/dist/cjs/server/options',
'@storybook/html': '@storybook/html/dist/cjs/server/options',
Expand All @@ -31,7 +36,7 @@ export function getStorybookFrameworkPath(uiFramework) {
}
}

function isStorybookV62onwards(uiFramework) {
function isStorybookV62onwards(uiFramework: string) {
const storybookPackageVersion = require(join(
uiFramework,
'package.json'
Expand All @@ -40,6 +45,14 @@ function isStorybookV62onwards(uiFramework) {
return gte(storybookPackageVersion, '6.2.0-rc.4');
}

export function isStorybookV7() {
const storybookPackageVersion = require(join(
'@storybook/core-server',
'package.json'
)).version;
return gte(storybookPackageVersion, '7.0.0-alpha.0');
}

export function runStorybookSetupCheck(options: CommonNxStorybookConfig) {
webpackFinalPropertyCheck(options);
reactWebpack5Check(options);
Expand Down
23 changes: 22 additions & 1 deletion packages/storybook/src/utils/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,32 @@ export type TsConfig = {
references?: Array<{ path: string }>;
};

export function storybookConfigExists(
config: StorybookConfig,
projectName: string
): boolean {
const exists = !!(
config?.configFolder && statSync(config.configFolder).isDirectory()
);

if (!exists) {
throw new Error(
`Could not find Storybook configuration for project ${projectName}.
Please generate Storybook configuration using the following command:
nx g @nrwl/storybook:configuration --name=${projectName}
`
);
}

return exists;
}

export function findOrCreateConfig(
config: StorybookConfig,
context: ExecutorContext
): string {
if (config?.configFolder && statSync(config.configFolder).isDirectory()) {
if (storybookConfigExists(config, context.projectName)) {
return config.configFolder;
} else if (
statSync(config.configPath).isFile() &&
Expand Down
Loading

0 comments on commit 32411e8

Please sign in to comment.