Skip to content

Commit 9868eb6

Browse files
committed
debug paths glob
1 parent 78660a5 commit 9868eb6

File tree

7 files changed

+167
-4
lines changed

7 files changed

+167
-4
lines changed

packages/next/src/build/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,8 @@ export default async function build(
11321132
nextBuildSpan,
11331133
config,
11341134
cacheDir,
1135+
debugBuildAppPaths,
1136+
debugBuildPagePaths,
11351137
}
11361138

11371139
if (appDir && 'exportPathMap' in config) {

packages/next/src/build/type-check.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ function verifyTypeScriptSetup(
2828
enableWorkerThreads: boolean | undefined,
2929
hasAppDir: boolean,
3030
hasPagesDir: boolean,
31-
isolatedDevBuild: boolean | undefined
31+
isolatedDevBuild: boolean | undefined,
32+
debugBuildAppPaths: string[] | undefined,
33+
debugBuildPagePaths: string[] | undefined
3234
) {
3335
const typeCheckWorker = new Worker(
3436
require.resolve('../lib/verify-typescript-setup'),
@@ -56,6 +58,8 @@ function verifyTypeScriptSetup(
5658
hasAppDir,
5759
hasPagesDir,
5860
isolatedDevBuild,
61+
debugBuildAppPaths,
62+
debugBuildPagePaths,
5963
})
6064
.then((result) => {
6165
typeCheckWorker.end()
@@ -76,6 +80,8 @@ export async function startTypeChecking({
7680
pagesDir,
7781
telemetry,
7882
appDir,
83+
debugBuildAppPaths,
84+
debugBuildPagePaths,
7985
}: {
8086
cacheDir: string
8187
config: NextConfigComplete
@@ -84,6 +90,8 @@ export async function startTypeChecking({
8490
pagesDir?: string
8591
telemetry: Telemetry
8692
appDir?: string
93+
debugBuildAppPaths?: string[]
94+
debugBuildPagePaths?: string[]
8795
}) {
8896
const ignoreTypeScriptErrors = Boolean(config.typescript.ignoreBuildErrors)
8997

@@ -119,7 +127,9 @@ export async function startTypeChecking({
119127
config.experimental.workerThreads,
120128
!!appDir,
121129
!!pagesDir,
122-
config.experimental.isolatedDevBuild
130+
config.experimental.isolatedDevBuild,
131+
debugBuildAppPaths,
132+
debugBuildPagePaths
123133
).then((resolved) => {
124134
const checkEnd = process.hrtime(typeCheckAndLintStart)
125135
return [resolved, checkEnd] as const

packages/next/src/lib/typescript/runTypeCheck.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ export async function runTypeCheck(
2323
tsConfigPath: string,
2424
cacheDir?: string,
2525
isAppDirEnabled?: boolean,
26-
isolatedDevBuild?: boolean
26+
isolatedDevBuild?: boolean,
27+
intentDirs?: string[],
28+
hasPagesDir?: boolean,
29+
debugBuildAppPaths?: string[],
30+
debugBuildPagePaths?: string[]
2731
): Promise<TypeCheckResult> {
2832
const effectiveConfiguration = await getTypeScriptConfiguration(
2933
typescript,
@@ -52,6 +56,65 @@ export async function runTypeCheck(
5256
)
5357
}
5458

59+
// Apply debug build paths filter if specified
60+
if (
61+
intentDirs &&
62+
(debugBuildAppPaths !== undefined || debugBuildPagePaths !== undefined)
63+
) {
64+
// Determine appDir and pagesDir from intentDirs
65+
// intentDirs is [pagesDir, appDir].filter(Boolean) from type-check.ts
66+
let pagesDir: string | undefined
67+
let appDir: string | undefined
68+
69+
if (hasPagesDir && isAppDirEnabled) {
70+
pagesDir = intentDirs[0]
71+
appDir = intentDirs[1]
72+
} else if (hasPagesDir) {
73+
pagesDir = intentDirs[0]
74+
} else if (isAppDirEnabled) {
75+
appDir = intentDirs[0]
76+
}
77+
78+
fileNames = fileNames.filter((fileName) => {
79+
// Check if file is in app directory
80+
if (appDir && fileName.startsWith(appDir + path.sep)) {
81+
// If debugBuildAppPaths is undefined, include all app files
82+
if (debugBuildAppPaths === undefined) {
83+
return true
84+
}
85+
// If debugBuildAppPaths is empty array, exclude all app files
86+
if (debugBuildAppPaths.length === 0) {
87+
return false
88+
}
89+
// Check if file matches any of the debug paths
90+
const relativeToApp = fileName.slice(appDir.length)
91+
return debugBuildAppPaths.some((debugPath) =>
92+
relativeToApp.startsWith(debugPath.replace(/\.[^/.]+$/, ''))
93+
)
94+
}
95+
96+
// Check if file is in pages directory
97+
if (pagesDir && fileName.startsWith(pagesDir + path.sep)) {
98+
// If debugBuildPagePaths is undefined, include all pages files
99+
if (debugBuildPagePaths === undefined) {
100+
return true
101+
}
102+
// If debugBuildPagePaths is empty array, exclude all pages files
103+
if (debugBuildPagePaths.length === 0) {
104+
return false
105+
}
106+
// Check if file matches any of the debug paths
107+
const relativeToPages = fileName.slice(pagesDir.length)
108+
return debugBuildPagePaths.some((debugPath) =>
109+
relativeToPages.startsWith(debugPath.replace(/\.[^/.]+$/, ''))
110+
)
111+
}
112+
113+
// Keep files outside app/pages directories (shared code, etc.)
114+
return true
115+
})
116+
}
117+
55118
if (fileNames.length < 1) {
56119
return {
57120
hasWarnings: false,

packages/next/src/lib/verify-typescript-setup.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export async function verifyTypeScriptSetup({
4444
hasAppDir,
4545
hasPagesDir,
4646
isolatedDevBuild,
47+
debugBuildAppPaths,
48+
debugBuildPagePaths,
4749
}: {
4850
dir: string
4951
distDir: string
@@ -55,6 +57,8 @@ export async function verifyTypeScriptSetup({
5557
hasAppDir: boolean
5658
hasPagesDir: boolean
5759
isolatedDevBuild: boolean | undefined
60+
debugBuildAppPaths?: string[]
61+
debugBuildPagePaths?: string[]
5862
}): Promise<{ result?: TypeCheckResult; version: string | null }> {
5963
const tsConfigFileName = tsconfigPath || 'tsconfig.json'
6064
const resolvedTsConfigPath = path.join(dir, tsConfigFileName)
@@ -159,7 +163,11 @@ export async function verifyTypeScriptSetup({
159163
resolvedTsConfigPath,
160164
cacheDir,
161165
hasAppDir,
162-
isolatedDevBuild
166+
isolatedDevBuild,
167+
intentDirs,
168+
hasPagesDir,
169+
debugBuildAppPaths,
170+
debugBuildPagePaths
163171
)
164172
}
165173
return { result, version: typescriptVersion }
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default async function BlogPost({
2+
params,
3+
}: {
4+
params: Promise<{ slug: string }>
5+
}) {
6+
const { slug } = await params
7+
return <h1>Blog Post: {slug}</h1>
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// This file has an intentional type error
2+
const invalidValue: string = 123
3+
4+
export default function WithTypeError() {
5+
return <div>WithTypeError: {invalidValue}</div>
6+
}

test/production/debug-build-path/debug-build-paths.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,71 @@ describe('debug-build-paths', () => {
6262
expect(buildResult.cliOutput).not.toContain('○ /about')
6363
expect(buildResult.cliOutput).not.toContain('○ /dashboard')
6464
})
65+
66+
it('should match nested routes with app/blog/**/page.tsx pattern', async () => {
67+
const buildResult = await next.build({
68+
args: ['--debug-build-paths', 'app/blog/**/page.tsx'],
69+
})
70+
expect(buildResult.exitCode).toBe(0)
71+
expect(buildResult.cliOutput).toBeDefined()
72+
73+
// Should build the blog route
74+
expect(buildResult.cliOutput).toContain('Route (app)')
75+
expect(buildResult.cliOutput).toContain('/blog/[slug]')
76+
// Should not build other app routes (check for exact route, not substring)
77+
expect(buildResult.cliOutput).not.toMatch(/ \/\n/)
78+
expect(buildResult.cliOutput).not.toContain('○ /about')
79+
expect(buildResult.cliOutput).not.toContain('○ /dashboard')
80+
// Should not build pages routes
81+
expect(buildResult.cliOutput).not.toContain('Route (pages)')
82+
})
83+
84+
it('should match multiple app routes with explicit patterns', async () => {
85+
const buildResult = await next.build({
86+
args: [
87+
'--debug-build-paths',
88+
'app/page.tsx,app/about/page.tsx,app/dashboard/page.tsx,app/blog/**/page.tsx',
89+
],
90+
})
91+
expect(buildResult.exitCode).toBe(0)
92+
expect(buildResult.cliOutput).toBeDefined()
93+
94+
// Should build specified app routes
95+
expect(buildResult.cliOutput).toContain('Route (app)')
96+
expect(buildResult.cliOutput).toContain('○ /')
97+
expect(buildResult.cliOutput).toContain('○ /about')
98+
expect(buildResult.cliOutput).toContain('○ /dashboard')
99+
expect(buildResult.cliOutput).toContain('/blog/[slug]')
100+
// Should not build routes not specified
101+
expect(buildResult.cliOutput).not.toContain('/with-type-error')
102+
// Should not build pages routes
103+
expect(buildResult.cliOutput).not.toContain('Route (pages)')
104+
})
105+
})
106+
107+
describe('typechecking with debug-build-paths', () => {
108+
it('should skip typechecking for excluded app routes', async () => {
109+
// Build only pages routes, excluding app routes with type error
110+
const buildResult = await next.build({
111+
args: ['--debug-build-paths', 'pages/foo.tsx'],
112+
})
113+
// Build should succeed because the file with type error is not checked
114+
expect(buildResult.exitCode).toBe(0)
115+
expect(buildResult.cliOutput).toContain('Route (pages)')
116+
expect(buildResult.cliOutput).toContain('○ /foo')
117+
// Should not include app routes
118+
expect(buildResult.cliOutput).not.toContain('Route (app)')
119+
})
120+
121+
it('should fail typechecking when route with type error is included', async () => {
122+
// Build all app routes including the one with type error
123+
const buildResult = await next.build({
124+
args: ['--debug-build-paths', 'app/**/page.tsx'],
125+
})
126+
// Build should fail due to type error in with-type-error/page.tsx
127+
expect(buildResult.exitCode).toBe(1)
128+
expect(buildResult.cliOutput).toContain('Type error')
129+
expect(buildResult.cliOutput).toContain('with-type-error/page.tsx')
130+
})
65131
})
66132
})

0 commit comments

Comments
 (0)