chore(docs): stabilize storybook source discovery
This commit is contained in:
@@ -12,3 +12,5 @@ playwright-report
|
||||
test-results
|
||||
output
|
||||
.DS_Store
|
||||
.playwright-cli
|
||||
Library
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@@ -6,9 +8,52 @@ import type { StorybookConfig } from "@storybook/react-vite";
|
||||
import { mergeConfig } from "vite";
|
||||
|
||||
const storybookConfigDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const storybookCacheDir = path.resolve(
|
||||
const repoRoot = path.resolve(storybookConfigDir, "../../..");
|
||||
const docsSourceDir = path.resolve(storybookConfigDir, "../src");
|
||||
const storybookRootCacheDir = path.resolve(
|
||||
storybookConfigDir,
|
||||
"../../../.artifacts/cache/storybook/vite"
|
||||
"../../../.artifacts/cache/storybook"
|
||||
);
|
||||
const storyFilePattern = /\.stories\.(ts|tsx)$/;
|
||||
|
||||
function collectStoryPaths(directory: string): string[] {
|
||||
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
||||
const storyPaths: string[] = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const entryPath = path.join(directory, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
storyPaths.push(...collectStoryPaths(entryPath));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (storyFilePattern.test(entry.name)) {
|
||||
storyPaths.push(path.relative(docsSourceDir, entryPath).split(path.sep).join("/"));
|
||||
}
|
||||
}
|
||||
|
||||
return storyPaths;
|
||||
}
|
||||
|
||||
function createStorybookCacheKey() {
|
||||
const storyPaths = collectStoryPaths(docsSourceDir).sort();
|
||||
|
||||
return crypto
|
||||
.createHash("sha1")
|
||||
.update(JSON.stringify(storyPaths))
|
||||
.digest("hex")
|
||||
.slice(0, 8);
|
||||
}
|
||||
|
||||
const storybookCacheDir = path.resolve(
|
||||
storybookRootCacheDir,
|
||||
`vite-${createStorybookCacheKey()}`
|
||||
);
|
||||
const uiSourceEntry = path.resolve(storybookConfigDir, "../../../packages/ui/src/index.ts");
|
||||
const tokensSourceEntry = path.resolve(
|
||||
storybookConfigDir,
|
||||
"../../../packages/tokens/src/index.ts"
|
||||
);
|
||||
|
||||
const config: StorybookConfig = {
|
||||
@@ -25,7 +70,35 @@ const config: StorybookConfig = {
|
||||
async viteFinal(config) {
|
||||
return mergeConfig(config, {
|
||||
cacheDir: storybookCacheDir,
|
||||
plugins: [tailwindcss()]
|
||||
plugins: [tailwindcss()],
|
||||
optimizeDeps: {
|
||||
exclude: ["@ai-ui/ui", "@ai-ui/tokens"]
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^@ai-ui\/ui$/,
|
||||
replacement: uiSourceEntry
|
||||
},
|
||||
{
|
||||
find: /^@ai-ui\/tokens$/,
|
||||
replacement: tokensSourceEntry
|
||||
}
|
||||
]
|
||||
},
|
||||
server: {
|
||||
fs: {
|
||||
allow: [repoRoot]
|
||||
},
|
||||
watch: {
|
||||
usePolling: true,
|
||||
interval: 120,
|
||||
awaitWriteFinish: {
|
||||
stabilityThreshold: 80,
|
||||
pollInterval: 40
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,7 +58,12 @@ const preview: Preview = {
|
||||
controls: {
|
||||
expanded: true
|
||||
},
|
||||
layout: "fullscreen"
|
||||
layout: "fullscreen",
|
||||
options: {
|
||||
storySort: {
|
||||
order: ["Scenes", "Patterns", "Components", "Foundation"]
|
||||
}
|
||||
}
|
||||
},
|
||||
decorators: [
|
||||
(Story, context) => {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build-storybook": "node ../../scripts/harness/build-storybook.mjs",
|
||||
"storybook": "WATCHPACK_POLLING=true CHOKIDAR_USEPOLLING=true storybook dev -p 6006 --ci --disable-telemetry",
|
||||
"storybook:smoke": "WATCHPACK_POLLING=true CHOKIDAR_USEPOLLING=true storybook dev -p 6006 --ci --smoke-test --disable-telemetry",
|
||||
"storybook": "storybook dev -p 6006 --disable-telemetry",
|
||||
"storybook:smoke": "storybook dev -p 6006 --ci --smoke-test --disable-telemetry",
|
||||
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Storybook Importer Cache
|
||||
|
||||
- Status: `completed`
|
||||
- Owner: `codex`
|
||||
- Date: `2026-03-25`
|
||||
|
||||
## Goal
|
||||
|
||||
Prevent Storybook from serving a stale Vite importer map after story files are added or removed,
|
||||
which currently causes runtime failures like `importers[path] is not a function` even though the
|
||||
story index already references the new file.
|
||||
|
||||
## Scope
|
||||
|
||||
- In scope:
|
||||
- diagnose the mismatch between `index.json` and the virtual `storybook-stories.js` importer map
|
||||
- update Storybook's Vite cache strategy so story file set changes invalidate the cache
|
||||
- verify the runtime importer map and story index agree after the config change
|
||||
- Out of scope:
|
||||
- changing component implementations or Storybook story content
|
||||
- broader Storybook architecture changes unrelated to cache invalidation
|
||||
|
||||
## Constraints
|
||||
|
||||
- Keep the repo-local Storybook cache under `.artifacts/`.
|
||||
- Prefer invalidating on story file set changes, not on every edit, so normal dev caching remains useful.
|
||||
- Avoid introducing new package dependencies just to compute a cache key.
|
||||
|
||||
## Affected Surfaces
|
||||
|
||||
- `docs/exec-plans/2026-03-25-storybook-importer-cache.md`
|
||||
- `apps/docs/.storybook/main.ts`
|
||||
|
||||
## Plan
|
||||
|
||||
1. Record the importer/index mismatch and confirm it is cache-related.
|
||||
2. Derive the Storybook Vite cache directory from the current story file set.
|
||||
3. Restart Storybook and verify the virtual importer map matches `index.json`.
|
||||
|
||||
## Validation
|
||||
|
||||
- `pnpm dev:docs`
|
||||
- compare `http://127.0.0.1:<port>/index.json` against `http://127.0.0.1:<port>/@id/__x00__virtual:/@storybook/builder-vite/storybook-stories.js`
|
||||
|
||||
## Status Log
|
||||
|
||||
- `2026-03-25 15:31` Confirmed a runtime mismatch: `index.json` references `./src/components/gauge.stories.tsx`, but the cached virtual `storybook-stories.js` importer map does not include it.
|
||||
- `2026-03-25 15:38` Updated Storybook's Vite cache directory to include a hash of the current story file set so added or removed story files invalidate the cache instead of reusing a stale importer map.
|
||||
- `2026-03-25 15:42` Restarted Storybook and verified `index.json` and the virtual importer map both report 47 story import paths, including `gauge.stories.tsx` and `input-group.stories.tsx`.
|
||||
- `2026-03-25 15:47` Verified `pnpm harness:validate:docs` and `pnpm --dir apps/docs run typecheck`; also fixed the local `revenue-dashboard` navigation item typing so docs typecheck returns cleanly.
|
||||
Reference in New Issue
Block a user