fix(storybook): load local Noto Sans KR and this checkout's UI theme
- Dev server: rewrite the @hm-design/ui dist font URL to the /fonts static directory so Preview no longer gets a 403 from the Vite fs allow list. - When node_modules/@hm-design/ui is linked to another checkout, resolve the five @hm-design/ui entry points to this checkout's packages/ui source so the current tokens are shown instead of a stale palette. Checkouts whose links already point at their own packages/ui keep the dist-based resolution.
This commit is contained in:
1 parent
ea984ab3a6
commit
be0661062f
1 file changed
+60
-1
+60
-1
@@ -1,5 +1,57 @@
|
|||||||
import type { StorybookConfig } from "@storybook/react-vite"
|
import type { StorybookConfig } from "@storybook/react-vite"
|
||||||
import { readFileSync } from "node:fs"
|
import { readFileSync, realpathSync } from "node:fs"
|
||||||
|
import { fileURLToPath } from "node:url"
|
||||||
|
import path from "node:path"
|
||||||
|
|
||||||
|
// Checkouts that share another checkout's node_modules (a linked git worktree whose node_modules is a junction) also
|
||||||
|
// inherit that checkout's node_modules/@hm-design/* links. Those links point at the other checkout's packages/ui and its
|
||||||
|
// compiled dist, so Storybook would show the other checkout's tokens (a stale palette) instead of this checkout's.
|
||||||
|
// Only in that case, resolve the five @hm-design/ui entry points to this checkout's own source. Nothing is built, the
|
||||||
|
// other checkout's node_modules and dist are only read, and a checkout whose links already point at its own
|
||||||
|
// packages/ui keeps the unchanged dist-based resolution.
|
||||||
|
const checkoutRoot = fileURLToPath(new URL("../", import.meta.url))
|
||||||
|
const localUiRoot = path.join(checkoutRoot, "packages/ui")
|
||||||
|
function uiLinkedElsewhere() {
|
||||||
|
try {
|
||||||
|
return realpathSync(path.join(checkoutRoot, "node_modules/@hm-design/ui")) !== realpathSync(localUiRoot)
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const localUiAliases = [
|
||||||
|
[/^@hm-design\/ui$/, "src/index.ts"],
|
||||||
|
[/^@hm-design\/ui\/theme$/, "src/theme/index.ts"],
|
||||||
|
[/^@hm-design\/ui\/theme\.css$/, "src/theme.css"],
|
||||||
|
[/^@hm-design\/ui\/tailwind\.css$/, "src/tailwind.css"],
|
||||||
|
[/^@hm-design\/ui\/dashboard-status\.css$/, "src/patterns/dashboard-status/dashboard-status.css"],
|
||||||
|
].map(([find, file]) => ({ find: find as RegExp, replacement: path.join(localUiRoot, file as string) }))
|
||||||
|
|
||||||
|
// @hm-design/ui's dist/theme.css declares the bundled font with a relative URL. When node_modules/@hm-design/ui is
|
||||||
|
// symlinked to a checkout outside this Vite root, Vite rewrites that URL to /@fs/<outside path> and the dev preview
|
||||||
|
// gets a 403 ("outside of Vite serving allow list"). Point only that font URL at the /fonts static directory the
|
||||||
|
// Manager already uses, instead of widening server.fs.allow. theme.css is inlined through @import, so the rewrite
|
||||||
|
// runs on the already-resolved CSS and is limited to the exact ".../ui/dist/fonts/NotoSansKR-variable.ttf" URL.
|
||||||
|
// Dev server only: the static build emits the font as a bundled asset.
|
||||||
|
const UI_DIST_FONT_URL = /[^"'()\\\s]*\/ui\/dist\/fonts\/NotoSansKR-variable\.ttf/g
|
||||||
|
const FONT_STATIC_URL = "/fonts/NotoSansKR-variable.ttf"
|
||||||
|
|
||||||
|
const hmUiThemeFontPath = {
|
||||||
|
name: "hm-ui-theme-font-path",
|
||||||
|
apply: "serve" as const,
|
||||||
|
enforce: "post" as const,
|
||||||
|
transform(code: string, id: string) {
|
||||||
|
const file = id.split("?")[0]
|
||||||
|
if (!file.endsWith(".css") || !code.includes("NotoSansKR-variable.ttf")) return null
|
||||||
|
const next = code.replace(UI_DIST_FONT_URL, FONT_STATIC_URL)
|
||||||
|
if (next === code) {
|
||||||
|
if (!code.includes(FONT_STATIC_URL)) {
|
||||||
|
this.warn(`[hm-ui-theme-font-path] ${file}: found NotoSansKR-variable.ttf but not at ".../ui/dist/fonts/"; the URL was left unchanged and Preview may fail to load Noto Sans KR.`)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return { code: next, map: null }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
stories: ["../src/**/*.stories.@(ts|tsx)"],
|
stories: ["../src/**/*.stories.@(ts|tsx)"],
|
||||||
@@ -21,6 +73,13 @@ const config: StorybookConfig = {
|
|||||||
viteFinal: (config) => {
|
viteFinal: (config) => {
|
||||||
config.server ??= {}
|
config.server ??= {}
|
||||||
config.server.watch = { ...config.server.watch, ignored: ["**/_local/**", "**/storybook-static/**"] }
|
config.server.watch = { ...config.server.watch, ignored: ["**/_local/**", "**/storybook-static/**"] }
|
||||||
|
config.plugins = [...(config.plugins ?? []), hmUiThemeFontPath]
|
||||||
|
if (uiLinkedElsewhere()) {
|
||||||
|
config.resolve ??= {}
|
||||||
|
const existing = config.resolve.alias
|
||||||
|
const current = Array.isArray(existing) ? existing : Object.entries(existing ?? {}).map(([find, replacement]) => ({ find, replacement }))
|
||||||
|
config.resolve.alias = [...localUiAliases, ...current]
|
||||||
|
}
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
framework: "@storybook/react-vite",
|
framework: "@storybook/react-vite",
|
||||||
|
|||||||
Reference in new issue
Block a user