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

Show warning if next export detects an API route #8257

Merged
merged 7 commits into from Aug 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
})