@@ -235,4 +235,169 @@ export default handler;
235235 expect ( buildOutput ) . not . toContain ( 'import.meta.glob("@/content/posts/**/*.mdx"' ) ;
236236 expect ( buildOutput ) . not . toContain ( "@/content/posts/" ) ;
237237 } , 60_000 ) ;
238+
239+ it ( "import.meta.glob with MDX files containing frontmatter does not cause parse errors (issue #659)" , async ( ) => {
240+ const root = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "vinext-mdx-frontmatter-build-" ) ) ;
241+ tmpDirs . push ( root ) ;
242+ const workerEntryPath = path
243+ . resolve ( import . meta. dirname , "../packages/vinext/src/server/app-router-entry.ts" )
244+ . replace ( / \\ / g, "/" ) ;
245+ const cfPluginPath = path . resolve (
246+ import . meta. dirname ,
247+ "./fixtures/cf-app-basic/node_modules/@cloudflare/vite-plugin/dist/index.mjs" ,
248+ ) ;
249+ const { cloudflare } = ( await import ( pathToFileURL ( cfPluginPath ) . href ) ) as {
250+ cloudflare : ( opts ?: {
251+ viteEnvironment ?: { name : string ; childEnvironments ?: string [ ] } ;
252+ } ) => import ( "vite" ) . Plugin ;
253+ } ;
254+
255+ fs . symlinkSync (
256+ path . resolve ( import . meta. dirname , "../node_modules" ) ,
257+ path . join ( root , "node_modules" ) ,
258+ "junction" ,
259+ ) ;
260+
261+ writeFixtureFile (
262+ root ,
263+ "package.json" ,
264+ JSON . stringify (
265+ {
266+ name : "vinext-mdx-frontmatter-test" ,
267+ private : true ,
268+ type : "module" ,
269+ } ,
270+ null ,
271+ 2 ,
272+ ) ,
273+ ) ;
274+ writeFixtureFile (
275+ root ,
276+ "wrangler.jsonc" ,
277+ `{
278+ "name": "vinext-mdx-frontmatter-test",
279+ "compatibility_date": "2026-02-12",
280+ "compatibility_flags": ["nodejs_compat"],
281+ "main": "./worker/index.ts",
282+ "assets": {
283+ "not_found_handling": "none",
284+ "binding": "ASSETS"
285+ }
286+ }
287+ ` ,
288+ ) ;
289+ writeFixtureFile (
290+ root ,
291+ "tsconfig.json" ,
292+ JSON . stringify (
293+ {
294+ compilerOptions : {
295+ target : "ES2022" ,
296+ module : "ESNext" ,
297+ moduleResolution : "bundler" ,
298+ jsx : "react-jsx" ,
299+ strict : true ,
300+ skipLibCheck : true ,
301+ types : [ "vite/client" , "@vitejs/plugin-rsc/types" ] ,
302+ paths : {
303+ "@/*" : [ "./*" ] ,
304+ } ,
305+ } ,
306+ include : [ "app" , "lib" , "content" , "*.ts" , "*.tsx" ] ,
307+ } ,
308+ null ,
309+ 2 ,
310+ ) ,
311+ ) ;
312+ writeFixtureFile (
313+ root ,
314+ "app/layout.tsx" ,
315+ `export default function RootLayout({
316+ children,
317+ }: {
318+ children: React.ReactNode;
319+ }) {
320+ return (
321+ <html lang="en">
322+ <body>{children}</body>
323+ </html>
324+ );
325+ }
326+ ` ,
327+ ) ;
328+ writeFixtureFile (
329+ root ,
330+ "app/page.tsx" ,
331+ `import { getGlobPostCount } from "../lib/mdx-loader";
332+
333+ export default function HomePage() {
334+ return <main>home {getGlobPostCount()}</main>;
335+ }
336+ ` ,
337+ ) ;
338+ writeFixtureFile (
339+ root ,
340+ "app/mdx-probe/page.mdx" ,
341+ `# Probe
342+
343+ This file exists only to trigger vinext's MDX auto-detection.
344+ ` ,
345+ ) ;
346+ writeFixtureFile (
347+ root ,
348+ "lib/mdx-loader.ts" ,
349+ `type MdxModule = {
350+ default: React.ComponentType;
351+ frontmatter?: {
352+ title: string;
353+ date: string;
354+ };
355+ };
356+
357+ export const mdxModules = import.meta.glob("@/content/posts/**/*.mdx", {
358+ eager: true,
359+ }) as Record<string, MdxModule>;
360+
361+ export function getGlobPostCount(): number {
362+ return Object.keys(mdxModules).length;
363+ }
364+ ` ,
365+ ) ;
366+ writeFixtureFile (
367+ root ,
368+ "content/posts/2025/08/20/second-post/index.mdx" ,
369+ `---
370+ title: "Second Post"
371+ date: "2025-08-20"
372+ ---
373+
374+ <span className="text-red-500">This is a post with frontmatter and JSX.</span>
375+ ` ,
376+ ) ;
377+ writeFixtureFile (
378+ root ,
379+ "worker/index.ts" ,
380+ `import handler from ${ JSON . stringify ( workerEntryPath ) } ;
381+
382+ export default handler;
383+ ` ,
384+ ) ;
385+
386+ const builder = await createBuilder ( {
387+ root,
388+ configFile : false ,
389+ plugins : [
390+ vinext ( { appDir : root } ) ,
391+ cloudflare ( { viteEnvironment : { name : "rsc" , childEnvironments : [ "ssr" ] } } ) ,
392+ ] ,
393+ logLevel : "silent" ,
394+ } ) ;
395+ await builder . buildApp ( ) ;
396+
397+ const buildOutput = readTextFilesRecursive ( path . join ( root , "dist" ) ) ;
398+ expect ( buildOutput ) . not . toContain ( 'import.meta.glob("@/content/posts/**/*.mdx"' ) ;
399+ expect ( buildOutput ) . not . toContain ( "@/content/posts/" ) ;
400+ expect ( buildOutput ) . not . toContain ( "---" ) ;
401+ expect ( buildOutput ) . not . toContain ( "text-red-500" ) ;
402+ } , 60_000 ) ;
238403} ) ;
0 commit comments