Parse and manipulate CSF and Storybook config files
—
Parse and manipulate Storybook configuration files including main.js, preview.js, and other config files. Supports both ESM and CommonJS formats with comprehensive field manipulation capabilities.
Parse configuration code into a structured ConfigFile object for analysis and manipulation.
/**
* Parse configuration code into a ConfigFile instance
* @param code - The configuration source code as a string
* @param fileName - Optional file name for error reporting
* @returns ConfigFile instance ready for parsing
*/
function loadConfig(code: string, fileName?: string): ConfigFile;Usage Examples:
import { loadConfig } from "@storybook/csf-tools";
// Parse Storybook main.js configuration
const mainConfig = `
module.exports = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-essentials'],
framework: '@storybook/react-vite'
};
`;
const config = loadConfig(mainConfig, 'main.js').parse();Read and parse configuration files directly from the filesystem.
/**
* Read and parse a configuration file from the filesystem
* @param fileName - Path to the configuration file
* @returns Promise resolving to parsed ConfigFile
*/
function readConfig(fileName: string): Promise<ConfigFile>;Usage Examples:
import { readConfig } from "@storybook/csf-tools";
// Read and parse main.js
const config = await readConfig('.storybook/main.js');
// Access configuration values
const stories = config.getFieldValue(['stories']);
const addons = config.getNamesFromPath(['addons']);Write ConfigFile objects back to the filesystem as formatted code.
/**
* Write a ConfigFile to the filesystem
* @param config - The ConfigFile instance to write
* @param fileName - Optional output file path (uses config.fileName if not provided)
* @returns Promise that resolves when file is written
*/
function writeConfig(config: ConfigFile, fileName?: string): Promise<void>;Generate formatted code from ConfigFile instances.
/**
* Generate formatted code from a ConfigFile
* @param config - The ConfigFile instance to format
* @returns Generated configuration code as string
*/
function formatConfig(config: ConfigFile): string;
/**
* Generate code with style preservation using recast
* @param config - The ConfigFile instance to print
* @param options - Recast formatting options
* @returns Object with code and optional source map
*/
function printConfig(config: ConfigFile, options?: RecastOptions): PrintResultType;The core class for representing and manipulating configuration files.
class ConfigFile {
/** Babel AST representation of the file */
readonly _ast: t.File;
/** Original source code */
readonly _code: string;
/** Parsed exports from the configuration */
readonly _exports: Record<string, t.Expression>;
/** Main exports object if present */
readonly _exportsObject?: t.ObjectExpression;
/** Optional file name */
fileName?: string;
/** Whether file has a default export */
hasDefaultExport: boolean;
constructor(ast: t.File, code: string, fileName?: string);
/**
* Parse the AST to extract configuration exports
* @returns Self for method chaining
*/
parse(): ConfigFile;
/**
* Get AST node at the specified path
* @param path - Array of property names representing the path
* @returns AST node if found, undefined otherwise
*/
getFieldNode(path: string[]): t.Node | undefined;
/**
* Get evaluated value at the specified path
* @param path - Array of property names representing the path
* @returns Evaluated value if found, undefined otherwise
*/
getFieldValue<T = any>(path: string[]): T | undefined;
/**
* Get value at path with error handling
* @param path - Array of property names representing the path
* @returns Value if found and evaluable, undefined otherwise
*/
getSafeFieldValue(path: string[]): any;
/**
* Set AST node at the specified path
* @param path - Array of property names representing the path
* @param expr - AST expression to set
*/
setFieldNode(path: string[], expr: t.Expression): void;
/**
* Set value at the specified path
* @param path - Array of property names representing the path
* @param value - Value to set (will be converted to AST)
*/
setFieldValue(path: string[], value: any): void;
/**
* Remove field at the specified path
* @param path - Array of property names representing the path
*/
removeField(path: string[]): void;
}Specialized methods for working with Storybook framework and addon configurations.
/**
* Get name from framework or addon configuration
* Supports both string format ("framework-name") and object format ({ name: "framework-name", options: {} })
* @param path - Path to the configuration field
* @returns Framework or addon name if found
*/
getNameFromPath(path: string[]): string | undefined;
/**
* Get array of names from addon configuration
* @param path - Path to the addons array
* @returns Array of addon names
*/
getNamesFromPath(path: string[]): string[] | undefined;Usage Examples:
// Get framework name
const frameworkName = config.getNameFromPath(['framework']);
// Get all addon names
const addonNames = config.getNamesFromPath(['addons']);
// Get stories configuration
const stories = config.getFieldValue(['stories']);Methods for working with array fields like stories and addons.
/**
* Append a value to an array field
* @param path - Path to the array field
* @param value - Value to append
*/
appendValueToArray(path: string[], value: any): void;
/**
* Append an AST node to an array field
* @param path - Path to the array field
* @param node - AST expression to append
*/
appendNodeToArray(path: string[], node: t.Expression): void;
/**
* Remove entry from array by name (for addons/frameworks)
* @param path - Path to the array field
* @param value - Name of entry to remove
*/
removeEntryFromArray(path: string[], value: string): void;Usage Examples:
// Add a new addon
config.appendValueToArray(['addons'], '@storybook/addon-docs');
// Remove an addon
config.removeEntryFromArray(['addons'], '@storybook/addon-actions');
// Add story pattern
config.appendValueToArray(['stories'], '../components/**/*.stories.ts');Methods for managing import statements in configuration files.
/**
* Add or update ES6 import statement
* @param importSpecifier - Import specifiers (string for default, array for named, object for namespace)
* @param fromImport - Module to import from
*/
setImport(
importSpecifier: string[] | string | { namespace: string } | null,
fromImport: string
): void;
/**
* Add or update CommonJS require statement
* @param importSpecifier - Import specifiers (string for default, array for destructured)
* @param fromImport - Module to require from
*/
setRequireImport(importSpecifier: string[] | string, fromImport: string): void;Usage Examples:
// Add ES6 imports
config.setImport(['StorybookConfig'], '@storybook/types');
config.setImport('path', 'path');
config.setImport({ namespace: 'fs' }, 'fs');
// Add CommonJS requires
config.setRequireImport(['join'], 'path');
config.setRequireImport('express', 'express');Helper functions for configuration analysis.
/**
* Check if preview configuration uses CSF factory pattern
* @param previewConfig - Parsed preview configuration
* @returns True if CSF factory pattern is detected
*/
function isCsfFactoryPreview(previewConfig: ConfigFile): boolean;
/**
* Convert JavaScript value to AST node with proper quote style
* @param value - Value to convert
* @returns AST expression node
*/
valueToNode(value: any): t.Expression | undefined;Typical patterns for working with .storybook/main.js:
import { readConfig, writeConfig } from "@storybook/csf-tools";
const config = await readConfig('.storybook/main.js');
// Modify stories glob patterns
const currentStories = config.getFieldValue(['stories']);
config.setFieldValue(['stories'], [
...currentStories,
'../components/**/*.stories.@(js|ts|jsx|tsx)'
]);
// Add an addon
config.appendValueToArray(['addons'], {
name: '@storybook/addon-docs',
options: { transcludeMarkdown: true }
});
// Set framework
config.setFieldValue(['framework'], {
name: '@storybook/react-vite',
options: { builder: { viteConfigPath: './vite.config.ts' } }
});
await writeConfig(config);Working with .storybook/preview.js:
const preview = await readConfig('.storybook/preview.js');
// Set global parameters
preview.setFieldValue(['parameters', 'actions'], { argTypesRegex: '^on[A-Z].*' });
preview.setFieldValue(['parameters', 'docs'], {
page: null,
source: { type: 'code' }
});
// Add decorators
preview.appendValueToArray(['decorators'], 'withThemeProvider');
await writeConfig(preview);Install with Tessl CLI
npx tessl i tessl/npm-storybook--csf-tools