Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(standalone): fixed output: "standalone" crashing build when there is no app/ page #51993

Merged
merged 11 commits into from
Jul 10, 2023
Merged
116 changes: 56 additions & 60 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { loadEnvConfig } from '@next/env'
import chalk from 'next/dist/compiled/chalk'
import crypto from 'crypto'
import { isMatch, makeRe } from 'next/dist/compiled/micromatch'
import { promises } from 'fs'
import { promises as fs, existsSync as fsExistsSync } from 'fs'
import os from 'os'
import { Worker } from '../lib/worker'
import { defaultConfig } from '../server/config-shared'
Expand Down Expand Up @@ -186,7 +186,7 @@ async function generateClientSsgManifest(
ssgPages
)};self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()`

await promises.writeFile(
await fs.writeFile(
path.join(distDir, CLIENT_STATIC_FILES_PATH, buildId, '_ssgManifest.js'),
clientSsgManifestContent
)
Expand Down Expand Up @@ -257,10 +257,7 @@ export default async function build(
let buildId: string = ''

if (isGenerate) {
buildId = await promises.readFile(
path.join(distDir, 'BUILD_ID'),
'utf8'
)
buildId = await fs.readFile(path.join(distDir, 'BUILD_ID'), 'utf8')
} else {
buildId = await nextBuildSpan
.traceChild('generate-buildid')
Expand Down Expand Up @@ -808,7 +805,7 @@ export default async function build(
.traceChild('create-dist-dir')
.traceAsyncFn(async () => {
try {
await promises.mkdir(distDir, { recursive: true })
await fs.mkdir(distDir, { recursive: true })
return true
} catch (err) {
if (isError(err) && err.code === 'EPERM') {
Expand All @@ -830,7 +827,7 @@ export default async function build(

// Ensure commonjs handling is used for files in the distDir (generally .next)
// Files outside of the distDir can be "type": "module"
await promises.writeFile(
await fs.writeFile(
path.join(distDir, 'package.json'),
'{"type": "commonjs"}'
)
Expand All @@ -839,7 +836,7 @@ export default async function build(
await nextBuildSpan
.traceChild('write-routes-manifest')
.traceAsyncFn(() =>
promises.writeFile(
fs.writeFile(
routesManifestPath,
JSON.stringify(routesManifest),
'utf8'
Expand Down Expand Up @@ -917,7 +914,6 @@ export default async function build(
? path.join(SERVER_DIRECTORY, FONT_MANIFEST)
: null,
BUILD_ID_FILE,
appDir ? path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST) : null,
path.join(SERVER_DIRECTORY, NEXT_FONT_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, NEXT_FONT_MANIFEST + '.json'),
...(hasInstrumentationHook
Expand Down Expand Up @@ -1052,7 +1048,7 @@ export default async function build(
})
await binding.turbo.startTrace(action, turboTasksForTrace)
if (turbotraceOutputPath && turbotraceFiles) {
const existedNftFile = await promises
const existedNftFile = await fs
.readFile(turbotraceOutputPath, 'utf8')
.then((existedContent) => JSON.parse(existedContent))
.catch(() => ({
Expand All @@ -1062,7 +1058,7 @@ export default async function build(
existedNftFile.files.push(...turbotraceFiles)
const filesSet = new Set(existedNftFile.files)
existedNftFile.files = [...filesSet]
await promises.writeFile(
await fs.writeFile(
turbotraceOutputPath,
JSON.stringify(existedNftFile),
'utf8'
Expand Down Expand Up @@ -1105,14 +1101,14 @@ export default async function build(
const appDefaultConfigs = new Map<string, AppConfig>()
const pageInfos = new Map<string, PageInfo>()
const pagesManifest = JSON.parse(
await promises.readFile(manifestPath, 'utf8')
await fs.readFile(manifestPath, 'utf8')
) as PagesManifest
const buildManifest = JSON.parse(
await promises.readFile(buildManifestPath, 'utf8')
await fs.readFile(buildManifestPath, 'utf8')
) as BuildManifest
const appBuildManifest = appDir
? (JSON.parse(
await promises.readFile(appBuildManifestPath, 'utf8')
await fs.readFile(appBuildManifestPath, 'utf8')
) as AppBuildManifest)
: undefined

Expand All @@ -1127,7 +1123,7 @@ export default async function build(

if (appDir) {
appPathsManifest = JSON.parse(
await promises.readFile(
await fs.readFile(
path.join(distDir, SERVER_DIRECTORY, APP_PATHS_MANIFEST),
'utf8'
)
Expand All @@ -1136,7 +1132,7 @@ export default async function build(
Object.keys(appPathsManifest).forEach((entry) => {
appPathRoutes[entry] = normalizeAppPath(entry)
})
await promises.writeFile(
await fs.writeFile(
path.join(distDir, APP_PATH_ROUTES_MANIFEST),
JSON.stringify(appPathRoutes, null, 2)
)
Expand Down Expand Up @@ -1739,7 +1735,7 @@ export default async function build(
}

if (Object.keys(functionsConfigManifest).length > 0) {
await promises.writeFile(
await fs.writeFile(
path.join(distDir, SERVER_DIRECTORY, FUNCTIONS_CONFIG_MANIFEST),
JSON.stringify(functionsConfigManifest, null, 2)
)
Expand Down Expand Up @@ -1833,7 +1829,7 @@ export default async function build(
)
const pageDir = path.dirname(traceFile)
const traceContent = JSON.parse(
await promises.readFile(traceFile, 'utf8')
await fs.readFile(traceFile, 'utf8')
)
const includes: string[] = []

Expand Down Expand Up @@ -1866,7 +1862,7 @@ export default async function build(
})
}

await promises.writeFile(
await fs.writeFile(
traceFile,
JSON.stringify({
version: traceContent.version,
Expand Down Expand Up @@ -1913,21 +1909,18 @@ export default async function build(

await Promise.all(
lockFiles.map(async (lockFile) => {
cacheHash.update(await promises.readFile(lockFile))
cacheHash.update(await fs.readFile(lockFile))
})
)
cacheKey = cacheHash.digest('hex')

try {
const existingTrace = JSON.parse(
await promises.readFile(cachedTracePath, 'utf8')
await fs.readFile(cachedTracePath, 'utf8')
)

if (existingTrace.cacheKey === cacheKey) {
await promises.copyFile(
cachedTracePath,
nextServerTraceOutput
)
await fs.copyFile(cachedTracePath, nextServerTraceOutput)
return
}
} catch (_) {}
Expand Down Expand Up @@ -2060,7 +2053,7 @@ export default async function build(
addToTracedFiles(root, file)
})
}
await promises.writeFile(
await fs.writeFile(
nextServerTraceOutput,
JSON.stringify({
version: 1,
Expand All @@ -2071,8 +2064,8 @@ export default async function build(
files: string[]
})
)
await promises.unlink(cachedTracePath).catch(() => {})
await promises
await fs.unlink(cachedTracePath).catch(() => {})
await fs
.copyFile(nextServerTraceOutput, cachedTracePath)
.catch(() => {})
})
Expand Down Expand Up @@ -2130,7 +2123,7 @@ export default async function build(
}
})

await promises.writeFile(
await fs.writeFile(
routesManifestPath,
JSON.stringify(routesManifest),
'utf8'
Expand Down Expand Up @@ -2206,14 +2199,14 @@ export default async function build(
})
)

await promises.writeFile(
await fs.writeFile(
path.join(distDir, SERVER_FILES_MANIFEST),
JSON.stringify(requiredServerFiles),
'utf8'
)

const middlewareManifest: MiddlewareManifest = JSON.parse(
await promises.readFile(
await fs.readFile(
path.join(distDir, SERVER_DIRECTORY, MIDDLEWARE_MANIFEST),
'utf8'
)
Expand Down Expand Up @@ -2440,7 +2433,7 @@ export default async function build(
// remove server bundles that were exported
for (const page of staticPages) {
const serverBundle = getPagePath(page, distDir, undefined, false)
await promises.unlink(serverBundle)
await fs.unlink(serverBundle)
}

for (const [originalAppPath, routes] of appStaticPaths) {
Expand Down Expand Up @@ -2623,8 +2616,8 @@ export default async function build(
// output with the locale prefixed so don't attempt moving
// without the prefix
if ((!i18n || additionalSsgFile) && !isNotFound) {
await promises.mkdir(path.dirname(dest), { recursive: true })
await promises.rename(orig, dest)
await fs.mkdir(path.dirname(dest), { recursive: true })
await fs.rename(orig, dest)
} else if (i18n && !isSsg) {
// this will be updated with the locale prefixed variant
// since all files are output with the locale prefix
Expand Down Expand Up @@ -2669,10 +2662,10 @@ export default async function build(
if (!isSsg) {
pagesManifest[curPath] = updatedRelativeDest
}
await promises.mkdir(path.dirname(updatedDest), {
await fs.mkdir(path.dirname(updatedDest), {
recursive: true,
})
await promises.rename(updatedOrig, updatedDest)
await fs.rename(updatedOrig, updatedDest)
}
}
})
Expand All @@ -2693,7 +2686,7 @@ export default async function build(
.replace(/\\/g, '/')

if (await fileExists(orig)) {
await promises.copyFile(
await fs.copyFile(
orig,
path.join(distDir, 'server', updatedRelativeDest)
)
Expand Down Expand Up @@ -2861,8 +2854,8 @@ export default async function build(

// remove temporary export folder
await recursiveDelete(exportOptions.outdir)
await promises.rmdir(exportOptions.outdir)
await promises.writeFile(
await fs.rmdir(exportOptions.outdir)
await fs.writeFile(
manifestPath,
JSON.stringify(pagesManifest, null, 2),
'utf8'
Expand Down Expand Up @@ -2954,12 +2947,12 @@ export default async function build(
NextBuildContext.allowedRevalidateHeaderKeys =
config.experimental.allowedRevalidateHeaderKeys

await promises.writeFile(
await fs.writeFile(
path.join(distDir, PRERENDER_MANIFEST),
JSON.stringify(prerenderManifest),
'utf8'
)
await promises.writeFile(
await fs.writeFile(
path.join(distDir, PRERENDER_MANIFEST).replace(/\.json$/, '.js'),
`self.__PRERENDER_MANIFEST=${JSON.stringify(
JSON.stringify(prerenderManifest)
Expand All @@ -2979,12 +2972,12 @@ export default async function build(
preview: previewProps,
notFoundRoutes: [],
}
await promises.writeFile(
await fs.writeFile(
path.join(distDir, PRERENDER_MANIFEST),
JSON.stringify(prerenderManifest),
'utf8'
)
await promises.writeFile(
await fs.writeFile(
path.join(distDir, PRERENDER_MANIFEST).replace(/\.json$/, '.js'),
`self.__PRERENDER_MANIFEST=${JSON.stringify(
JSON.stringify(prerenderManifest)
Expand All @@ -3006,15 +2999,15 @@ export default async function build(
pathname: makeRe(p.pathname ?? '**').source,
}))

await promises.writeFile(
await fs.writeFile(
path.join(distDir, IMAGES_MANIFEST),
JSON.stringify({
version: 1,
images,
}),
'utf8'
)
await promises.writeFile(
await fs.writeFile(
path.join(distDir, EXPORT_MARKER),
JSON.stringify({
version: 1,
Expand All @@ -3024,7 +3017,7 @@ export default async function build(
}),
'utf8'
)
await promises.unlink(path.join(distDir, EXPORT_DETAIL)).catch((err) => {
await fs.unlink(path.join(distDir, EXPORT_DETAIL)).catch((err) => {
if (err.code === 'ENOENT') {
return Promise.resolve()
}
Expand All @@ -3048,10 +3041,10 @@ export default async function build(
'standalone',
path.relative(outputFileTracingRoot, filePath)
)
await promises.mkdir(path.dirname(outputPath), {
await fs.mkdir(path.dirname(outputPath), {
recursive: true,
})
await promises.copyFile(filePath, outputPath)
await fs.copyFile(filePath, outputPath)
}
await recursiveCopy(
path.join(distDir, SERVER_DIRECTORY, 'pages'),
Expand All @@ -3065,17 +3058,20 @@ export default async function build(
{ 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 }
)
const originalServerApp = path.join(distDir, SERVER_DIRECTORY, 'app')
if (fsExistsSync(originalServerApp)) {
await recursiveCopy(
originalServerApp,
path.join(
distDir,
'standalone',
path.relative(outputFileTracingRoot, distDir),
SERVER_DIRECTORY,
'app'
),
{ overwrite: true }
)
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions test/production/standalone-mode/no-app-routes/app/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function Root({ children }) {
return (
<html>
<head></head>
<body>{children}</body>
</html>
)
}
14 changes: 14 additions & 0 deletions test/production/standalone-mode/no-app-routes/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createNextDescribe } from 'e2e-utils'

createNextDescribe(
'standalone mode - no app routes',
{
files: __dirname,
},
({ next }) => {
it('should handle pages rendering correctly', async () => {
const browser = await next.browser('/hello')
expect(await browser.elementByCss('#index').text()).toBe('index-page')
})
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
output: 'standalone',
}