first commit
Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled

This commit is contained in:
Arian Tron
2026-03-10 19:37:31 +03:30
commit 61f56f997c
27684 changed files with 2784175 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
/* eslint-env jest */
import path from 'path'
import fs from 'fs-extra'
import { nextBuild } from 'next-test-utils'
const appDir = __dirname
// Turbopack doesn't support additional experimental features in the first version
describe('app type checking - production mode', () => {
let stderr, errors
beforeAll(async () => {
stderr = (await nextBuild(appDir, [], { stderr: true })).stderr
errors = stderr.match(
/===== TS errors =====(.+)===== TS errors =====/s
)?.[1]
})
it('should report link errors', async () => {
// Make sure the d.ts file is generated
const dts = (
await fs.readFile(path.join(appDir, '.next', 'types', 'link.d.ts'))
).toString()
expect(dts.includes('`/dashboard/user/')).toBeTruthy()
expect(dts.includes('`/dashboard/another')).toBeTruthy()
// Check type checking errors
expect(errors).toContain(
'Type error: "/(newroot)/dashboard/another" is not an existing route. If it is intentional, please type it explicitly with `as Route`.'
)
// Make sure all errors were reported and other links passed type checking
const errorLines = [
...errors.matchAll(/\.\/src\/app\/type-checks\/link\/page\.tsx:(\d+):/g),
].map(([, line]) => +line)
const ST = 18
const ED = 35
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
it('should generate route types correctly and report router API errors', async () => {
// Make sure all errors were reported and other links passed type checking
const errorLines = [
...errors.matchAll(
/\.\/src\/app\/type-checks\/router\/page\.tsx:(\d+):/g
),
].map(([, line]) => +line)
const ST = 11
const ED = 13
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
it('should generate route types correctly and report form errors', async () => {
// Make sure all errors were reported and other Forms passed type checking
const errorLines = [
...errors.matchAll(/\.\/src\/app\/type-checks\/form\/page\.tsx:(\d+):/g),
].map(([, line]) => +line)
const ST = 8
const ED = 10
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
it('should generate route types correctly and report redirect errors', async () => {
// Make sure all errors were reported and other redirect functions passed type checking
const errorLines = [
...errors.matchAll(
/\.\/src\/app\/type-checks\/redirect\/page\.tsx:(\d+):/g
),
].map(([, line]) => +line)
const ST = 7
const ED = 11
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
// Type validation is not supported in Turbopack yet.
if (!process.env.IS_TURBOPACK_TEST && !process.env.TURBOPACK_DEV) {
it('should type check invalid entry exports', () => {
// Can't export arbitrary things.
expect(errors).toContain(`"foo" is not a valid Page export field.`)
// Can't export invalid fields.
expect(errors).toMatch(
/Invalid configuration "revalidate":\s+Expected "false | number (>= 0)", got "-1"/
)
// Avoid invalid argument types for exported functions.
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "default" export:\s+Type "{ foo: string; }" is not valid/
)
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "generateMetadata" export:\s+Type "{ s: number; }" is not valid/
)
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "generateStaticParams" export:\s+Type "string" is not valid/
)
// Avoid invalid return types for exported functions.
expect(errors).toContain(
`"Promise<number>" is not a valid generateStaticParams return type`
)
// Can't export arbitrary things.
expect(errors).toContain(`"bar" is not a valid Route export field.`)
// Can't export invalid fields.
expect(errors).toMatch(
/Invalid configuration "revalidate":\s+Expected "false | number (>= 0)", got "-1"/
)
// Avoid invalid argument types for exported functions.
expect(errors).toMatch(
/Route "src\/app\/type-checks\/route-handlers\/route\.ts" has an invalid "GET" export:\s+Type "boolean" is not a valid type for the function's first argument/
)
expect(errors).toMatch(
/Route "src\/app\/type-checks\/route-handlers\/route\.ts" has an invalid "generateStaticParams" export:\s+Type "string" is not valid/
)
// Avoid invalid return types for exported functions.
expect(errors).toContain(
`"Promise<boolean>" is not a valid generateStaticParams return type`
)
})
}
})

View File

@@ -0,0 +1,48 @@
import createMdx from '@next/mdx'
const withMdx = createMdx()
export default withMdx({
typedRoutes: true,
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'mdx'],
async rewrites() {
return [
{
source: '/rewrite',
destination: 'https://nextjs.org',
},
{
source: '/rewrite-any/(.*)',
destination: 'https://nextjs.org',
},
{
source: '/rewrite-one-or-more/:param+',
destination: 'https://nextjs.org',
},
{
source: '/rewrite-all/:param*',
destination: 'https://nextjs.org',
},
{
source: '/rewrite-param/:param/page',
destination: 'https://nextjs.org',
},
]
},
async redirects() {
return [
{
source: '/redirect',
destination: 'https://nextjs.org',
permanent: false,
},
{
source: '/redirect(/v1)?/guides/:param/page',
destination: 'https://nextjs.org',
permanent: false,
},
]
},
})

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@@ -0,0 +1,7 @@
export default function Page() {
return (
<>
<p>hello from newroot/dashboard/another</p>
</>
)
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return null
}

View File

@@ -0,0 +1,3 @@
export default function page() {
return null
}

View File

@@ -0,0 +1,3 @@
export default function page() {
return null
}

View File

@@ -0,0 +1,3 @@
export default function page() {
return null
}

View File

@@ -0,0 +1,16 @@
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My App',
}
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<header>top bar</header>
{children}
</body>
</html>
)
}

View File

@@ -0,0 +1 @@
# Im a MDX page

View File

@@ -0,0 +1,11 @@
export default function Page({ foo }: { foo: string }) {}
export const revalidate = -1
export async function generateStaticParams(s: string) {
return 1
}
async function generateMetadata({ s }: { s: number }) {}
export { generateMetadata }
export const foo = 'bar'

View File

@@ -0,0 +1,5 @@
export const revalidate = 500_500
export default function Page() {
return null
}

View File

@@ -0,0 +1,36 @@
import * as React from 'react'
import type { Route } from 'next'
import Form from 'next/form'
export default function Page() {
const invalidRoutes = (
<>
<Form action="/wrong-link"></Form>
<Form action="/blog/a?1/b"></Form>
<Form action={`/blog/${'a/b/c'}`}></Form>
</>
)
const validRoutes = (
<>
<Form action="/dashboard/another"></Form>
<Form action="/about"></Form>
<Form action="/redirect"></Form>
<Form action={`/blog/${'a/b'}`}></Form>
<Form action={'/invalid' as Route}></Form>
<Form
action={async (formData) => {
'use server'
console.log('function action', formData.get('myInput'))
}}
></Form>
</>
)
return (
<>
{invalidRoutes}
{validRoutes}
</>
)
}

View File

@@ -0,0 +1,7 @@
import type { PropsWithChildren } from 'react'
type LayoutProps = PropsWithChildren<{}>
export default function Layout(props: LayoutProps) {
return <div>{props.children}</div>
}

View File

@@ -0,0 +1,81 @@
'use client'
import type { Route } from 'next'
import Link from 'next/link'
export function Card<T extends string>({ href }: { href: Route<T> | URL }) {
return (
<Link href={href}>
<div>My Card</div>
</Link>
)
}
export default function page() {
const test = 'a/b'
const shouldFail = (
<>
<Card href="/(newroot)/dashboard/another" />
<Card href="/dashboard" />
<Card href="/blog/a/b/c/d" />
<Link href="/typing">test</Link>
<Link href="/button">test</Link>
<Link href="/buttooon">test</Link>
<Link href="/blog/">test</Link>
<Link href="/blog/a?1/b">test</Link>
<Link href="/blog/a#1/b">test</Link>
<Link href="/blog/v/w/z">test</Link>
<Link href="/(newroot)/dashboard/another" />
<Link href="/dashboard">test</Link>
<Link href={`/blog/a/${test}`}>test</Link>
<Link href="/rewrite-any">test</Link>
<Link href="/rewrite-one-or-more">test</Link>
<Link href="/rewrite-param/page">test</Link>
<Link href="/rewrite-param/x/page1">test</Link>
<Link href="/redirect/v2/guides/x/page">test</Link>
</>
)
const shouldPass = (
<>
<Card href="/dashboard/another" />
<Card href="/aaa" />
<Card href="/blog/a/b?1" />
<Link href="/about">test</Link>
<Link href="/mdx-test" />
<Link href="/aaa#aaa">test</Link>
<Link href="/aaa?q=1">test</Link>
<Link href="/blog/a/b">test</Link>
<Link href="/blog/v/w">test</Link>
<Link href="/dashboard/another" />
<Link href="/dashboard/123">test</Link>
<Link href="/dashboard/user">test</Link>
<Link href="/dashboard/user/">test</Link>
<Link href="/dashboard/user/x">test</Link>
<Link href="/dashboard/x/x">test</Link>
<Link href={`/blog/${test}`}>test</Link>
<Link href={('/blog/' + test) as Route}>test</Link>
<Link href="/rewrite">test</Link>
<Link href="/rewrite-any/x">test</Link>
<Link href="/rewrite-one-or-more/x/y">test</Link>
<Link href="/rewrite-all/x/y/z">test</Link>
<Link href="/rewrite-param/x/page?1">test</Link>
<Link href="/redirect">test</Link>
<Link href="/redirect/v1/guides/x/page">test</Link>
<Link href="/redirect/guides/x/page">test</Link>
<Link href={new URL('https://nextjs.org')}>test</Link>
<Link href="https://nextjs.org">test</Link>
<Link href="http://nextjs.org">test</Link>
<Link href="#id">test</Link>
<Link href="?page=1">test</Link>
<Link href="mailto:foo@example.com">test</Link>
</>
)
return (
<>
{shouldFail}
{shouldPass}
</>
)
}

View File

@@ -0,0 +1,32 @@
import { redirect, permanentRedirect, RedirectType } from 'next/navigation'
import type { Route } from 'next'
export default function Page() {
function testRedirect() {
// Invalid routes - these should cause type errors:
redirect('/wrong-link')
redirect('/blog/a?1/b')
redirect(`/blog/${'a/b/c'}`)
permanentRedirect('/nonexistent-route')
permanentRedirect('/wrong/route')
// Correctly typed - these should pass:
redirect('/dashboard/another')
redirect('/about')
redirect('/redirect')
redirect(`/blog/${'a/b'}`)
redirect('https://vercel.com')
redirect('/invalid' as Route)
permanentRedirect('/dashboard/user')
permanentRedirect('/blog/a/b')
permanentRedirect(`/dashboard/${'123'}`)
permanentRedirect('/external' as Route)
// RedirectType should be correctly typed as literal types:
redirect('/dashboard/another', RedirectType.replace)
redirect('/about', RedirectType.push)
permanentRedirect('/dashboard/user', RedirectType.replace)
}
return <div onClick={testRedirect} />
}

View File

@@ -0,0 +1,16 @@
import type { NextRequest } from 'next/server'
export const revalidate = -1
export async function generateStaticParams(s: string) {
return false
}
export function bar() {}
export function GET(request: boolean) {}
export function POST(request: NextRequest) {}
export function PUT(request: Request, { foo }) {}
export function DELETE(request: Request) {}

View File

@@ -0,0 +1,25 @@
'use client'
import { useRouter } from 'next/navigation'
import type { Route } from 'next'
export default function Page() {
const router = useRouter()
function test() {
// Invalid routes:
router.push('/wrong-link')
router.push('/blog/a?1/b')
router.push(`/blog/${'a/b/c'}`)
// Correctly typed:
router.push('/dashboard/another')
router.prefetch('/about')
router.push('/redirect')
router.push(`/blog/${'a/b'}`)
router.push('/invalid' as Route)
router.back()
}
return <div onClick={test} />
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return null
}

View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true,
"target": "ES2017"
},
"include": [
"next-env.d.ts",
".next/types/**/*.ts",
"**/*.ts",
"**/*.tsx",
".next/dev/types/**/*.ts"
],
"exclude": ["node_modules", "**/*.test.ts", "**/*.test.tsx"]
}