import path from 'path' import { nextTestSetup } from 'e2e-utils' describe('debug-build-paths', () => { describe('default fixture', () => { const { next, skipped } = nextTestSetup({ files: path.join(__dirname, 'fixtures/default'), skipDeployment: true, skipStart: true, }) if (skipped) return describe('explicit path formats', () => { it('should build single page with pages/ prefix', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', 'pages/foo.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should only build the specified page expect(buildResult.cliOutput).toContain('Route (pages)') expect(buildResult.cliOutput).toContain('○ /foo') // Should not build other pages expect(buildResult.cliOutput).not.toContain('○ /bar') // Should not build app routes expect(buildResult.cliOutput).not.toContain('Route (app)') }) it('should build multiple pages routes', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', 'pages/foo.tsx,pages/bar.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should build both specified pages expect(buildResult.cliOutput).toContain('Route (pages)') expect(buildResult.cliOutput).toContain('○ /foo') expect(buildResult.cliOutput).toContain('○ /bar') // Should not build app routes expect(buildResult.cliOutput).not.toContain('Route (app)') }) it('should build dynamic route with literal [slug] path', async () => { // Test that literal paths with brackets work without escaping // The path is checked for file existence before being treated as glob const buildResult = await next.build({ args: ['--debug-build-paths', 'app/blog/[slug]/page.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should build only the blog/[slug] route expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('/blog/[slug]') // Should not build other app routes expect(buildResult.cliOutput).not.toMatch(/○ \/\n/) expect(buildResult.cliOutput).not.toContain('○ /about') expect(buildResult.cliOutput).not.toContain('○ /dashboard') // Should not build pages routes expect(buildResult.cliOutput).not.toContain('Route (pages)') }) }) describe('glob pattern matching', () => { it('should match app and pages routes with glob patterns', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', 'pages/*.tsx,app/page.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should build pages matching the glob expect(buildResult.cliOutput).toContain('Route (pages)') expect(buildResult.cliOutput).toContain('○ /foo') expect(buildResult.cliOutput).toContain('○ /bar') // Should build the specified app route expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('○ /') // Should not build other app routes expect(buildResult.cliOutput).not.toContain('○ /about') expect(buildResult.cliOutput).not.toContain('○ /dashboard') }) it('should match nested routes with app/blog/**/page.tsx pattern', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', 'app/blog/**/page.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should build the blog route expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('/blog/[slug]') // Should not build other app routes (check for exact route, not substring) expect(buildResult.cliOutput).not.toMatch(/○ \/\n/) expect(buildResult.cliOutput).not.toContain('○ /about') expect(buildResult.cliOutput).not.toContain('○ /dashboard') // Should not build pages routes expect(buildResult.cliOutput).not.toContain('Route (pages)') }) it('should match dynamic routes with glob before brackets like app/**/[slug]/page.tsx', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', 'app/**/[slug]/page.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should build the blog/[slug] route expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('/blog/[slug]') // Should not build other app routes expect(buildResult.cliOutput).not.toMatch(/○ \/\n/) expect(buildResult.cliOutput).not.toContain('○ /about') expect(buildResult.cliOutput).not.toContain('○ /dashboard') // Should not build pages routes expect(buildResult.cliOutput).not.toContain('Route (pages)') }) it('should match hybrid pattern with literal [slug] and glob **', async () => { // Test pattern: app/blog/[slug]/**/page.tsx // [slug] should be treated as literal directory (exists on disk) // ** should be treated as glob (match any depth) const buildResult = await next.build({ args: ['--debug-build-paths', 'app/blog/[slug]/**/page.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should build both blog/[slug] and blog/[slug]/comments routes expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('/blog/[slug]') expect(buildResult.cliOutput).toContain('/blog/[slug]/comments') // Should not build other app routes expect(buildResult.cliOutput).not.toMatch(/○ \/\n/) expect(buildResult.cliOutput).not.toContain('○ /about') expect(buildResult.cliOutput).not.toContain('○ /dashboard') // Should not build pages routes expect(buildResult.cliOutput).not.toContain('Route (pages)') }) it('should match multiple app routes with explicit patterns', async () => { const buildResult = await next.build({ args: [ '--debug-build-paths', 'app/page.tsx,app/about/page.tsx,app/dashboard/page.tsx,app/blog/**/page.tsx', ], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toBeDefined() // Should build specified app routes expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('○ /') expect(buildResult.cliOutput).toContain('○ /about') expect(buildResult.cliOutput).toContain('○ /dashboard') expect(buildResult.cliOutput).toContain('/blog/[slug]') // Should not build routes not specified expect(buildResult.cliOutput).not.toContain('/with-type-error') // Should not build pages routes expect(buildResult.cliOutput).not.toContain('Route (pages)') }) it('should exclude paths matching negation patterns', async () => { const buildResult = await next.build({ args: [ '--debug-build-paths', 'app/**/page.tsx,!app/with-type-error/**', ], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('○ /') expect(buildResult.cliOutput).toContain('○ /about') expect(buildResult.cliOutput).toContain('○ /dashboard') expect(buildResult.cliOutput).toContain('/blog/[slug]') expect(buildResult.cliOutput).not.toContain('/with-type-error') }) it('should exclude dynamic route paths with negation', async () => { const buildResult = await next.build({ args: [ '--debug-build-paths', 'app/blog/**/page.tsx,!app/blog/[slug]/comments/**', ], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('/blog/[slug]') expect(buildResult.cliOutput).not.toContain('/blog/[slug]/comments') }) it('should support multiple negation patterns', async () => { const buildResult = await next.build({ args: [ '--debug-build-paths', 'app/**/page.tsx,!app/with-type-error/**,!app/dashboard/**', ], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('○ /') expect(buildResult.cliOutput).toContain('○ /about') expect(buildResult.cliOutput).not.toContain('/with-type-error') expect(buildResult.cliOutput).not.toContain('○ /dashboard') }) it('should build everything except excluded paths when only negation patterns are provided', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', '!app/with-type-error/**'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('Route (pages)') expect(buildResult.cliOutput).toContain('○ /') expect(buildResult.cliOutput).toContain('○ /about') expect(buildResult.cliOutput).toContain('○ /foo') expect(buildResult.cliOutput).not.toContain('/with-type-error') }) it('should build routes inside route groups', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', 'app/(group)/**/page.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (app)') // Route groups are stripped from the path, so /nested instead of /(group)/nested expect(buildResult.cliOutput).toContain('/nested') // Should not build other routes expect(buildResult.cliOutput).not.toContain('○ /about') expect(buildResult.cliOutput).not.toContain('○ /dashboard') }) it('should build routes with parallel routes', async () => { const buildResult = await next.build({ args: ['--debug-build-paths', 'app/parallel-test/**/page.tsx'], }) expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (app)') // Parallel route segments (@sidebar) are stripped from the path expect(buildResult.cliOutput).toContain('/parallel-test') // Should not build other routes expect(buildResult.cliOutput).not.toContain('○ /about') expect(buildResult.cliOutput).not.toContain('○ /dashboard') }) }) describe('typechecking with debug-build-paths', () => { it('should skip typechecking for excluded app routes', async () => { // Build only pages routes, excluding app routes with type error const buildResult = await next.build({ args: ['--debug-build-paths', 'pages/foo.tsx'], }) // Build should succeed because the file with type error is not checked expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (pages)') expect(buildResult.cliOutput).toContain('○ /foo') // Should not include app routes expect(buildResult.cliOutput).not.toContain('Route (app)') }) it('should fail typechecking when route with type error is included', async () => { // Build all app routes including the one with type error const buildResult = await next.build({ args: ['--debug-build-paths', 'app/**/page.tsx'], }) // Build should fail due to type error in with-type-error/page.tsx expect(buildResult.exitCode).toBe(1) expect(buildResult.cliOutput).toContain('Type error') expect(buildResult.cliOutput).toContain('with-type-error/page.tsx') }) }) }) describe('with-compile-error fixture', () => { const { next, skipped } = nextTestSetup({ files: path.join(__dirname, 'fixtures/with-compile-error'), skipDeployment: true, skipStart: true, }) if (skipped) return it('should skip compilation of excluded routes with compile errors', async () => { // Build only the valid page, excluding the broken page const buildResult = await next.build({ args: ['--debug-build-paths', 'app/valid/page.tsx'], }) // Build should succeed because the broken page is not compiled expect(buildResult.exitCode).toBe(0) expect(buildResult.cliOutput).toContain('Route (app)') expect(buildResult.cliOutput).toContain('○ /valid') // Should not include the broken route expect(buildResult.cliOutput).not.toContain('/broken') }) it('should fail compilation when route with compile error is included', async () => { // Build all app routes including the one with compile error const buildResult = await next.build({ args: ['--debug-build-paths', 'app/**/page.tsx'], }) // Build should fail due to compile error in broken/page.tsx expect(buildResult.exitCode).toBe(1) expect(buildResult.cliOutput).toMatch(/error|Error/) }) }) })