Deprecated compatibility shim for Storybook's framework-agnostic API utilities
—
Story entry normalization and metadata generation for Storybook's story indexing system. These utilities handle glob patterns, directory mapping, story ID generation, and story metadata processing.
Convert and normalize story entry configurations from various formats into a standardized structure.
/**
* Normalize array of story entries with configuration options
* @param entries - Array of story entry configurations
* @param options - Normalization options including working directory context
* @returns Array of normalized story entries
*/
function normalizeStories(
entries: StoriesEntry[],
options: NormalizeOptions
): NormalizedStoriesEntry[];
/**
* Normalize individual story entry configuration
* @param entry - Single story entry configuration
* @param options - Normalization options
* @returns Normalized story entry
*/
function normalizeStoriesEntry(
entry: StoriesEntry,
options: NormalizeOptions
): NormalizedStoriesEntry;
interface NormalizeOptions {
configDir: string;
workingDir?: string;
}Usage Examples:
import { normalizeStories } from "@storybook/core-common";
// Normalize story entries from main config
const stories = normalizeStories([
'./src/**/*.stories.@(js|jsx|ts|tsx)',
{
directory: '../shared/components',
files: '**/*.stories.@(js|jsx|ts|tsx)',
titlePrefix: 'Shared'
}
], {
configDir: '.storybook',
workingDir: process.cwd()
});
console.log(stories);
// [
// {
// directory: './src',
// files: '**/*.stories.@(js|jsx|ts|tsx)',
// importPathMatcher: /^\.\/src\/(.*)\.stories\.(js|jsx|ts|tsx)$/,
// normalizedPath: './src',
// titlePrefix: undefined
// },
// ...
// ]Generate unique story IDs from file paths and story exports.
/**
* Generate unique story ID from story data and options
* @param data - Story identification data
* @param options - Story ID generation options
* @returns Generated story ID string
*/
function getStoryId(data: StoryIdData, options: GetStoryIdOptions): string;
interface StoryIdData {
title: string;
name: string;
story?: string;
}
interface GetStoryIdOptions {
directory: string;
importPath: string;
normalizedPath: string;
titlePrefix?: string;
}Usage Example:
import { getStoryId } from "@storybook/core-common";
// Generate story ID
const storyId = getStoryId(
{
title: 'Button',
name: 'Primary',
story: 'primary'
},
{
directory: './src/components',
importPath: './src/components/Button.stories.js',
normalizedPath: './src/components',
titlePrefix: 'UI'
}
);
console.log(storyId); // 'ui-button--primary'Generate story titles from file paths and component information.
/**
* Generate story title from file path and component specifier
* @param options - Title generation options
* @returns Generated story title
*/
function getStoryTitle(options: {
specifier: {
title?: string;
component?: string;
};
filepath: string;
normalizedPath: string;
}): string;Usage Example:
import { getStoryTitle } from "@storybook/core-common";
// Generate title from component
const title = getStoryTitle({
specifier: { component: 'Button' },
filepath: './src/components/Button/Button.stories.js',
normalizedPath: './src/components'
});
console.log(title); // 'Button/Button'
// Generate title with explicit title
const explicitTitle = getStoryTitle({
specifier: { title: 'Design System/Button' },
filepath: './src/components/Button.stories.js',
normalizedPath: './src'
});
console.log(explicitTitle); // 'Design System/Button'Convert between different path representations and resolve working directory contexts.
/**
* Convert config-relative paths to working-directory-relative paths
* @param options - Path conversion options
* @returns Working directory relative path
*/
function getDirectoryFromWorkingDir(options: {
configDir: string;
workingDir?: string;
directory: string;
}): string;
/**
* Ensure story paths start with './' or '../'
* @param filename - File path to normalize
* @returns Normalized path with proper prefix
*/
function normalizeStoryPath(filename: string): string;Usage Examples:
import {
getDirectoryFromWorkingDir,
normalizeStoryPath
} from "@storybook/core-common";
// Convert to working directory relative
const workingDirPath = getDirectoryFromWorkingDir({
configDir: '.storybook',
workingDir: '/project',
directory: '../shared/stories'
});
// Normalize story paths
const normalized = normalizeStoryPath('src/components/Button.stories.js');
console.log(normalized); // './src/components/Button.stories.js'Convert glob patterns to regular expressions for story matching.
/**
* Convert glob pattern to RegExp with special handling for story patterns
* @param glob - Glob pattern string
* @returns Regular expression for pattern matching
*/
function globToRegexp(glob: string): RegExp;Usage Example:
import { globToRegexp } from "@storybook/core-common";
// Convert story glob to regex
const pattern = globToRegexp('**/*.stories.@(js|jsx|ts|tsx)');
console.log(pattern.test('Button.stories.js')); // true
console.log(pattern.test('Button.test.js')); // false
// Handle specific path patterns
const srcPattern = globToRegexp('./src/**/*.stories.*');
console.log(srcPattern.test('./src/Button.stories.js')); // true/**
* Story entry configuration - string glob or detailed object
*/
type StoriesEntry = string | {
/** Directory containing stories */
directory: string;
/** File pattern within directory */
files: string;
/** Optional prefix for story titles */
titlePrefix?: string;
};
/**
* Normalized story entry with computed properties
*/
interface NormalizedStoriesEntry {
/** Base directory for stories */
directory: string;
/** File pattern for matching stories */
files: string;
/** RegExp for matching import paths */
importPathMatcher: RegExp;
/** Normalized directory path */
normalizedPath: string;
/** Title prefix for story organization */
titlePrefix?: string;
}import { normalizeStories, getStoryId } from "@storybook/core-common";
async function buildCustomStoryIndex(mainConfig: any) {
// Normalize all story entries
const normalizedEntries = normalizeStories(mainConfig.stories, {
configDir: '.storybook'
});
const storyIndex = {};
for (const entry of normalizedEntries) {
// Find all story files matching this entry
const storyFiles = await glob(entry.files, {
cwd: entry.directory
});
for (const file of storyFiles) {
// Extract stories from file
const stories = await extractStoriesFromFile(file);
for (const story of stories) {
const storyId = getStoryId(
{ title: story.title, name: story.name },
{
directory: entry.directory,
importPath: file,
normalizedPath: entry.normalizedPath,
titlePrefix: entry.titlePrefix
}
);
storyIndex[storyId] = {
id: storyId,
title: story.title,
name: story.name,
importPath: file
};
}
}
}
return storyIndex;
}import { normalizeStories } from "@storybook/core-common";
function validateStoryConfiguration(stories: StoriesEntry[], configDir: string) {
try {
const normalized = normalizeStories(stories, { configDir });
// Check for valid patterns
for (const entry of normalized) {
if (!entry.files || !entry.directory) {
throw new Error(`Invalid story entry: ${JSON.stringify(entry)}`);
}
// Validate glob patterns
if (!entry.importPathMatcher) {
throw new Error(`Unable to create matcher for entry: ${entry.files}`);
}
}
return { valid: true, entries: normalized };
} catch (error) {
return { valid: false, error: error.message };
}
}import { normalizeStoryPath, getDirectoryFromWorkingDir } from "@storybook/core-common";
function resolveStoryPaths(configDir: string, stories: string[]) {
return stories.map(story => {
// Normalize the path format
const normalizedPath = normalizeStoryPath(story);
// Convert to working directory relative if needed
return getDirectoryFromWorkingDir({
configDir,
workingDir: process.cwd(),
directory: normalizedPath
});
});
}
// Usage
const resolvedPaths = resolveStoryPaths('.storybook', [
'src/**/*.stories.js',
'../shared/components/**/*.stories.tsx'
]);Install with Tessl CLI
npx tessl i tessl/npm-storybook--core-common