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,6 @@
module.exports = {
onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60,
},
}

View File

@@ -0,0 +1,39 @@
import getPaths from '../../paths'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
if (router.isFallback) {
return 'Loading...'
}
return (
<>
<p id="props">{JSON.stringify(props)}</p>
<p id="router">
{JSON.stringify({
query: router.query,
asPath: router.asPath,
pathname: router.pathname,
})}
</p>
</>
)
}
export const getStaticProps = ({ params }) => {
return {
props: {
random: Math.random(),
params,
},
}
}
export const getStaticPaths = () => {
return {
paths: getPaths('/fallback-blocking'),
fallback: 'blocking',
}
}

View File

@@ -0,0 +1,39 @@
import { useRouter } from 'next/router'
import getPaths from '../../paths'
export default function Page(props) {
const router = useRouter()
if (router.isFallback) {
return 'Loading...'
}
return (
<>
<p id="props">{JSON.stringify(props)}</p>
<p id="router">
{JSON.stringify({
query: router.query,
asPath: router.asPath,
pathname: router.pathname,
})}
</p>
</>
)
}
export const getStaticProps = ({ params }) => {
return {
props: {
random: Math.random(),
params,
},
}
}
export const getStaticPaths = () => {
return {
paths: getPaths('/fallback-false'),
fallback: false,
}
}

View File

@@ -0,0 +1,39 @@
import getPaths from '../../paths'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
if (router.isFallback) {
return 'Loading...'
}
return (
<>
<p id="props">{JSON.stringify(props)}</p>
<p id="router">
{JSON.stringify({
query: router.query,
asPath: router.asPath,
pathname: router.pathname,
})}
</p>
</>
)
}
export const getStaticProps = ({ params }) => {
return {
props: {
random: Math.random(),
params,
},
}
}
export const getStaticPaths = () => {
return {
paths: getPaths('/fallback-true'),
fallback: true,
}
}

View File

@@ -0,0 +1,22 @@
export default function getPaths(pathPrefix) {
return [
// this will get turned into %2Fmy-post%2F
{ params: { slug: '/my-post/' } },
// this will get turned into %252Fmy-post%252F
{ params: { slug: '%2Fmy-post%2F' } },
// this will be passed through
{ params: { slug: '+my-post+' } },
// this will get turned into %3Fmy-post%3F
{ params: { slug: '?my-post?' } },
// ampersand signs
{ params: { slug: '&my-post&' } },
// non-ascii characters
{ params: { slug: '商業日語' } },
{ params: { slug: ' my-post ' } },
{ params: { slug: encodeURIComponent('商業日語') } },
`${pathPrefix}/%2Fsecond-post%2F`,
`${pathPrefix}/%2Bsecond-post%2B`,
`${pathPrefix}/%26second-post%26`,
`${pathPrefix}/mixed-${encodeURIComponent('商業日語')}`,
]
}

View File

@@ -0,0 +1,348 @@
/* eslint-env jest */
import fs from 'fs-extra'
import { join } from 'path'
import cheerio from 'cheerio'
import webdriver from 'next-webdriver'
import {
killApp,
findPort,
nextBuild,
launchApp,
nextStart,
fetchViaHTTP,
check,
} from 'next-test-utils'
const appDir = join(__dirname, '..')
let app
let appPort
let buildId
// paths on the filesystem
const prerenderedPaths = [
'%2Fmy-post%2F',
'%252Fmy-post%252F',
'+my-post+',
'%3Fmy-post%3F',
'&my-post&',
'商業日語',
encodeURIComponent('商業日語'),
' my-post ',
'%2Fsecond-post%2F',
'+second-post+',
'&second-post&',
'mixed-商業日語',
]
// paths that should be requested in the URL
const urlPaths = [
'%2Fmy-post%2F',
'%252Fmy-post%252F',
'%2Bmy-post%2B',
'%3Fmy-post%3F',
'%26my-post%26',
encodeURIComponent('商業日語'),
encodeURIComponent(encodeURIComponent('商業日語')),
'%20my-post%20',
'%2Fsecond-post%2F',
'%2Bsecond-post%2B',
'%26second-post%26',
`mixed-${encodeURIComponent('商業日語')}`,
]
const modePaths = ['fallback-blocking', 'fallback-false', 'fallback-true']
const pagesDir = join(appDir, '.next/server/pages')
function runTests(isDev: boolean) {
if (!isDev) {
it('should output paths correctly', async () => {
for (const path of prerenderedPaths) {
for (const mode of modePaths) {
console.log('checking output', { path, mode })
expect(fs.existsSync(join(pagesDir, mode, path + '.html'))).toBe(true)
expect(fs.existsSync(join(pagesDir, mode, path + '.json'))).toBe(true)
}
}
})
it('should handle non-prerendered paths correctly', async () => {
const prerenderedPaths = [
'%2Fanother-post%2F',
'+another-post+',
'%3Fanother-post%3F',
'&another-post&',
'商業日語商業日語',
]
const urlPaths = [
'%2Fanother-post%2F',
'%2Banother-post%2B',
'%3Fanother-post%3F',
'%26another-post%26',
encodeURIComponent('商業日語商業日語'),
]
for (const mode of modePaths) {
for (let i = 0; i < urlPaths.length; i++) {
const testSlug = urlPaths[i]
const path = prerenderedPaths[i]
const res = await fetchViaHTTP(
appPort,
`/_next/data/${buildId}/${mode}/${testSlug}.json`
)
if (mode === 'fallback-false') {
expect(res.status).toBe(404)
} else {
expect(res.status).toBe(200)
const { pageProps: props } = await res.json()
expect(props.params).toEqual({
slug: decodeURIComponent(testSlug),
})
if (!isDev) {
// we don't block on writing incremental data to the
// disk so use check
await check(
() => fs.existsSync(join(pagesDir, mode, path + '.html')),
true
)
await check(
() => fs.existsSync(join(pagesDir, mode, path + '.json')),
true
)
}
const browser = await webdriver(appPort, `/${mode}/${testSlug}`)
expect(
JSON.parse(await browser.elementByCss('#props').text()).params
).toEqual({
slug: decodeURIComponent(testSlug),
})
const browserRouter = JSON.parse(
await browser.elementByCss('#router').text()
)
expect(browserRouter.pathname).toBe(`/${mode}/[slug]`)
expect(browserRouter.asPath).toBe(`/${mode}/${testSlug}`)
expect(browserRouter.query).toEqual({
slug: decodeURIComponent(testSlug),
})
}
}
}
})
}
it('should respond with the prerendered pages correctly', async () => {
for (let i = 0; i < urlPaths.length; i++) {
const testSlug = urlPaths[i]
for (const mode of modePaths) {
const res = await fetchViaHTTP(
appPort,
`/${mode}/${testSlug}`,
undefined,
{
redirect: 'manual',
}
)
console.log('checking', { mode, testSlug })
expect(res.status).toBe(200)
const $ = cheerio.load(await res.text())
expect(JSON.parse($('#props').text()).params).toEqual({
slug: decodeURIComponent(testSlug),
})
const router = JSON.parse($('#router').text())
expect(router.pathname).toBe(`/${mode}/[slug]`)
expect(router.asPath).toBe(`/${mode}/${testSlug}`)
expect(router.query).toEqual({
slug: decodeURIComponent(testSlug),
})
}
}
})
it('should respond with the prerendered data correctly', async () => {
for (const path of urlPaths) {
for (const mode of modePaths) {
const res = await fetchViaHTTP(
appPort,
`/_next/data/${buildId}/${mode}/${path}.json`,
undefined,
{
redirect: 'manual',
}
)
expect(res.status).toBe(200)
const { pageProps: props } = await res.json()
expect(props.params).toEqual({
slug: decodeURIComponent(path),
})
}
}
})
it('should render correctly in the browser for prerender paths', async () => {
for (let i = 0; i < urlPaths.length; i++) {
const testSlug = urlPaths[i]
for (const mode of modePaths) {
const browser = await webdriver(appPort, `/${mode}/${testSlug}`)
expect(
JSON.parse(await browser.elementByCss('#props').text()).params
).toEqual({
slug: decodeURIComponent(testSlug),
})
const browserRouter = JSON.parse(
await browser.elementByCss('#router').text()
)
expect(browserRouter.pathname).toBe(`/${mode}/[slug]`)
expect(browserRouter.asPath).toBe(`/${mode}/${testSlug}`)
expect(browserRouter.query).toEqual({
slug: decodeURIComponent(testSlug),
})
}
}
})
it('should navigate client-side correctly with interpolating', async () => {
for (const mode of modePaths) {
const testSlug = urlPaths[0]
const browser = await webdriver(appPort, `/${mode}/${testSlug}`)
expect(
JSON.parse(await browser.elementByCss('#props').text()).params
).toEqual({
slug: decodeURIComponent(testSlug),
})
const browserRouter = JSON.parse(
await browser.elementByCss('#router').text()
)
expect(browserRouter.pathname).toBe(`/${mode}/[slug]`)
expect(browserRouter.asPath).toBe(`/${mode}/${testSlug}`)
expect(browserRouter.query).toEqual({
slug: decodeURIComponent(testSlug),
})
await browser.eval('window.beforeNav = 1')
for (const nextSlug of urlPaths) {
if (nextSlug === testSlug) continue
await browser.eval(`(function() {
window.next.router.push({
pathname: '/${mode}/[slug]',
query: { slug: '${decodeURIComponent(nextSlug)}' }
})
})()`)
await check(async () => {
const browserRouter = JSON.parse(
await browser.elementByCss('#router').text()
)
return browserRouter.asPath === `/${mode}/${nextSlug}`
? 'success'
: 'fail'
}, 'success')
expect(await browser.eval('window.beforeNav')).toBe(1)
}
}
})
it('should navigate client-side correctly with string href', async () => {
for (const mode of modePaths) {
const testSlug = urlPaths[0]
const browser = await webdriver(appPort, `/${mode}/${testSlug}`)
expect(
JSON.parse(await browser.elementByCss('#props').text()).params
).toEqual({
slug: decodeURIComponent(testSlug),
})
const browserRouter = JSON.parse(
await browser.elementByCss('#router').text()
)
expect(browserRouter.pathname).toBe(`/${mode}/[slug]`)
expect(browserRouter.asPath).toBe(`/${mode}/${testSlug}`)
expect(browserRouter.query).toEqual({
slug: decodeURIComponent(testSlug),
})
await browser.eval('window.beforeNav = 1')
for (const nextSlug of urlPaths) {
if (nextSlug === testSlug) continue
await browser.eval(`(function() {
window.next.router.push('/${mode}/${nextSlug}')
})()`)
await check(async () => {
const browserRouter = JSON.parse(
await browser.elementByCss('#router').text()
)
return browserRouter.asPath === `/${mode}/${nextSlug}`
? 'success'
: 'fail'
}, 'success')
expect(await browser.eval('window.beforeNav')).toBe(1)
}
}
})
}
describe('Fallback path encoding', () => {
;(process.env.TURBOPACK_BUILD ? describe.skip : describe)(
'development mode',
() => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
appPort = await findPort()
app = await launchApp(appDir, appPort)
buildId = 'development'
})
afterAll(() => killApp(app))
runTests(true)
}
)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
appPort = await findPort()
await nextBuild(appDir)
app = await nextStart(appDir, appPort)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(() => killApp(app))
runTests(false)
}
)
})