fix: resolve font assets under development base paths

Serve font definitions beside their WOFF2 files through a base-aware stylesheet link, using relative URLs consistently in dev and static builds.

Exercise all six faces, nested-route reloads and existing font fallback/no-JS checks in the dev suite; avoid starting the preview server as part of dev checks.
This commit is contained in:
2026-10-16 06:12:57 +09:00
parent c480b67301
commit 1297d86140
6 changed files with 47 additions and 15 deletions
+2 -2
View File
@@ -73,12 +73,12 @@ cargo test --workspace --locked -- --include-ignored # build後の実文書テ
cargo clippy --workspace --all-targets --locked -- -D warnings
npx playwright install chromium
npm run test:browser
npm run test:dev # dev serverでも実WASMの読み込み・本文反映を確認
npm run test:dev # dev serverでも実WASMの読み込み・本文反映とフォント配信を確認
# インストール済みChromeを使う場合:CHROME_PATH=/path/to/chrome npm run test:browser
npm run preview
```
Nixでnativeテストを行う場合は`nix develop ../../wip-rs --command cargo test --workspace --locked -- --include-ignored`のように実行します。テスト用previewは`127.0.0.1:4322`、`test:dev`は別のdev serverを4323で起動し、通常のdev server(4321)を再利用・停止しません。`SITE_BASE=/reference npm run test:dev`でサブパスのWASM読み込みも確認できます。
Nixでnativeテストを行う場合は`nix develop ../../wip-rs --command cargo test --workspace --locked -- --include-ignored`のように実行します。テスト用previewは`127.0.0.1:4322`、`test:dev`は別のdev serverを4323で起動し、通常のdev server(4321)を再利用・停止しません。`SITE_BASE=/reference npm run test:dev`でサブパスのWASM・全6書体の読み込みも確認できます。フォント定義は`public/fonts/fonts.css`をbase付きURLで参照し、各WOFF2はCSSからの相対URLで解決します。
`npm run test:browser`は先に`test-probes` feature付きの別WASMを`site/test-assets/`へ生成します。`wasm-limits.spec.ts`は実WASMの入力上限と意図的なRust panicを検証し、trapがProtocol failureへ誤変換されず、サイトがsessionを再利用しないことを確認します。このprobeはテストrouteでのみ配信され、通常の`public/`・`dist/`には入りません。
+5 -3
View File
@@ -5,9 +5,11 @@ const port = process.env.SITE_TEST_PORT || '4323';
const base = process.env.SITE_BASE?.replace(/\/$/, '') || '';
const baseURL = `http://127.0.0.1:${port}${base}/`;
export default defineConfig(preview, {
testMatch: 'wip-loading.spec.ts',
use: { baseURL },
export default defineConfig({
...preview,
// Override rather than merge webServer: dev checks must not require dist/.
testMatch: ['wip-loading.spec.ts', 'fonts.spec.ts'],
use: { ...preview.use, baseURL },
webServer: {
command: `npm run dev -- --host 127.0.0.1 --port ${port} --ignore-lock`,
url: baseURL,
@@ -1,44 +1,45 @@
/* Self-hosted, unmodified upstream fonts; revisions and licenses: /fonts/README.md.
Vite resolves these public asset URLs against the configured site base. */
/* Self-hosted, unmodified upstream fonts; revisions and licenses: README.md.
Linked through the site's base-aware href helper. Relative URLs resolve beside
this stylesheet in both dev and static builds, without Vite URL rewriting. */
@font-face {
font-family: 'Lyth Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/LythMono-Regular.woff2') format('woff2');
src: url('LythMono-Regular.woff2') format('woff2');
}
@font-face {
font-family: 'Lyth Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/LythMono-Bold.woff2') format('woff2');
src: url('LythMono-Bold.woff2') format('woff2');
}
@font-face {
font-family: 'EB Garamond';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/EBGaramond-Regular.woff2') format('woff2');
src: url('EBGaramond-Regular.woff2') format('woff2');
}
@font-face {
font-family: 'EB Garamond';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/EBGaramond-Bold.woff2') format('woff2');
src: url('EBGaramond-Bold.woff2') format('woff2');
}
@font-face {
font-family: 'EB Garamond';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('/fonts/EBGaramond-Italic.woff2') format('woff2');
src: url('EBGaramond-Italic.woff2') format('woff2');
}
@font-face {
font-family: 'EB Garamond';
font-style: italic;
font-weight: 700;
font-display: swap;
src: url('/fonts/EBGaramond-BoldItalic.woff2') format('woff2');
src: url('EBGaramond-BoldItalic.woff2') format('woff2');
}
+1
View File
@@ -34,6 +34,7 @@ const canonical = Astro.site ? new URL(Astro.url.pathname, Astro.site) : undefin
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#171a17" />
<link rel="icon" type="image/svg+xml" href={href('favicon.svg')} />
<link rel="preload" href={href('fonts/LythMono-Regular.woff2')} as="font" type="font/woff2" crossorigin="anonymous" />
<link rel="stylesheet" href={href('fonts/fonts.css')} />
</head>
<body class:list={{ 'landing-page': landing }}>
<a class="skip-link" href="#main">本文へスキップ</a>
-1
View File
@@ -1,4 +1,3 @@
@import './fonts.css';
@import './theme.css';
:root {
+30 -1
View File
@@ -5,19 +5,28 @@ test.use({ javaScriptEnabled: true });
test('self-hosted Lyth Mono and EB Garamond load with the configured base', async ({ page }, testInfo) => {
const external: string[] = [];
const fontResponses: { url: string; status: number }[] = [];
page.on('response', (response) => {
if (response.request().resourceType() === 'font') fontResponses.push({ url: response.url(), status: response.status() });
});
page.on('request', (request) => {
if (request.resourceType() === 'font' && !request.url().startsWith(testInfo.project.use.baseURL!)) external.push(request.url());
});
await page.goto('./');
// Cold dev startup may reload the document while modules initialize. Let
// initial asset traffic settle before probing FontFaceSet in that document.
await page.goto('./', { waitUntil: 'networkidle' });
const fonts = await page.evaluate(async () => {
await document.fonts.ready;
const mono = await document.fonts.load('14px "Lyth Mono"', '0W│├└─');
const bold = await document.fonts.load('700 14px "Lyth Mono"', 'WIP');
const serif = await document.fonts.load('16px "EB Garamond"', 'Worldspace');
const italic = await document.fonts.load('italic 16px "EB Garamond"', 'Worldspace');
const serifBold = await document.fonts.load('700 16px "EB Garamond"', 'Worldspace');
const boldItalic = await document.fonts.load('italic 700 16px "EB Garamond"', 'Worldspace');
return {
mono: mono.map((font) => font.status), bold: bold.map((font) => font.status),
serif: serif.map((font) => font.status), italic: italic.map((font) => font.status),
serifBold: serifBold.map((font) => font.status), boldItalic: boldItalic.map((font) => font.status),
stack: getComputedStyle(document.body).fontFamily,
prefix: document.querySelector('.branch-prefix')!.textContent!.trim(),
};
@@ -26,6 +35,14 @@ test('self-hosted Lyth Mono and EB Garamond load with the configured base', asyn
expect(fonts.bold).toEqual(['loaded']);
expect(fonts.serif).toEqual(['loaded']);
expect(fonts.italic).toEqual(['loaded']);
expect(fonts.serifBold).toEqual(['loaded']);
expect(fonts.boldItalic).toEqual(['loaded']);
const filenames = ['LythMono-Regular', 'LythMono-Bold', 'EBGaramond-Regular', 'EBGaramond-Bold', 'EBGaramond-Italic', 'EBGaramond-BoldItalic'];
expect([...new Set(fontResponses.map(({ url }) => url))].sort()).toEqual(
filenames.map((name) => new URL(`fonts/${name}.woff2`, testInfo.project.use.baseURL!).href).sort(),
);
// The dev server may revalidate the preloaded regular face after a reload.
expect(fontResponses.filter(({ status }) => status !== 200 && status !== 304)).toEqual([]);
expect(fonts.stack).toContain('Lyth Mono');
expect(fonts.prefix).toBe(''); // SSR uses the same glyph paths, not HTML wires.
expect(external).toEqual([]);
@@ -45,6 +62,18 @@ test('self-hosted Lyth Mono and EB Garamond load with the configured base', asyn
expect(await page.locator('.tree-pane .root-vector .trunk-reveal').evaluate((rect) => (rect as SVGRectElement).height.baseVal.value)).toBeGreaterThan(0);
expect(await page.locator('article.prose').evaluate((element) => getComputedStyle(element).fontFamily)).toContain('EB Garamond');
await page.screenshot({ path: testInfo.outputPath('eb-garamond-book.png') });
// A fresh nested route must resolve fonts relative to their stylesheet, not
// relative to the page URL, without relying on ClientRouter's persisted DOM.
await page.reload();
const reloaded = await page.evaluate(async () => {
await document.fonts.ready;
return [
(await document.fonts.load('14px "Lyth Mono"', 'WIP')).map((font) => font.status),
(await document.fonts.load('16px "EB Garamond"', 'Worldspace')).map((font) => font.status),
];
});
expect(reloaded).toEqual([['loaded'], ['loaded']]);
expect(external).toEqual([]);
});
test.describe('static font and tree rendering', () => {