Skip to content

Commit

Permalink
fix appDir returning 404 in production with "output": "standalone" (#…
Browse files Browse the repository at this point in the history
…43268)

<!--
Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change that you're making:
-->

Fixes: #42812
Fixes: #43037

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have a helpful link attached, see
[`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ]
[e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see
[`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)

This PR fixes the issue in which urls from appDir will always not be
found in production when built with `"output": "standalone"` by copying
.next/server/app and .next/server/app-paths-manifest.json into
.next/standalone/server.

Co-authored-by: JJ Kasper <jj@jjsweb.site>
  • Loading branch information
DuCanhGH and ijjk committed Nov 24, 2022
1 parent 60d5c96 commit 2eaa3ae
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 11 deletions.
64 changes: 53 additions & 11 deletions packages/next/build/index.ts
Expand Up @@ -61,6 +61,9 @@ import {
FONT_LOADER_MANIFEST,
CLIENT_STATIC_FILES_RUNTIME_MAIN_APP,
APP_CLIENT_INTERNALS,
SUBRESOURCE_INTEGRITY_MANIFEST,
MIDDLEWARE_BUILD_MANIFEST,
MIDDLEWARE_REACT_LOADABLE_MANIFEST,
} from '../shared/lib/constants'
import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils'
import { __ApiPreviewProps } from '../server/api-utils'
Expand Down Expand Up @@ -865,6 +868,11 @@ export default async function build(
)

const manifestPath = path.join(distDir, SERVER_DIRECTORY, PAGES_MANIFEST)
const appManifestPath = path.join(
distDir,
SERVER_DIRECTORY,
APP_PATHS_MANIFEST
)

const requiredServerFiles = nextBuildSpan
.traceChild('generate-required-server-files')
Expand All @@ -885,8 +893,26 @@ export default async function build(
BUILD_MANIFEST,
PRERENDER_MANIFEST,
path.join(SERVER_DIRECTORY, MIDDLEWARE_MANIFEST),
path.join(SERVER_DIRECTORY, MIDDLEWARE_BUILD_MANIFEST + '.js'),
path.join(
SERVER_DIRECTORY,
MIDDLEWARE_REACT_LOADABLE_MANIFEST + '.js'
),
...(appDir
? [
...(config.experimental.sri
? [
path.join(
SERVER_DIRECTORY,
SUBRESOURCE_INTEGRITY_MANIFEST + '.js'
),
path.join(
SERVER_DIRECTORY,
SUBRESOURCE_INTEGRITY_MANIFEST + '.json'
),
]
: []),
path.relative(distDir, appManifestPath),
path.join(SERVER_DIRECTORY, FLIGHT_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, FLIGHT_MANIFEST + '.json'),
path.join(
Expand Down Expand Up @@ -2046,6 +2072,7 @@ export default async function build(
dir,
distDir,
pageKeys.pages,
pageKeys.app,
outputFileTracingRoot,
requiredServerFiles.config,
middlewareManifest
Expand Down Expand Up @@ -2750,17 +2777,32 @@ export default async function build(
})
await promises.copyFile(filePath, outputPath)
}
await recursiveCopy(
path.join(distDir, SERVER_DIRECTORY, 'pages'),
path.join(
distDir,
'standalone',
path.relative(outputFileTracingRoot, distDir),
SERVER_DIRECTORY,
'pages'
),
{ overwrite: true }
)
if (pagesDir) {
await recursiveCopy(
path.join(distDir, SERVER_DIRECTORY, 'pages'),
path.join(
distDir,
'standalone',
path.relative(outputFileTracingRoot, distDir),
SERVER_DIRECTORY,
'pages'
),
{ overwrite: true }
)
}
if (appDir) {
await recursiveCopy(
path.join(distDir, SERVER_DIRECTORY, 'app'),
path.join(
distDir,
'standalone',
path.relative(outputFileTracingRoot, distDir),
SERVER_DIRECTORY,
'app'
),
{ overwrite: true }
)
}
}

staticPages.forEach((pg) => allStaticPages.add(pg))
Expand Down
10 changes: 10 additions & 0 deletions packages/next/build/utils.ts
Expand Up @@ -1598,6 +1598,7 @@ export async function copyTracedFiles(
dir: string,
distDir: string,
pageKeys: ReadonlyArray<string>,
appPageKeys: readonly string[] | undefined,
tracingRoot: string,
serverConfig: { [key: string]: any },
middlewareManifest: MiddlewareManifest
Expand Down Expand Up @@ -1680,6 +1681,15 @@ export async function copyTracedFiles(
Log.warn(`Failed to copy traced files for ${pageFile}`, err)
})
}
if (appPageKeys) {
for (const page of appPageKeys) {
const pageFile = path.join(distDir, 'server', 'app', `${page}`, 'page.js')
const pageTraceFile = `${pageFile}.nft.json`
await handleTraceFiles(pageTraceFile).catch((err) => {
Log.warn(`Failed to copy traced files for ${pageFile}`, err)
})
}
}
await handleTraceFiles(path.join(distDir, 'next-server.js.nft.json'))
const serverOutputPath = path.join(
outputPath,
Expand Down
1 change: 1 addition & 0 deletions test/e2e/app-dir/app/next.config.js
Expand Up @@ -5,6 +5,7 @@ module.exports = {
algorithm: 'sha256',
},
},
output: 'standalone',
rewrites: async () => {
return {
afterFiles: [
Expand Down
45 changes: 45 additions & 0 deletions test/e2e/app-dir/index.test.ts
@@ -1,17 +1,22 @@
import os from 'os'
import { createNext, FileRef } from 'e2e-utils'
import crypto from 'crypto'
import { NextInstance } from 'test/lib/next-modes/base'
import {
check,
fetchViaHTTP,
findPort,
getRedboxHeader,
hasRedbox,
initNextServerScript,
killApp,
renderViaHTTP,
waitFor,
} from 'next-test-utils'
import path from 'path'
import cheerio from 'cheerio'
import webdriver from 'next-webdriver'
import fs from 'fs-extra'

describe('app dir', () => {
const isDev = (global as any).isNextDev
Expand Down Expand Up @@ -2554,4 +2559,44 @@ describe('app dir', () => {
}

runTests()

if ((global as any).isNextStart) {
it('should work correctly with output standalone', async () => {
const tmpFolder = path.join(os.tmpdir(), 'next-standalone-' + Date.now())
await fs.move(path.join(next.testDir, '.next/standalone'), tmpFolder)
let server

try {
const testServer = path.join(tmpFolder, 'server.js')
const appPort = await findPort()
server = await initNextServerScript(
testServer,
/Listening on/,
{
...process.env,
PORT: appPort,
},
undefined,
{
cwd: tmpFolder,
}
)

for (const testPath of [
'/',
'/api/hello',
'/blog/first',
'/dashboard',
'/dashboard/deployments/123',
'/catch-all/first',
]) {
const res = await fetchViaHTTP(appPort, testPath)
expect(res.status).toBe(200)
}
} finally {
if (server) await killApp(server)
await fs.remove(tmpFolder)
}
})
}
})

0 comments on commit 2eaa3ae

Please sign in to comment.