-
Notifications
You must be signed in to change notification settings - Fork 296
fix(plugins): track font call ranges during transform #785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c92de34
0e44a62
ed6295c
73858a1
0676075
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -729,6 +729,7 @@ export function createGoogleFontsPlugin(fontGoogleShimPath: string, shimsDir: st | |
|
|
||
| const replacement = `${calleeSource}(${optionsWithCSS})`; | ||
| s.overwrite(callStart, callEnd, replacement); | ||
| overwrittenRanges.push([callStart, callEnd]); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a correct fix. Every other That said, for the specific scenario described in the PR (two non-overlapping
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct fix. This was the only Note: for the specific |
||
| hasChanges = true; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import type { Metadata } from "next"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding a nested layout with Consider either:
|
||
| import { Geist, Geist_Mono } from "next/font/google"; | ||
|
|
||
| const geistSans = Geist({ | ||
| variable: "--font-geist-sans", | ||
| subsets: ["latin"], | ||
| }); | ||
|
|
||
| const geistMono = Geist_Mono({ | ||
| variable: "--font-geist-mono", | ||
| subsets: ["latin"], | ||
| }); | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "Font Google Multiple Test", | ||
| }; | ||
|
|
||
| export default function RootLayout({ | ||
| children, | ||
| }: Readonly<{ | ||
| children: React.ReactNode; | ||
| }>) { | ||
| return ( | ||
| <html lang="en" className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}> | ||
| <body className="min-h-full flex flex-col">{children}</body> | ||
| </html> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| export default function HomePage() { | ||
| return ( | ||
| <main> | ||
| <h1>Font Google Multiple Test</h1> | ||
| <p>This page tests multiple Google Fonts (Geist + Geist_Mono).</p> | ||
| </main> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import { defineConfig } from "vite"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This config file is unused — the build test passes
Also, the fixture is missing a |
||
| import vinext from "vinext"; | ||
|
|
||
| export default defineConfig({ | ||
| plugins: [vinext({ appDir: import.meta.dirname })], | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,92 @@ | ||||||||||||||||
| import { describe, it, expect, afterAll } from "vite-plus/test"; | ||||||||||||||||
| import path from "node:path"; | ||||||||||||||||
| import fs from "node:fs/promises"; | ||||||||||||||||
| import os from "node:os"; | ||||||||||||||||
| import { createBuilder } from "vite"; | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||
| import vinext from "../packages/vinext/src/index.js"; | ||||||||||||||||
|
|
||||||||||||||||
| const APP_FIXTURE_DIR = path.resolve(import.meta.dirname, "./fixtures/font-google-multiple"); | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Build an App Router fixture's RSC/SSR/client bundles using the actual Vite | ||||||||||||||||
| * build pipeline (createBuilder + buildApp). This exercises the full build | ||||||||||||||||
| * pipeline for font-google transforms. | ||||||||||||||||
| */ | ||||||||||||||||
| async function buildFontGoogleMultipleFixture(): Promise<string> { | ||||||||||||||||
| const outDir = await fs.mkdtemp(path.join(os.tmpdir(), "vinext-font-google-multiple-")); | ||||||||||||||||
|
|
||||||||||||||||
| const rscOutDir = path.join(outDir, "server"); | ||||||||||||||||
| const ssrOutDir = path.join(outDir, "server", "ssr"); | ||||||||||||||||
| const clientOutDir = path.join(outDir, "client"); | ||||||||||||||||
|
|
||||||||||||||||
| // Mock fetch before building to avoid network calls | ||||||||||||||||
| const originalFetch = globalThis.fetch; | ||||||||||||||||
| globalThis.fetch = async (input: any) => { | ||||||||||||||||
| const url = String(input); | ||||||||||||||||
| if (url.includes("Geist") && !url.includes("Mono")) { | ||||||||||||||||
| return new Response("@font-face { font-family: 'Geist'; src: url(/geist.woff2); }", { | ||||||||||||||||
| status: 200, | ||||||||||||||||
| headers: { "content-type": "text/css" }, | ||||||||||||||||
| }); | ||||||||||||||||
| } | ||||||||||||||||
| return new Response("@font-face { font-family: 'Geist Mono'; src: url(/geist-mono.woff2); }", { | ||||||||||||||||
| status: 200, | ||||||||||||||||
| headers: { "content-type": "text/css" }, | ||||||||||||||||
| }); | ||||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
| const nodeModulesLink = path.join(APP_FIXTURE_DIR, "node_modules"); | ||||||||||||||||
|
|
||||||||||||||||
| try { | ||||||||||||||||
| // Symlink node_modules before building so imports work | ||||||||||||||||
| const projectNodeModules = path.resolve(import.meta.dirname, "../node_modules"); | ||||||||||||||||
| await fs.symlink(projectNodeModules, nodeModulesLink); | ||||||||||||||||
|
|
||||||||||||||||
| const builder = await createBuilder({ | ||||||||||||||||
| root: APP_FIXTURE_DIR, | ||||||||||||||||
| configFile: false, | ||||||||||||||||
| plugins: [ | ||||||||||||||||
| vinext({ | ||||||||||||||||
| appDir: APP_FIXTURE_DIR, | ||||||||||||||||
| rscOutDir, | ||||||||||||||||
| ssrOutDir, | ||||||||||||||||
| clientOutDir, | ||||||||||||||||
| }), | ||||||||||||||||
| ], | ||||||||||||||||
| logLevel: "silent", | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| await builder.buildApp(); | ||||||||||||||||
|
|
||||||||||||||||
| return path.join(outDir, "server", "index.mjs"); | ||||||||||||||||
| } finally { | ||||||||||||||||
| globalThis.fetch = originalFetch; | ||||||||||||||||
| // Cleanup symlink | ||||||||||||||||
| await fs.unlink(nodeModulesLink).catch(() => {}); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| describe("font-google build integration", () => { | ||||||||||||||||
| let buildOutputPath: string; | ||||||||||||||||
| let outDir: string; | ||||||||||||||||
|
|
||||||||||||||||
| afterAll(async () => { | ||||||||||||||||
| // Cleanup temp directory | ||||||||||||||||
| if (outDir) { | ||||||||||||||||
| await fs.rm(outDir, { recursive: true, force: true }); | ||||||||||||||||
| } | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| it("should build successfully with multiple Google fonts (Geist + Geist_Mono)", async () => { | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment claims this reproduces issue #751, but it doesn't — this test passes on Please update or remove this comment to avoid misleading future readers.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment on lines 81-83 implies this test validates the fix, but it passes without the one-line change. Please either:
|
||||||||||||||||
| // This test validates that the build pipeline can handle multiple | ||||||||||||||||
| // Google font imports without errors. It exercises the font transform | ||||||||||||||||
| // plugin during the full createBuilder + buildApp() flow. | ||||||||||||||||
| buildOutputPath = await buildFontGoogleMultipleFixture(); | ||||||||||||||||
| outDir = path.dirname(path.dirname(buildOutputPath)); | ||||||||||||||||
|
|
||||||||||||||||
| // Verify the build produced output | ||||||||||||||||
| expect(buildOutputPath).toBeTruthy(); | ||||||||||||||||
| const stats = await fs.stat(buildOutputPath); | ||||||||||||||||
| expect(stats.isFile()).toBe(true); | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assertion only checks that the output file exists. After running a full
Suggested change
As-is,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assertion is too weak to justify a 2-minute full build.
Suggested change
|
||||||||||||||||
| }, 120000); // 2 minute timeout for full build | ||||||||||||||||
| }); | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is a reasonable defensive improvement — it ensures
overwrittenRangesis consistent afterinjectSelfHostedCssruns, preventing theoretical double-overwrite of overlapping ranges.However, it does not fix issue #751. I verified by reverting this line and running the new test — it still passes. The two font calls (
Geist(...)andGeist_Mono(...)) occupy non-overlapping character ranges in the original source, so the overlap check at line 757 was never triggered.The actual error from #751 (
Unexpected token in app/layout.tsx at 234..235) likely originates from a different part of the build pipeline. To find the real cause, someone needs to reproduce the build failure with the reproduction repo and trace the actual error through the Vite build.