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 {
|
try {
|
||||||
let fetchUrl = url
|
let fetchUrl = url
|
||||||
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
fetchUrl = new URL(url) // clone
|
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
|
||||||
if (fetchUrl.pathname.endsWith('/')) {
|
fetchUrl = new URL(url) // clone
|
||||||
fetchUrl.pathname += 'index.txt'
|
if (fetchUrl.pathname.endsWith('/')) {
|
||||||
} else {
|
fetchUrl.pathname += 'index.txt'
|
||||||
fetchUrl.pathname += '.txt'
|
} else {
|
||||||
|
fetchUrl.pathname += '.txt'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const res = await fetch(fetchUrl, {
|
const res = await fetch(fetchUrl, {
|
||||||
|
@ -59,9 +61,11 @@ export async function fetchServerResponse(
|
||||||
const contentType = res.headers.get('content-type') || ''
|
const contentType = res.headers.get('content-type') || ''
|
||||||
let isFlightResponse = contentType === RSC_CONTENT_TYPE_HEADER
|
let isFlightResponse = contentType === RSC_CONTENT_TYPE_HEADER
|
||||||
|
|
||||||
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
if (!isFlightResponse) {
|
if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
|
||||||
isFlightResponse = contentType.startsWith('text/plain')
|
if (!isFlightResponse) {
|
||||||
|
isFlightResponse = contentType.startsWith('text/plain')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -535,6 +535,7 @@ export default class DevServer extends Server {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
!isAppPath &&
|
||||||
pageName.startsWith('/api/') &&
|
pageName.startsWith('/api/') &&
|
||||||
this.nextConfig.output === 'export'
|
this.nextConfig.output === 'export'
|
||||||
) {
|
) {
|
||||||
|
@ -1535,6 +1536,7 @@ export default class DevServer extends Server {
|
||||||
staticPaths?: string[]
|
staticPaths?: string[]
|
||||||
fallbackMode?: false | 'static' | 'blocking'
|
fallbackMode?: false | 'static' | 'blocking'
|
||||||
}> {
|
}> {
|
||||||
|
const isAppPath = Boolean(originalAppPath)
|
||||||
// we lazy load the staticPaths to prevent the user
|
// we lazy load the staticPaths to prevent the user
|
||||||
// from waiting on them for the page to load in dev mode
|
// from waiting on them for the page to load in dev mode
|
||||||
|
|
||||||
|
@ -1561,7 +1563,7 @@ export default class DevServer extends Server {
|
||||||
locales,
|
locales,
|
||||||
defaultLocale,
|
defaultLocale,
|
||||||
originalAppPath,
|
originalAppPath,
|
||||||
isAppPath: !!originalAppPath,
|
isAppPath,
|
||||||
requestHeaders,
|
requestHeaders,
|
||||||
incrementalCacheHandlerPath:
|
incrementalCacheHandlerPath:
|
||||||
this.nextConfig.experimental.incrementalCacheHandlerPath,
|
this.nextConfig.experimental.incrementalCacheHandlerPath,
|
||||||
|
@ -1579,7 +1581,7 @@ export default class DevServer extends Server {
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const { paths: staticPaths = [], fallback } = res.value
|
const { paths: staticPaths = [], fallback } = res.value
|
||||||
if (this.nextConfig.output === 'export') {
|
if (!isAppPath && this.nextConfig.output === 'export') {
|
||||||
if (fallback === 'blocking') {
|
if (fallback === 'blocking') {
|
||||||
throw new Error(
|
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'
|
'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 globOrig from 'glob'
|
||||||
import {
|
import {
|
||||||
File,
|
File,
|
||||||
|
findPort,
|
||||||
|
killApp,
|
||||||
|
launchApp,
|
||||||
nextBuild,
|
nextBuild,
|
||||||
nextExport,
|
nextExport,
|
||||||
startStaticServer,
|
startStaticServer,
|
||||||
|
@ -20,12 +23,13 @@ const distDir = join(__dirname, '.next')
|
||||||
const exportDir = join(appDir, 'out')
|
const exportDir = join(appDir, 'out')
|
||||||
const nextConfig = new File(join(appDir, 'next.config.js'))
|
const nextConfig = new File(join(appDir, 'next.config.js'))
|
||||||
const slugPage = new File(join(appDir, 'app/another/[slug]/page.js'))
|
const slugPage = new File(join(appDir, 'app/another/[slug]/page.js'))
|
||||||
const delay = 100
|
|
||||||
|
|
||||||
async function runTests({
|
async function runTests({
|
||||||
|
isDev,
|
||||||
trailingSlash,
|
trailingSlash,
|
||||||
dynamic,
|
dynamic,
|
||||||
}: {
|
}: {
|
||||||
|
isDev?: boolean
|
||||||
trailingSlash?: boolean
|
trailingSlash?: boolean
|
||||||
dynamic?: string
|
dynamic?: string
|
||||||
}) {
|
}) {
|
||||||
|
@ -43,12 +47,18 @@ async function runTests({
|
||||||
}
|
}
|
||||||
await fs.remove(distDir)
|
await fs.remove(distDir)
|
||||||
await fs.remove(exportDir)
|
await fs.remove(exportDir)
|
||||||
await nextBuild(appDir)
|
const delay = isDev ? 500 : 100
|
||||||
await nextExport(appDir, { outdir: exportDir })
|
const appPort = await findPort()
|
||||||
const app = await startStaticServer(exportDir)
|
let stopOrKill: () => Promise<void>
|
||||||
const address = app.address()
|
if (isDev) {
|
||||||
const appPort = typeof address !== 'string' ? address.port : 3000
|
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 {
|
try {
|
||||||
const a = (n: number) => `li:nth-child(${n}) a`
|
const a = (n: number) => `li:nth-child(${n}) a`
|
||||||
console.log('[navigate]')
|
console.log('[navigate]')
|
||||||
|
@ -108,15 +118,20 @@ async function runTests({
|
||||||
'/test.3f1a293b.png'
|
'/test.3f1a293b.png'
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
await stopApp(app)
|
await stopOrKill()
|
||||||
nextConfig.restore()
|
nextConfig.restore()
|
||||||
slugPage.restore()
|
slugPage.restore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('app dir with next export', () => {
|
describe('app dir with output export', () => {
|
||||||
it.each([{ trailingSlash: false }, { trailingSlash: true }])(
|
it.each([
|
||||||
"should work with trailingSlash '$trailingSlash'",
|
{ 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 }) => {
|
async ({ trailingSlash }) => {
|
||||||
await runTests({ trailingSlash })
|
await runTests({ trailingSlash })
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue