fix: app dir with next dev and output: export (#47171)

This PR is a follow up to PR #47022 which broke `next dev`.

A test has been added to confirm `next dev` works as expected.

fix NEXT-825 ([link](https://linear.app/vercel/issue/NEXT-825)) ([NEXT-825](https://linear.app/vercel/issue/NEXT-825))
This commit is contained in:
Steven 2023-03-15 20:36:08 -04:00 committed by GitHub
parent 0e91549397
commit 9a89c4933d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 22 deletions

View file

@ -39,12 +39,14 @@ export async function fetchServerResponse(
try {
let fetchUrl = url
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
fetchUrl = new URL(url) // clone
if (fetchUrl.pathname.endsWith('/')) {
fetchUrl.pathname += 'index.txt'
} else {
fetchUrl.pathname += '.txt'
if (process.env.NODE_ENV === 'production') {
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
fetchUrl = new URL(url) // clone
if (fetchUrl.pathname.endsWith('/')) {
fetchUrl.pathname += 'index.txt'
} else {
fetchUrl.pathname += '.txt'
}
}
}
const res = await fetch(fetchUrl, {
@ -59,9 +61,11 @@ export async function fetchServerResponse(
const contentType = res.headers.get('content-type') || ''
let isFlightResponse = contentType === RSC_CONTENT_TYPE_HEADER
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
if (!isFlightResponse) {
isFlightResponse = contentType.startsWith('text/plain')
if (process.env.NODE_ENV === 'production') {
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
if (!isFlightResponse) {
isFlightResponse = contentType.startsWith('text/plain')
}
}
}

View file

@ -535,6 +535,7 @@ export default class DevServer extends Server {
})
if (
!isAppPath &&
pageName.startsWith('/api/') &&
this.nextConfig.output === 'export'
) {
@ -1535,6 +1536,7 @@ export default class DevServer extends Server {
staticPaths?: string[]
fallbackMode?: false | 'static' | 'blocking'
}> {
const isAppPath = Boolean(originalAppPath)
// we lazy load the staticPaths to prevent the user
// from waiting on them for the page to load in dev mode
@ -1561,7 +1563,7 @@ export default class DevServer extends Server {
locales,
defaultLocale,
originalAppPath,
isAppPath: !!originalAppPath,
isAppPath,
requestHeaders,
incrementalCacheHandlerPath:
this.nextConfig.experimental.incrementalCacheHandlerPath,
@ -1579,7 +1581,7 @@ export default class DevServer extends Server {
)
.then((res) => {
const { paths: staticPaths = [], fallback } = res.value
if (this.nextConfig.output === 'export') {
if (!isAppPath && this.nextConfig.output === 'export') {
if (fallback === 'blocking') {
throw new Error(
'getStaticPaths with "fallback: blocking" cannot be used with "output: export". See more info here: https://nextjs.org/docs/advanced-features/static-html-export'

View file

@ -7,6 +7,9 @@ import webdriver from 'next-webdriver'
import globOrig from 'glob'
import {
File,
findPort,
killApp,
launchApp,
nextBuild,
nextExport,
startStaticServer,
@ -20,12 +23,13 @@ const distDir = join(__dirname, '.next')
const exportDir = join(appDir, 'out')
const nextConfig = new File(join(appDir, 'next.config.js'))
const slugPage = new File(join(appDir, 'app/another/[slug]/page.js'))
const delay = 100
async function runTests({
isDev,
trailingSlash,
dynamic,
}: {
isDev?: boolean
trailingSlash?: boolean
dynamic?: string
}) {
@ -43,12 +47,18 @@ async function runTests({
}
await fs.remove(distDir)
await fs.remove(exportDir)
await nextBuild(appDir)
await nextExport(appDir, { outdir: exportDir })
const app = await startStaticServer(exportDir)
const address = app.address()
const appPort = typeof address !== 'string' ? address.port : 3000
const delay = isDev ? 500 : 100
const appPort = await findPort()
let stopOrKill: () => Promise<void>
if (isDev) {
const app = await launchApp(appDir, appPort)
stopOrKill = async () => await killApp(app)
} else {
await nextBuild(appDir)
await nextExport(appDir, { outdir: exportDir })
const app = await startStaticServer(exportDir, null, appPort)
stopOrKill = async () => await stopApp(app)
}
try {
const a = (n: number) => `li:nth-child(${n}) a`
console.log('[navigate]')
@ -108,15 +118,20 @@ async function runTests({
'/test.3f1a293b.png'
)
} finally {
await stopApp(app)
await stopOrKill()
nextConfig.restore()
slugPage.restore()
}
}
describe('app dir with next export', () => {
it.each([{ trailingSlash: false }, { trailingSlash: true }])(
"should work with trailingSlash '$trailingSlash'",
describe('app dir with output export', () => {
it.each([
{ isDev: true, trailingSlash: false },
{ isDev: true, trailingSlash: true },
{ isDev: false, trailingSlash: false },
{ isDev: false, trailingSlash: true },
])(
"should work with isDev '$isDev' and trailingSlash '$trailingSlash'",
async ({ trailingSlash }) => {
await runTests({ trailingSlash })
}