CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-storybook--csf-tools

Parse and manipulate CSF and Storybook config files

Pending
Overview
Eval results
Files

config-management.mddocs/

Configuration Management

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.

Capabilities

Loading Configuration Files

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();

Reading Configuration Files

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']);

Writing Configuration Files

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>;

Code Generation

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;

ConfigFile Class

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;
}

Framework and Addon Management

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']);

Array Manipulation

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');

Import Management

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');

Utility Functions

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;

Configuration Patterns

Main Configuration

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);

Preview Configuration

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

docs

config-management.md

csf-processing.md

index.md

story-enhancement.md

testing-integration.md

tile.json