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:
parent
0e91549397
commit
9a89c4933d
3 changed files with 43 additions and 22 deletions
|
@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue