diff --git a/errors/next-export-no-build-id.md b/errors/next-export-no-build-id.md new file mode 100644 index 0000000000..affed88c30 --- /dev/null +++ b/errors/next-export-no-build-id.md @@ -0,0 +1,10 @@ +# Could not find a production build + +#### Why This Error Occurred + +When running `next export` a production build is needed. + +#### Possible Ways to Fix It + +- Run `next build` to create a production build before running `next export`. +- If your intention was to run the development server run `next dev` instead. diff --git a/errors/production-start-no-build-id.md b/errors/production-start-no-build-id.md new file mode 100644 index 0000000000..c6c9bfdb93 --- /dev/null +++ b/errors/production-start-no-build-id.md @@ -0,0 +1,10 @@ +# Could not find a production build + +#### Why This Error Occurred + +When running `next start` or a custom server in production mode a production build is needed. + +#### Possible Ways to Fix It + +- Run `next build` to create a production build before booting up the production server. +- If your intention was to run the development server run `next dev` instead. diff --git a/packages/next/cli/next-export.ts b/packages/next/cli/next-export.ts index f13533bd26..272877f920 100755 --- a/packages/next/cli/next-export.ts +++ b/packages/next/cli/next-export.ts @@ -62,7 +62,7 @@ const nextExport: cliCommand = (argv) => { exportApp(dir, options) .then(() => { - printAndExit('Export successful', 0) + printAndExit(`Export successful. Files written to ${options.outdir}`, 0) }) .catch((err) => { printAndExit(err) diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts index a0c5d26f7d..010651fdf5 100644 --- a/packages/next/export/index.ts +++ b/packages/next/export/index.ts @@ -164,9 +164,11 @@ export default async function exportApp( Log.info(`using build directory: ${distDir}`) } - if (!existsSync(distDir)) { + const buildIdFile = join(distDir, BUILD_ID_FILE) + + if (!existsSync(buildIdFile)) { throw new Error( - `Build directory ${distDir} does not exist. Make sure you run "next build" before running "next start" or "next export".` + `Could not find a production build in the '${distDir}' directory. Try building your app with 'next build' before starting the static export. https://err.sh/vercel/next.js/next-export-no-build-id` ) } @@ -186,7 +188,7 @@ export default async function exportApp( ) } - const buildId = readFileSync(join(distDir, BUILD_ID_FILE), 'utf8') + const buildId = readFileSync(buildIdFile, 'utf8') const pagesManifest = !options.pages && (require(join( diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 622c615099..7a8d2e9aaf 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -1899,7 +1899,7 @@ export default class Server { } catch (err) { if (!fs.existsSync(buildIdFile)) { throw new Error( - `Could not find a valid build in the '${this.distDir}' directory! Try building your app with 'next build' before starting the server.` + `Could not find a production build in the '${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://err.sh/vercel/next.js/production-start-no-build-id` ) } diff --git a/test/integration/export-no-build/next.config.js b/test/integration/export-no-build/next.config.js new file mode 100644 index 0000000000..1f568747f8 --- /dev/null +++ b/test/integration/export-no-build/next.config.js @@ -0,0 +1,39 @@ +module.exports = { + onDemandEntries: { + // Make sure entries are not getting disposed. + maxInactiveAge: 1000 * 60 * 60, + }, + rewrites() { + // add a rewrite so the code isn't dead-code eliminated + return [ + { + source: '/some-rewrite', + destination: '/', + }, + ] + }, + redirects() { + return [ + { + source: '/redirect/me/to-about/:lang', + destination: '/:lang/about', + permanent: false, + }, + { + source: '/nonexistent', + destination: '/about', + permanent: false, + }, + { + source: '/shadowed-page', + destination: '/about', + permanent: false, + }, + { + source: '/redirect-query-test/:path', + destination: '/about?foo=:path', + permanent: false, + }, + ] + }, +} diff --git a/test/integration/export-no-build/test/index.test.js b/test/integration/export-no-build/test/index.test.js new file mode 100644 index 0000000000..bd3ca813b4 --- /dev/null +++ b/test/integration/export-no-build/test/index.test.js @@ -0,0 +1,13 @@ +/* eslint-env jest */ +import { nextExport } from 'next-test-utils' +import { join } from 'path' +const appDir = join(__dirname, '../') +jest.setTimeout(1000 * 60 * 5) + +describe('next export without build', () => { + it('should show error when there is no production build', async () => { + const result = await nextExport(appDir, {}, { stderr: true, stdout: true }) + console.log(result.stdout, result.stderr) + expect(result.stderr).toMatch(/Could not find a production build in the/) + }) +}) diff --git a/test/integration/production-start-no-build/next.config.js b/test/integration/production-start-no-build/next.config.js new file mode 100644 index 0000000000..1f568747f8 --- /dev/null +++ b/test/integration/production-start-no-build/next.config.js @@ -0,0 +1,39 @@ +module.exports = { + onDemandEntries: { + // Make sure entries are not getting disposed. + maxInactiveAge: 1000 * 60 * 60, + }, + rewrites() { + // add a rewrite so the code isn't dead-code eliminated + return [ + { + source: '/some-rewrite', + destination: '/', + }, + ] + }, + redirects() { + return [ + { + source: '/redirect/me/to-about/:lang', + destination: '/:lang/about', + permanent: false, + }, + { + source: '/nonexistent', + destination: '/about', + permanent: false, + }, + { + source: '/shadowed-page', + destination: '/about', + permanent: false, + }, + { + source: '/redirect-query-test/:path', + destination: '/about?foo=:path', + permanent: false, + }, + ] + }, +} diff --git a/test/integration/production-start-no-build/test/index.test.js b/test/integration/production-start-no-build/test/index.test.js new file mode 100644 index 0000000000..01e30da1f3 --- /dev/null +++ b/test/integration/production-start-no-build/test/index.test.js @@ -0,0 +1,17 @@ +/* eslint-env jest */ +import { nextServer } from 'next-test-utils' +import { join } from 'path' +const appDir = join(__dirname, '../') +jest.setTimeout(1000 * 60 * 5) + +describe('Production Usage without production build', () => { + it('should show error when there is no production build', async () => { + expect(() => { + nextServer({ + dir: appDir, + dev: false, + quiet: true, + }) + }).toThrow(/Could not find a production build in the/) + }) +})