Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ import {
createRouteTypesManifest,
writeRouteTypesManifest,
writeValidatorFile,
writeDynamicTypesFile,
} from '../server/lib/router-utils/route-types-utils'
import { Lockfile } from './lockfile'
import {
Expand Down Expand Up @@ -1469,6 +1470,12 @@ export default async function build(
config
)
await writeValidatorFile(routeTypesManifest, validatorFilePath)
await writeDynamicTypesFile({
distDir,
imageImportsEnabled: !config.images.disableStaticImages,
hasPagesDir: !!pagesDir,
hasAppDir: !!appDir,
})
})

// Turbopack already handles conflicting app and page routes.
Expand Down
3 changes: 0 additions & 3 deletions packages/next/src/build/type-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ function verifyTypeScriptSetup(
intentDirs: string[],
typeCheckPreflight: boolean,
tsconfigPath: string | undefined,
disableStaticImages: boolean,
cacheDir: string | undefined,
enableWorkerThreads: boolean | undefined,
hasAppDir: boolean,
Expand Down Expand Up @@ -51,7 +50,6 @@ function verifyTypeScriptSetup(
intentDirs,
typeCheckPreflight,
tsconfigPath,
disableStaticImages,
cacheDir,
hasAppDir,
hasPagesDir,
Expand Down Expand Up @@ -114,7 +112,6 @@ export async function startTypeChecking({
[pagesDir, appDir].filter(Boolean) as string[],
!ignoreTypeScriptErrors,
config.typescript.tsconfigPath,
config.images.disableStaticImages,
cacheDir,
config.experimental.workerThreads,
!!appDir,
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/cli/next-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ async function runPlaywright(
intentDirs: [pagesDir, appDir].filter(Boolean) as string[],
typeCheckPreflight: false,
tsconfigPath: nextConfig.typescript.tsconfigPath,
disableStaticImages: nextConfig.images.disableStaticImages,
hasAppDir: !!appDir,
hasPagesDir: !!pagesDir,
isolatedDevBuild: nextConfig.experimental.isolatedDevBuild,
Expand Down
9 changes: 8 additions & 1 deletion packages/next/src/cli/next-typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
createRouteTypesManifest,
writeRouteTypesManifest,
writeValidatorFile,
writeDynamicTypesFile,
} from '../server/lib/router-utils/route-types-utils'
import { writeCacheLifeTypes } from '../server/lib/router-utils/cache-life-type-utils'
import { createValidFileMatcher } from '../server/lib/find-page-file'
Expand Down Expand Up @@ -60,7 +61,6 @@ const nextTypegen = async (
intentDirs: [pagesDir, appDir].filter(Boolean) as string[],
typeCheckPreflight: false,
tsconfigPath: nextConfig.typescript.tsconfigPath,
disableStaticImages: nextConfig.images.disableStaticImages,
hasAppDir: !!appDir,
hasPagesDir: !!pagesDir,
isolatedDevBuild: nextConfig.experimental.isolatedDevBuild,
Expand Down Expand Up @@ -170,6 +170,13 @@ const nextTypegen = async (

await writeValidatorFile(routeTypesManifest, validatorFilePath)

await writeDynamicTypesFile({
distDir,
imageImportsEnabled: !nextConfig.images.disableStaticImages,
hasPagesDir: !!pagesDir,
hasAppDir: !!appDir,
})

// Generate cache-life types if cacheLife config exists
const cacheLifeFilePath = join(distDir, 'types', 'cache-life.d.ts')
writeCacheLifeTypes(nextConfig.cacheLife, cacheLifeFilePath)
Expand Down
82 changes: 0 additions & 82 deletions packages/next/src/lib/typescript/writeAppTypeDeclarations.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ describe('writeConfigurationDefaults()', () => {
"node_modules",
],
"include": [
"next-env.d.ts",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts",
Expand All @@ -107,7 +106,7 @@ describe('writeConfigurationDefaults()', () => {
- strict was set to false
- noEmit was set to true
- incremental was set to true
- include was set to ['next-env.d.ts', '.next/types/**/*.ts', '.next/dev/types/**/*.ts', '**/*.mts', '**/*.ts', '**/*.tsx']
- include was set to ['.next/types/**/*.ts', '.next/dev/types/**/*.ts', '**/*.mts', '**/*.ts', '**/*.tsx']
- plugins was updated to add { name: 'next' }
- exclude was set to ['node_modules']
Expand Down
11 changes: 4 additions & 7 deletions packages/next/src/lib/typescript/writeConfigurationDefaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,19 +282,16 @@ export async function writeConfigurationDefaults(
)

if (!('include' in userTsConfig)) {
userTsConfig.include = hasAppDir
? ['next-env.d.ts', ...nextAppTypes, '**/*.mts', '**/*.ts', '**/*.tsx']
: ['next-env.d.ts', '**/*.mts', '**/*.ts', '**/*.tsx']
// Always include .next/types for dynamic types (image imports, route types, etc.)
userTsConfig.include = [...nextAppTypes, '**/*.mts', '**/*.ts', '**/*.tsx']
suggestedActions.push(
cyan('include') +
' was set to ' +
bold(
hasAppDir
? `['next-env.d.ts', ${nextAppTypes.map((type) => `'${type}'`).join(', ')}, '**/*.mts', '**/*.ts', '**/*.tsx']`
: `['next-env.d.ts', '**/*.mts', '**/*.ts', '**/*.tsx']`
`[${nextAppTypes.map((type) => `'${type}'`).join(', ')}, '**/*.mts', '**/*.ts', '**/*.tsx']`
)
)
} else if (hasAppDir) {
} else {
const missingFromResolved = []
for (const type of nextAppTypes) {
if (!userTsConfig.include.includes(type)) {
Expand Down
13 changes: 0 additions & 13 deletions packages/next/src/lib/verify-typescript-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import * as log from '../build/output/log'

import { getTypeScriptIntent } from './typescript/getTypeScriptIntent'
import type { TypeCheckResult } from './typescript/runTypeCheck'
import { writeAppTypeDeclarations } from './typescript/writeAppTypeDeclarations'
import { writeConfigurationDefaults } from './typescript/writeConfigurationDefaults'
import { installDependencies } from './install-dependencies'
import { isCI } from '../server/ci-info'
Expand Down Expand Up @@ -40,7 +39,6 @@ export async function verifyTypeScriptSetup({
intentDirs,
tsconfigPath,
typeCheckPreflight,
disableStaticImages,
hasAppDir,
hasPagesDir,
isolatedDevBuild,
Expand All @@ -51,7 +49,6 @@ export async function verifyTypeScriptSetup({
tsconfigPath: string | undefined
intentDirs: string[]
typeCheckPreflight: boolean
disableStaticImages: boolean
hasAppDir: boolean
hasPagesDir: boolean
isolatedDevBuild: boolean | undefined
Expand Down Expand Up @@ -131,16 +128,6 @@ export async function verifyTypeScriptSetup({
hasPagesDir,
isolatedDevBuild
)
// Write out the necessary `next-env.d.ts` file to correctly register
// Next.js' types:
await writeAppTypeDeclarations({
baseDir: dir,
distDir,
imageImportsEnabled: !disableStaticImages,
hasPagesDir,
hasAppDir,
})

let result
if (typeCheckPreflight) {
const { runTypeCheck } =
Expand Down
33 changes: 33 additions & 0 deletions packages/next/src/server/lib/router-utils/route-types-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
generateRouteTypesFile,
generateLinkTypesFile,
generateValidatorFile,
generateDynamicTypesFile,
} from './typegen'
import { tryToParsePath } from '../../../lib/try-to-parse-path'
import {
Expand Down Expand Up @@ -382,3 +383,35 @@ export async function writeValidatorFile(

await fs.promises.writeFile(filePath, generateValidatorFile(manifest))
}

/**
* Writes the dynamic types file (.next/types/next-env.d.ts) that contains
* config-dependent type references (image imports, navigation compat, etc.)
*/
export async function writeDynamicTypesFile({
distDir,
imageImportsEnabled,
hasPagesDir,
hasAppDir,
}: {
distDir: string
imageImportsEnabled: boolean
hasPagesDir: boolean
hasAppDir: boolean
}) {
const typesDir = path.join(distDir, 'types')
const filePath = path.join(typesDir, 'next-env.d.ts')

// Directory should already be created by writeRouteTypesManifest, but ensure it exists
if (!fs.existsSync(typesDir)) {
await fs.promises.mkdir(typesDir, { recursive: true })
}

const content = generateDynamicTypesFile({
imageImportsEnabled,
hasPagesDir,
hasAppDir,
})

await fs.promises.writeFile(filePath, content)
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import {
createRouteTypesManifest,
writeRouteTypesManifest,
writeValidatorFile,
writeDynamicTypesFile,
} from './route-types-utils'
import { writeCacheLifeTypes } from './cache-life-type-utils'
import { isParallelRouteSegment } from '../../../shared/lib/segment'
Expand Down Expand Up @@ -146,7 +147,6 @@ async function verifyTypeScript(opts: SetupOpts) {
intentDirs: [opts.pagesDir, opts.appDir].filter(Boolean) as string[],
typeCheckPreflight: false,
tsconfigPath: opts.nextConfig.typescript.tsconfigPath,
disableStaticImages: opts.nextConfig.images.disableStaticImages,
hasAppDir: !!opts.appDir,
hasPagesDir: !!opts.pagesDir,
isolatedDevBuild: opts.nextConfig.experimental.isolatedDevBuild,
Expand Down Expand Up @@ -260,6 +260,12 @@ async function startWatcher(
path.join(distTypesDir, 'routes.d.ts'),
opts.nextConfig
)
await writeDynamicTypesFile({
distDir,
imageImportsEnabled: !opts.nextConfig.images.disableStaticImages,
hasPagesDir: !!pagesDir,
hasAppDir: !!appDir,
})

const routesManifestPath = path.join(distDir, ROUTES_MANIFEST)
const routesManifest: DevRoutesManifest = {
Expand Down
39 changes: 38 additions & 1 deletion packages/next/src/server/lib/router-utils/typegen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
import type { RouteTypesManifest } from './route-types-utils'
import { isDynamicRoute } from '../../../shared/lib/router/utils/is-dynamic'

/**
* Generates the content for .next/types/next-env.d.ts
* This file contains dynamic type references that depend on project configuration.
*/
export function generateDynamicTypesFile({
imageImportsEnabled,
hasPagesDir,
hasAppDir,
}: {
imageImportsEnabled: boolean
hasPagesDir: boolean
hasAppDir: boolean
}): string {
const directives: string[] = [
// Include the core Next.js typings.
'/// <reference types="next" />',
'// This file is generated automatically by Next.js',
'// Do not edit this file manually',
'',
]

if (imageImportsEnabled) {
directives.push('/// <reference types="next/image-types/global" />')
}

if (hasAppDir && hasPagesDir) {
directives.push(
'/// <reference types="next/navigation-types/compat/navigation" />'
)
}

// Import routes types from the same directory
directives.push('import "./routes.d.ts"')

return directives.join('\n') + '\n'
}

function generateRouteTypes(routesManifest: RouteTypesManifest): string {
const appRoutes = Object.keys(routesManifest.appRoutes).sort()
const pageRoutes = Object.keys(routesManifest.pageRoutes).sort()
Expand Down Expand Up @@ -574,7 +611,7 @@ export function generateValidatorFile(
default: (req: any, res: any) => ReturnType<NextApiHandler>
config?: {
api?: {
bodyParser?: boolean | { sizeLimit?: string }
bodyParser?: boolean | { sizeLimit?: string | number }
responseLimit?: string | number | boolean
externalResolver?: boolean
}
Expand Down
Loading
Loading