@crvy/strybk 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.0.4] - 2026-07-03
9
+
10
+ ### Other
11
+
12
+ - Update README.md
13
+ ## [0.0.3] - 2026-06-18
14
+
15
+ ### Added
16
+
17
+ - Match stories by importPath and resolve globs via configDir
8
18
  ## [0.0.2] - 2026-06-11
9
19
  ## [0.0.1] - 2026-06-11
10
20
 
package/README.md CHANGED
@@ -1,23 +1,11 @@
1
- # strybk
1
+ # @crvy/strybk
2
2
 
3
3
  Generator-first Playwright screenshot testing for Storybook.
4
4
 
5
- ## Development
6
-
7
- Install dependencies with Bun:
8
-
9
- ```sh
10
- bun install
11
- ```
12
-
13
- Common local commands:
5
+ ## Installation
14
6
 
15
7
  ```sh
16
- bun run lint
17
- bun run typecheck
18
- bun run test:bun
19
- bun run build
20
- bun run check
8
+ npm install --save-dev @crvy/strybk
21
9
  ```
22
10
 
23
11
  ## CLI
@@ -93,3 +81,21 @@ If you want to persist the link in the consumer's manifest, Bun supports a `link
93
81
  }
94
82
  }
95
83
  ```
84
+
85
+ ## Development
86
+
87
+ Install dependencies with Bun:
88
+
89
+ ```sh
90
+ bun install
91
+ ```
92
+
93
+ Common local commands:
94
+
95
+ ```sh
96
+ bun run lint
97
+ bun run typecheck
98
+ bun run test:bun
99
+ bun run build
100
+ bun run check
101
+ ```
package/dist/src/cli.js CHANGED
@@ -53,14 +53,15 @@ const toStoryIndexEntry = (value) => {
53
53
  }
54
54
  if (typeof value.id !== "string" ||
55
55
  typeof value.title !== "string" ||
56
- typeof value.name !== "string") {
56
+ typeof value.name !== "string" ||
57
+ typeof value.importPath !== "string") {
57
58
  return null;
58
59
  }
59
60
  return {
60
61
  id: value.id,
61
62
  title: value.title,
62
63
  name: value.name,
63
- importPath: typeof value.importPath === "string" ? value.importPath : undefined,
64
+ importPath: value.importPath,
64
65
  exportName: typeof value.exportName === "string" ? value.exportName : undefined,
65
66
  };
66
67
  };
@@ -144,6 +145,7 @@ export async function runCli(argv) {
144
145
  const indexEntries = await fetchStoryIndex(config);
145
146
  const outputs = await generateScreenshots({
146
147
  config,
148
+ configDir: dirname(resolve(cliArgs.configPath)),
147
149
  indexEntries,
148
150
  readExistingFile,
149
151
  });
@@ -1,5 +1,6 @@
1
1
  export interface StoryFile {
2
2
  filePath: string;
3
- title: string;
4
3
  }
5
- export declare function discoverStoryFiles(patterns: string[]): Promise<StoryFile[]>;
4
+ export declare function discoverStoryFiles(patterns: string[], options?: {
5
+ cwd?: string;
6
+ }): Promise<StoryFile[]>;
@@ -1,13 +1,6 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { glob } from "glob";
3
- export async function discoverStoryFiles(patterns) {
4
- const files = await glob(patterns, { absolute: true });
5
- return files.flatMap((filePath) => {
6
- const content = readFileSync(filePath, "utf8");
7
- const titleMatch = content.match(/title:\s*['"]([^'"]+)['"]/u);
8
- if (!titleMatch) {
9
- return [];
10
- }
11
- return [{ filePath, title: titleMatch[1] }];
12
- });
2
+ export async function discoverStoryFiles(patterns, options = {}) {
3
+ const cwd = options.cwd ?? process.cwd();
4
+ const files = await glob(patterns, { absolute: true, cwd });
5
+ return files.map((filePath) => ({ filePath }));
13
6
  }
@@ -3,13 +3,14 @@ export interface StoryIndexEntry {
3
3
  id: string;
4
4
  title: string;
5
5
  name: string;
6
- importPath?: string;
6
+ importPath: string;
7
7
  exportName?: string;
8
8
  }
9
9
  export declare function generateScreenshots(args: {
10
10
  config: StrybkConfig;
11
11
  indexEntries: StoryIndexEntry[];
12
12
  readExistingFile?: (filePath: string) => string | null;
13
+ configDir?: string;
13
14
  }): Promise<Array<{
14
15
  outputPath: string;
15
16
  content: string;
@@ -2,6 +2,16 @@ import { readFileSync } from "node:fs";
2
2
  import { discoverStoryFiles } from "./discover.js";
3
3
  import { extractCreeveyMetadata, FILE_POLICY_KEY } from "./metadata.js";
4
4
  import { renderScreenshotSpec } from "./render.js";
5
+ const normalizePath = (value) => value.replace(/\\/gu, "/").replace(/^\.\//u, "");
6
+ const matchesByImportPath = (filePath, importPath) => {
7
+ const normalizedImport = normalizePath(importPath);
8
+ const normalizedFile = normalizePath(filePath);
9
+ return normalizedFile === normalizedImport || normalizedFile.endsWith(`/${normalizedImport}`);
10
+ };
11
+ const resolveStoryTitle = (storyFile, indexEntries) => {
12
+ const pathMatch = indexEntries.find((entry) => matchesByImportPath(storyFile.filePath, entry.importPath));
13
+ return pathMatch?.title ?? null;
14
+ };
5
15
  const escapeForRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
6
16
  const toStoryIdSegment = (value) => value
7
17
  .replace(/([a-z0-9])([A-Z])/gu, "$1-$2")
@@ -16,12 +26,16 @@ const isStorySkipped = (story, storyMetadata) => {
16
26
  return Object.entries(storyMetadata).some(([exportName, policy]) => policy.skip === true && toStoryIdSegment(exportName) === storyIdSegment);
17
27
  };
18
28
  export async function generateScreenshots(args) {
19
- const storyFiles = await discoverStoryFiles(args.config.storyGlobs);
29
+ const storyFiles = await discoverStoryFiles(args.config.storyGlobs, args.configDir === undefined ? {} : { cwd: args.configDir });
20
30
  const generatedRegionName = args.config.generatedRegionName ?? "auto-screenshots";
21
31
  const manualRegionPattern = new RegExp(`// @generated-end ${escapeForRegExp(generatedRegionName)}\\s*([\\s\\S]*)$`, "u");
22
32
  const shouldExtractCreeveyMetadata = args.config.metadataExtractors?.includes("creevey") ?? false;
23
33
  return storyFiles.flatMap((storyFile) => {
24
- const stories = args.indexEntries.filter((entry) => entry.title === storyFile.title);
34
+ const title = resolveStoryTitle(storyFile, args.indexEntries);
35
+ if (title === null) {
36
+ return [];
37
+ }
38
+ const stories = args.indexEntries.filter((entry) => entry.title === title);
25
39
  const storyMetadata = shouldExtractCreeveyMetadata
26
40
  ? extractCreeveyMetadata(readFileSync(storyFile.filePath, "utf8"))
27
41
  : {};
@@ -40,7 +54,7 @@ export async function generateScreenshots(args) {
40
54
  outputPath,
41
55
  content: renderScreenshotSpec({
42
56
  config: args.config,
43
- title: storyFile.title,
57
+ title,
44
58
  stories: filteredStories,
45
59
  manualRegion,
46
60
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crvy/strybk",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Generator-first Playwright screenshot testing for Storybook.",
5
5
  "keywords": [
6
6
  "crvy",