From 1297d861401682066cf3030870577fb37543073f Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 16 Oct 2026 06:12:57 +0900 Subject: [PATCH] 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. --- README.md | 4 +-- site/playwright.dev.config.ts | 8 ++++-- site/{src/styles => public/fonts}/fonts.css | 17 +++++------ site/src/layouts/Site.astro | 1 + site/src/styles/global.css | 1 - site/tests/browser/fonts.spec.ts | 31 ++++++++++++++++++++- 6 files changed, 47 insertions(+), 15 deletions(-) rename site/{src/styles => public/fonts}/fonts.css (58%) diff --git a/README.md b/README.md index 5c0a0b6..bdd51f1 100644 --- a/README.md +++ b/README.md @@ -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/`には入りません。 diff --git a/site/playwright.dev.config.ts b/site/playwright.dev.config.ts index b683fdf..9e0e8ff 100644 --- a/site/playwright.dev.config.ts +++ b/site/playwright.dev.config.ts @@ -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, diff --git a/site/src/styles/fonts.css b/site/public/fonts/fonts.css similarity index 58% rename from site/src/styles/fonts.css rename to site/public/fonts/fonts.css index 0c3658e..babe244 100644 --- a/site/src/styles/fonts.css +++ b/site/public/fonts/fonts.css @@ -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'); } diff --git a/site/src/layouts/Site.astro b/site/src/layouts/Site.astro index 487d7db..b6f5298 100644 --- a/site/src/layouts/Site.astro +++ b/site/src/layouts/Site.astro @@ -34,6 +34,7 @@ const canonical = Astro.site ? new URL(Astro.url.pathname, Astro.site) : undefin + diff --git a/site/src/styles/global.css b/site/src/styles/global.css index eb679fd..fba1b16 100644 --- a/site/src/styles/global.css +++ b/site/src/styles/global.css @@ -1,4 +1,3 @@ -@import './fonts.css'; @import './theme.css'; :root { diff --git a/site/tests/browser/fonts.spec.ts b/site/tests/browser/fonts.spec.ts index ccdc10c..1562bfe 100644 --- a/site/tests/browser/fonts.spec.ts +++ b/site/tests/browser/fonts.spec.ts @@ -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', () => {