chore(docs): stabilize storybook source discovery

This commit is contained in:
2026-03-25 19:48:27 +08:00
parent 49f94a6c89
commit eccaacece7
5 changed files with 136 additions and 6 deletions
+2
View File
@@ -12,3 +12,5 @@ playwright-report
test-results
output
.DS_Store
.playwright-cli
Library
+76 -3
View File
@@ -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
}
}
}
});
}
};
+6 -1
View File
@@ -58,7 +58,12 @@ const preview: Preview = {
controls: {
expanded: true
},
layout: "fullscreen"
layout: "fullscreen",
options: {
storySort: {
order: ["Scenes", "Patterns", "Components", "Foundation"]
}
}
},
decorators: [
(Story, context) => {
+2 -2
View File
@@ -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.