or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

caching.mdconfiguration.mdenvironment-cli.mdindex.mdpackage-management.mdpresets.mdstory-processing.mdtext-processing.md

story-processing.mddocs/

0

# Story Processing

1

2

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.

3

4

## Capabilities

5

6

### Story Entry Normalization

7

8

Convert and normalize story entry configurations from various formats into a standardized structure.

9

10

```typescript { .api }

11

/**

12

* Normalize array of story entries with configuration options

13

* @param entries - Array of story entry configurations

14

* @param options - Normalization options including working directory context

15

* @returns Array of normalized story entries

16

*/

17

function normalizeStories(

18

entries: StoriesEntry[],

19

options: NormalizeOptions

20

): NormalizedStoriesEntry[];

21

22

/**

23

* Normalize individual story entry configuration

24

* @param entry - Single story entry configuration

25

* @param options - Normalization options

26

* @returns Normalized story entry

27

*/

28

function normalizeStoriesEntry(

29

entry: StoriesEntry,

30

options: NormalizeOptions

31

): NormalizedStoriesEntry;

32

33

interface NormalizeOptions {

34

configDir: string;

35

workingDir?: string;

36

}

37

```

38

39

**Usage Examples:**

40

41

```typescript

42

import { normalizeStories } from "@storybook/core-common";

43

44

// Normalize story entries from main config

45

const stories = normalizeStories([

46

'./src/**/*.stories.@(js|jsx|ts|tsx)',

47

{

48

directory: '../shared/components',

49

files: '**/*.stories.@(js|jsx|ts|tsx)',

50

titlePrefix: 'Shared'

51

}

52

], {

53

configDir: '.storybook',

54

workingDir: process.cwd()

55

});

56

57

console.log(stories);

58

// [

59

// {

60

// directory: './src',

61

// files: '**/*.stories.@(js|jsx|ts|tsx)',

62

// importPathMatcher: /^\.\/src\/(.*)\.stories\.(js|jsx|ts|tsx)$/,

63

// normalizedPath: './src',

64

// titlePrefix: undefined

65

// },

66

// ...

67

// ]

68

```

69

70

### Story ID Generation

71

72

Generate unique story IDs from file paths and story exports.

73

74

```typescript { .api }

75

/**

76

* Generate unique story ID from story data and options

77

* @param data - Story identification data

78

* @param options - Story ID generation options

79

* @returns Generated story ID string

80

*/

81

function getStoryId(data: StoryIdData, options: GetStoryIdOptions): string;

82

83

interface StoryIdData {

84

title: string;

85

name: string;

86

story?: string;

87

}

88

89

interface GetStoryIdOptions {

90

directory: string;

91

importPath: string;

92

normalizedPath: string;

93

titlePrefix?: string;

94

}

95

```

96

97

**Usage Example:**

98

99

```typescript

100

import { getStoryId } from "@storybook/core-common";

101

102

// Generate story ID

103

const storyId = getStoryId(

104

{

105

title: 'Button',

106

name: 'Primary',

107

story: 'primary'

108

},

109

{

110

directory: './src/components',

111

importPath: './src/components/Button.stories.js',

112

normalizedPath: './src/components',

113

titlePrefix: 'UI'

114

}

115

);

116

117

console.log(storyId); // 'ui-button--primary'

118

```

119

120

### Story Title Generation

121

122

Generate story titles from file paths and component information.

123

124

```typescript { .api }

125

/**

126

* Generate story title from file path and component specifier

127

* @param options - Title generation options

128

* @returns Generated story title

129

*/

130

function getStoryTitle(options: {

131

specifier: {

132

title?: string;

133

component?: string;

134

};

135

filepath: string;

136

normalizedPath: string;

137

}): string;

138

```

139

140

**Usage Example:**

141

142

```typescript

143

import { getStoryTitle } from "@storybook/core-common";

144

145

// Generate title from component

146

const title = getStoryTitle({

147

specifier: { component: 'Button' },

148

filepath: './src/components/Button/Button.stories.js',

149

normalizedPath: './src/components'

150

});

151

152

console.log(title); // 'Button/Button'

153

154

// Generate title with explicit title

155

const explicitTitle = getStoryTitle({

156

specifier: { title: 'Design System/Button' },

157

filepath: './src/components/Button.stories.js',

158

normalizedPath: './src'

159

});

160

161

console.log(explicitTitle); // 'Design System/Button'

162

```

163

164

### Directory Path Utilities

165

166

Convert between different path representations and resolve working directory contexts.

167

168

```typescript { .api }

169

/**

170

* Convert config-relative paths to working-directory-relative paths

171

* @param options - Path conversion options

172

* @returns Working directory relative path

173

*/

174

function getDirectoryFromWorkingDir(options: {

175

configDir: string;

176

workingDir?: string;

177

directory: string;

178

}): string;

179

180

/**

181

* Ensure story paths start with './' or '../'

182

* @param filename - File path to normalize

183

* @returns Normalized path with proper prefix

184

*/

185

function normalizeStoryPath(filename: string): string;

186

```

187

188

**Usage Examples:**

189

190

```typescript

191

import {

192

getDirectoryFromWorkingDir,

193

normalizeStoryPath

194

} from "@storybook/core-common";

195

196

// Convert to working directory relative

197

const workingDirPath = getDirectoryFromWorkingDir({

198

configDir: '.storybook',

199

workingDir: '/project',

200

directory: '../shared/stories'

201

});

202

203

// Normalize story paths

204

const normalized = normalizeStoryPath('src/components/Button.stories.js');

205

console.log(normalized); // './src/components/Button.stories.js'

206

```

207

208

### Pattern Matching Utilities

209

210

Convert glob patterns to regular expressions for story matching.

211

212

```typescript { .api }

213

/**

214

* Convert glob pattern to RegExp with special handling for story patterns

215

* @param glob - Glob pattern string

216

* @returns Regular expression for pattern matching

217

*/

218

function globToRegexp(glob: string): RegExp;

219

```

220

221

**Usage Example:**

222

223

```typescript

224

import { globToRegexp } from "@storybook/core-common";

225

226

// Convert story glob to regex

227

const pattern = globToRegexp('**/*.stories.@(js|jsx|ts|tsx)');

228

console.log(pattern.test('Button.stories.js')); // true

229

console.log(pattern.test('Button.test.js')); // false

230

231

// Handle specific path patterns

232

const srcPattern = globToRegexp('./src/**/*.stories.*');

233

console.log(srcPattern.test('./src/Button.stories.js')); // true

234

```

235

236

## Story Entry Types

237

238

```typescript { .api }

239

/**

240

* Story entry configuration - string glob or detailed object

241

*/

242

type StoriesEntry = string | {

243

/** Directory containing stories */

244

directory: string;

245

/** File pattern within directory */

246

files: string;

247

/** Optional prefix for story titles */

248

titlePrefix?: string;

249

};

250

251

/**

252

* Normalized story entry with computed properties

253

*/

254

interface NormalizedStoriesEntry {

255

/** Base directory for stories */

256

directory: string;

257

/** File pattern for matching stories */

258

files: string;

259

/** RegExp for matching import paths */

260

importPathMatcher: RegExp;

261

/** Normalized directory path */

262

normalizedPath: string;

263

/** Title prefix for story organization */

264

titlePrefix?: string;

265

}

266

```

267

268

## Advanced Story Processing

269

270

### Custom Story Indexing

271

272

```typescript

273

import { normalizeStories, getStoryId } from "@storybook/core-common";

274

275

async function buildCustomStoryIndex(mainConfig: any) {

276

// Normalize all story entries

277

const normalizedEntries = normalizeStories(mainConfig.stories, {

278

configDir: '.storybook'

279

});

280

281

const storyIndex = {};

282

283

for (const entry of normalizedEntries) {

284

// Find all story files matching this entry

285

const storyFiles = await glob(entry.files, {

286

cwd: entry.directory

287

});

288

289

for (const file of storyFiles) {

290

// Extract stories from file

291

const stories = await extractStoriesFromFile(file);

292

293

for (const story of stories) {

294

const storyId = getStoryId(

295

{ title: story.title, name: story.name },

296

{

297

directory: entry.directory,

298

importPath: file,

299

normalizedPath: entry.normalizedPath,

300

titlePrefix: entry.titlePrefix

301

}

302

);

303

304

storyIndex[storyId] = {

305

id: storyId,

306

title: story.title,

307

name: story.name,

308

importPath: file

309

};

310

}

311

}

312

}

313

314

return storyIndex;

315

}

316

```

317

318

### Story Validation

319

320

```typescript

321

import { normalizeStories } from "@storybook/core-common";

322

323

function validateStoryConfiguration(stories: StoriesEntry[], configDir: string) {

324

try {

325

const normalized = normalizeStories(stories, { configDir });

326

327

// Check for valid patterns

328

for (const entry of normalized) {

329

if (!entry.files || !entry.directory) {

330

throw new Error(`Invalid story entry: ${JSON.stringify(entry)}`);

331

}

332

333

// Validate glob patterns

334

if (!entry.importPathMatcher) {

335

throw new Error(`Unable to create matcher for entry: ${entry.files}`);

336

}

337

}

338

339

return { valid: true, entries: normalized };

340

} catch (error) {

341

return { valid: false, error: error.message };

342

}

343

}

344

```

345

346

### Story Path Resolution

347

348

```typescript

349

import { normalizeStoryPath, getDirectoryFromWorkingDir } from "@storybook/core-common";

350

351

function resolveStoryPaths(configDir: string, stories: string[]) {

352

return stories.map(story => {

353

// Normalize the path format

354

const normalizedPath = normalizeStoryPath(story);

355

356

// Convert to working directory relative if needed

357

return getDirectoryFromWorkingDir({

358

configDir,

359

workingDir: process.cwd(),

360

directory: normalizedPath

361

});

362

});

363

}

364

365

// Usage

366

const resolvedPaths = resolveStoryPaths('.storybook', [

367

'src/**/*.stories.js',

368

'../shared/components/**/*.stories.tsx'

369

]);

370

```