Skip to content

Commit

Permalink
Show warning if next export detects an API route (#8257)
Browse files Browse the repository at this point in the history
* Throw error if next export detects an api route

* Moved the test to the export tests

* Added missing comma

* Move the API route comment

* Add comma after replace

* Show a warning instead
  • Loading branch information
Luis Fernando Alvarez D authored and timneutkens committed Aug 11, 2019
1 parent f01af7c commit 36aabe0
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 24 deletions.
13 changes: 13 additions & 0 deletions errors/api-routes-static-export.md
@@ -0,0 +1,13 @@
# API routes in Static export

#### Why This Warning Occurred

An `exportPathMap` path was matched to an API route. `next export` will not prerender API routes to HTML.

#### Possible Ways to Fix It

Remove any paths using API routes from your `exportPathMap` in `next.config.js`.

### Useful Links

- [Static HTML export](https://github.com/zeit/next.js#static-html-export)
31 changes: 19 additions & 12 deletions packages/next/export/index.js
Expand Up @@ -4,6 +4,7 @@ import { recursiveCopy } from '../lib/recursive-copy'
import mkdirpModule from 'mkdirp'
import { resolve, join } from 'path'
import { existsSync, readFileSync } from 'fs'
import chalk from 'chalk'
import loadConfig, {
isTargetLikeServerless
} from 'next-server/dist/server/config'
Expand Down Expand Up @@ -36,7 +37,6 @@ export default async function (dir, options, configuration) {
const threads = options.threads || Math.max(cpus().length - 1, 1)
const distDir = join(dir, nextConfig.distDir)
const subFolders = nextConfig.exportTrailingSlash
let apiPage = false

if (!options.buildExport && nextConfig.target !== 'server') {
throw new Error(
Expand All @@ -62,19 +62,12 @@ export default async function (dir, options, configuration) {
for (const page of pages) {
// _document and _app are not real pages
// _error is exported as 404.html later on
const isApiRoute = page.match(API_ROUTE)

// Warn about API pages export
if (isApiRoute && !apiPage) {
apiPage = true
log(` API pages are not supported in export`)
}

// API Routes are Node.js functions
if (
page === '/_document' ||
page === '/_app' ||
page === '/_error' ||
isApiRoute
page.match(API_ROUTE)
) {
continue
}
Expand Down Expand Up @@ -149,10 +142,24 @@ export default async function (dir, options, configuration) {
})
exportPathMap['/404.html'] = exportPathMap['/404.html'] || { page: '/_error' }
const exportPaths = Object.keys(exportPathMap)
const filteredPaths = exportPaths.filter(
// Remove API routes
route => !exportPathMap[route].page.match(API_ROUTE)
)
const hasApiRoutes = exportPaths.length !== filteredPaths.length

// Warn if the user defines a path for an API page
if (hasApiRoutes) {
log(
chalk.yellow(
' API pages are not supported by next export. https://err.sh/zeit/next.js/api-routes-static-export'
)
)
}

const progress = !options.silent && createProgress(exportPaths.length)
const progress = !options.silent && createProgress(filteredPaths.length)

const chunks = exportPaths.reduce((result, route, i) => {
const chunks = filteredPaths.reduce((result, route, i) => {
const worker = i % threads
if (!result[worker]) {
result[worker] = { paths: [], pathMap: {} }
Expand Down
12 changes: 0 additions & 12 deletions test/integration/api-support/test/index.test.js
Expand Up @@ -10,7 +10,6 @@ import {
renderViaHTTP,
nextBuild,
nextStart,
runNextCommand,
File
} from 'next-test-utils'
import json from '../big.json'
Expand Down Expand Up @@ -286,17 +285,6 @@ function runTests (serverless = false) {
}
})

if (!serverless) {
it('should warn about API export', async () => {
const { stdout } = await runNextCommand(['export', appDir], {
stdout: true,
stderr: true
})

expect(stdout).toContain('API pages are not supported in export')
})
}

it('should return data on dynamic optional nested route', async () => {
const data = await fetchViaHTTP(
appPort,
Expand Down
1 change: 1 addition & 0 deletions test/integration/export/next.config.js
Expand Up @@ -37,6 +37,7 @@ module.exports = phase => {
query: { text: 'this file has an extension' }
},
'/query': { page: '/query', query: { a: 'blue' } },
// API route
'/blog/nextjs/comment/test': { page: '/blog/[post]/comment/[id]' }
}
} // end exportPathMap
Expand Down
3 changes: 3 additions & 0 deletions test/integration/export/pages/api/data.js
@@ -0,0 +1,3 @@
export default (req, res) => {
res.send('Hello World')
}
28 changes: 28 additions & 0 deletions test/integration/export/test/api-routes.js
@@ -0,0 +1,28 @@
/* eslint-env jest */
import { join } from 'path'
import { File, runNextCommand } from 'next-test-utils'

export default function (context) {
describe('API routes export', () => {
const nextConfig = new File(join(context.appDir, 'next.config.js'))

beforeEach(() => {
nextConfig.replace('// API route', `'/data': { page: '/api/data' },`)
})
afterEach(() => {
nextConfig.restore()
})

it('Should throw if a route is matched', async () => {
const outdir = join(context.appDir, 'outApi')
const { stdout } = await runNextCommand(
['export', context.appDir, '--outdir', outdir],
{ stdout: true }
)

expect(stdout).toContain(
'https://err.sh/zeit/next.js/api-routes-static-export'
)
})
})
}
2 changes: 2 additions & 0 deletions test/integration/export/test/index.test.js
Expand Up @@ -19,6 +19,7 @@ import dev from './dev'
import { promisify } from 'util'
import fs from 'fs'
import dynamic from './dynamic'
import apiRoutes from './api-routes'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5

Expand Down Expand Up @@ -96,4 +97,5 @@ describe('Static Export', () => {
browser(context)
dev(devContext)
dynamic(context)
apiRoutes(context)
})

0 comments on commit 36aabe0

Please sign in to comment.