BREAKING CHANGE: Remove target: serverless (#41495)

The `target: serverless` config was deprecated a year ago starting in
[Next.js 12](https://nextjs.org/blog/next-12).

Tests were disabled in #41252 so we can now remove `target: serverless`
and all usage of `target` in `next.config.js`.

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
This commit is contained in:
Steven 2022-10-18 12:47:13 -04:00 committed by GitHub
parent ebae05eeed
commit 9c5bb5bfe9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
224 changed files with 309 additions and 5884 deletions

View file

@ -15,6 +15,8 @@ A [codemod is available](/docs/advanced-features/codemods.md#next-image-to-legac
The `next/link` child can no longer be `<a>`. Add the `legacyBehavior` prop to use the legacy behavior or remove the `<a>` to upgrade. A [codemod is available](/docs/advanced-features/codemods.md#new-link) to automatically upgrade your code.
The `target` configuration option has been removed and superseded by [Output File Tracing](https://nextjs.org/docs/advanced-features/output-file-tracing).
## Upgrading to 12.2
If you were using Middleware prior to `12.2`, please see the [upgrade guide](https://nextjs.org/docs/messages/middleware-upgrade-guide) for more information.

View file

@ -2,11 +2,11 @@
#### Why This Error Occurred
The `target` property in `next.config.js` has been deprecated. Please migrate to leverage the default target instead.
Starting in Next.js 13, the `target` property in `next.config.js` has been removed.
#### Possible Ways to Fix It
For serverless cases, leverage the new output file traces or deploy your application somewhere where they are leveraged automatically like [Vercel](https://vercel.com).
For serverless targets, please use the Output File Tracing or deploy your application somewhere where it can be leveraged automatically, like [Vercel](https://vercel.com).
### Useful Links

View file

@ -4,7 +4,7 @@
You attempted to statically export your application via `next export`, however, one or more of your pages uses `getServerSideProps`.
The `getServerSideProps` lifecycle is not compatible with `next export`, so you'll need to use `next start` or a [serverless deployment](https://vercel.com).
The `getServerSideProps` lifecycle is not compatible with `next export`, so you'll need to use `next start` when self hosting or deploy to a provider like [Vercel](https://vercel.com).
#### Possible Ways to Fix It

View file

@ -2,7 +2,6 @@ import type { ClientPagesLoaderOptions } from './webpack/loaders/next-client-pag
import type { MiddlewareLoaderOptions } from './webpack/loaders/next-middleware-loader'
import type { EdgeSSRLoaderQuery } from './webpack/loaders/next-edge-ssr-loader'
import type { NextConfigComplete } from '../server/config-shared'
import type { ServerlessLoaderQuery } from './webpack/loaders/next-serverless-loader'
import type { webpack } from 'next/dist/compiled/webpack/webpack'
import type {
MiddlewareConfig,
@ -14,7 +13,6 @@ import { posix, join } from 'path'
import { stringify } from 'querystring'
import {
API_ROUTE,
DOT_NEXT_ALIAS,
PAGES_DIR_ALIAS,
ROOT_DIR_ALIAS,
APP_DIR_ALIAS,
@ -33,13 +31,11 @@ import {
EDGE_RUNTIME_WEBPACK,
} from '../shared/lib/constants'
import { __ApiPreviewProps } from '../server/api-utils'
import { isTargetLikeServerless } from '../server/utils'
import { warn } from './output/log'
import {
isMiddlewareFile,
isMiddlewareFilename,
NestedMiddlewareError,
MiddlewareInServerlessTargetError,
} from './utils'
import { getPageStaticInfo } from './analysis/get-page-static-info'
import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep'
@ -147,7 +143,6 @@ interface CreateEntrypointsParams {
previewMode: __ApiPreviewProps
rootDir: string
rootPaths?: Record<string, string>
target: 'server' | 'serverless' | 'experimental-serverless-trace'
appDir?: string
appPaths?: Record<string, string>
pageExtensions: string[]
@ -229,48 +224,6 @@ export function getAppEntry(opts: {
}
}
export function getServerlessEntry(opts: {
absolutePagePath: string
buildId: string
config: NextConfigComplete
envFiles: LoadedEnvFiles
page: string
previewMode: __ApiPreviewProps
pages: { [page: string]: string }
}): ObjectValue<webpack.EntryObject> {
const loaderParams: ServerlessLoaderQuery = {
absolute404Path: opts.pages['/404'] || '',
absoluteAppPath: opts.pages['/_app'],
absoluteDocumentPath: opts.pages['/_document'],
absoluteErrorPath: opts.pages['/_error'],
absolutePagePath: opts.absolutePagePath,
assetPrefix: opts.config.assetPrefix,
basePath: opts.config.basePath,
buildId: opts.buildId,
canonicalBase: opts.config.amp.canonicalBase || '',
distDir: DOT_NEXT_ALIAS,
generateEtags: opts.config.generateEtags ? 'true' : '',
i18n: opts.config.i18n ? JSON.stringify(opts.config.i18n) : '',
// base64 encode to make sure contents don't break webpack URL loading
loadedEnvFiles: Buffer.from(JSON.stringify(opts.envFiles)).toString(
'base64'
),
page: opts.page,
poweredByHeader: opts.config.poweredByHeader ? 'true' : '',
previewProps: JSON.stringify(opts.previewMode),
runtimeConfig:
Object.keys(opts.config.publicRuntimeConfig).length > 0 ||
Object.keys(opts.config.serverRuntimeConfig).length > 0
? JSON.stringify({
publicRuntimeConfig: opts.config.publicRuntimeConfig,
serverRuntimeConfig: opts.config.serverRuntimeConfig,
})
: '',
}
return `next-serverless-loader?${stringify(loaderParams)}!`
}
export function getClientEntry(opts: {
absolutePagePath: string
page: string
@ -340,7 +293,6 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
isDev,
rootDir,
rootPaths,
target,
appDir,
appPaths,
pageExtensions,
@ -428,10 +380,6 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
middlewareMatchers = staticInfo.middleware?.matchers ?? [
{ regexp: '.*' },
]
if (target === 'serverless') {
throw new MiddlewareInServerlessTargetError()
}
}
await runDependingOnPageType({
@ -459,14 +407,6 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
appPaths: matchedAppPaths,
pageExtensions,
})
} else if (isTargetLikeServerless(target)) {
if (page !== '/_app' && page !== '/_document') {
server[serverBundlePath] = getServerlessEntry({
...params,
absolutePagePath: mappings[page],
page,
})
}
} else {
server[serverBundlePath] = [mappings[page]]
}

View file

@ -48,7 +48,6 @@ import {
FLIGHT_MANIFEST,
REACT_LOADABLE_MANIFEST,
ROUTES_MANIFEST,
SERVERLESS_DIRECTORY,
SERVER_DIRECTORY,
SERVER_FILES_MANIFEST,
STATIC_STATUS_PAGES,
@ -64,7 +63,6 @@ import {
import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils'
import { __ApiPreviewProps } from '../server/api-utils'
import loadConfig from '../server/config'
import { isTargetLikeServerless } from '../server/utils'
import { BuildManifest } from '../server/get-page-files'
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { getPagePath } from '../server/require'
@ -433,8 +431,6 @@ export default async function build(
prefixText: `${Log.prefixes.info} Creating an optimized production build`,
})
const isLikeServerless = isTargetLikeServerless(target)
const pagesPaths = pagesDir
? await nextBuildSpan
.traceChild('collect-pages')
@ -531,7 +527,6 @@ export default async function build(
pages: mappedPages,
pagesDir,
previewMode: previewProps,
target,
rootDir: dir,
rootPaths: mappedRootPaths,
appDir,
@ -807,10 +802,7 @@ export default async function build(
)
)
const serverDir = isLikeServerless
? SERVERLESS_DIRECTORY
: SERVER_DIRECTORY
const manifestPath = path.join(distDir, serverDir, PAGES_MANIFEST)
const manifestPath = path.join(distDir, SERVER_DIRECTORY, PAGES_MANIFEST)
const requiredServerFiles = nextBuildSpan
.traceChild('generate-required-server-files')
@ -846,9 +838,11 @@ export default async function build(
]
: []),
REACT_LOADABLE_MANIFEST,
config.optimizeFonts ? path.join(serverDir, FONT_MANIFEST) : null,
config.optimizeFonts
? path.join(SERVER_DIRECTORY, FONT_MANIFEST)
: null,
BUILD_ID_FILE,
appDir ? path.join(serverDir, APP_PATHS_MANIFEST) : null,
appDir ? path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST) : null,
...(config.experimental.fontLoaders
? [
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.js'),
@ -924,60 +918,30 @@ export default async function build(
// We run client and server compilation separately to optimize for memory usage
await runWebpackSpan.traceAsyncFn(async () => {
// If we are under the serverless build, we will have to run the client
// compiler first because the server compiler depends on the manifest
// files that are created by the client compiler.
// Otherwise, we run the server compilers first and then the client
// Run the server compilers first and then the client
// compiler to track the boundary of server/client components.
let clientResult: SingleCompilerResult | null = null
let serverResult: SingleCompilerResult | null = null
let edgeServerResult: SingleCompilerResult | null = null
if (isLikeServerless) {
if (appDir) {
throw new Error('`appDir` is not supported in serverless mode.')
}
// During the server compilations, entries of client components will be
// injected to this set and then will be consumed by the client compiler.
injectedClientEntries.clear()
const serverResult = await runCompiler(configs[1], {
runWebpackSpan,
})
const edgeServerResult = configs[2]
? await runCompiler(configs[2], { runWebpackSpan })
: null
// Only continue if there were no errors
if (!serverResult.errors.length && !edgeServerResult?.errors.length) {
injectedClientEntries.forEach((value, key) => {
;(clientConfig.entry as webpack.EntryObject)[key] = value
})
// Build client first
clientResult = await runCompiler(clientConfig, {
runWebpackSpan,
})
// Only continue if there were no errors
if (!clientResult.errors.length) {
serverResult = await runCompiler(configs[1], {
runWebpackSpan,
})
edgeServerResult = configs[2]
? await runCompiler(configs[2], { runWebpackSpan })
: null
}
} else {
// During the server compilations, entries of client components will be
// injected to this set and then will be consumed by the client compiler.
injectedClientEntries.clear()
serverResult = await runCompiler(configs[1], {
runWebpackSpan,
})
edgeServerResult = configs[2]
? await runCompiler(configs[2], { runWebpackSpan })
: null
// Only continue if there were no errors
if (
!serverResult.errors.length &&
!edgeServerResult?.errors.length
) {
injectedClientEntries.forEach((value, key) => {
;(clientConfig.entry as webpack.EntryObject)[key] = value
})
clientResult = await runCompiler(clientConfig, {
runWebpackSpan,
})
}
}
result = {
@ -1120,7 +1084,7 @@ export default async function build(
if (appDir) {
appPathsManifest = JSON.parse(
await promises.readFile(
path.join(distDir, serverDir, APP_PATHS_MANIFEST),
path.join(distDir, SERVER_DIRECTORY, APP_PATHS_MANIFEST),
'utf8'
)
)
@ -1209,7 +1173,6 @@ export default async function build(
(await staticWorkers.hasCustomGetInitialProps(
'/_error',
distDir,
isLikeServerless,
runtimeEnvConfig,
false
))
@ -1221,7 +1184,6 @@ export default async function build(
staticWorkers.isPageStatic({
page: '/_error',
distDir,
serverless: isLikeServerless,
configFileName,
runtimeEnvConfig,
httpAgentOptions: config.httpAgentOptions,
@ -1232,15 +1194,12 @@ export default async function build(
})
)
// we don't output _app in serverless mode so use _app export
// from _error instead
const appPageToCheck = isLikeServerless ? '/_error' : '/_app'
const appPageToCheck = '/_app'
const customAppGetInitialPropsPromise =
staticWorkers.hasCustomGetInitialProps(
appPageToCheck,
distDir,
isLikeServerless,
runtimeEnvConfig,
true
)
@ -1248,7 +1207,6 @@ export default async function build(
const namedExportsPromise = staticWorkers.getNamedExports(
appPageToCheck,
distDir,
isLikeServerless,
runtimeEnvConfig
)
@ -1352,7 +1310,7 @@ export default async function build(
if (pageRuntime === SERVER_RUNTIME.edge) {
const manifest = require(join(
distDir,
serverDir,
SERVER_DIRECTORY,
MIDDLEWARE_MANIFEST
))
const manifestKey =
@ -1369,7 +1327,6 @@ export default async function build(
page,
originalAppPath,
distDir,
serverless: isLikeServerless,
configFileName,
runtimeEnvConfig,
httpAgentOptions: config.httpAgentOptions,
@ -1931,7 +1888,7 @@ export default async function build(
const middlewareManifest: MiddlewareManifest = JSON.parse(
await promises.readFile(
path.join(distDir, serverDir, MIDDLEWARE_MANIFEST),
path.join(distDir, SERVER_DIRECTORY, MIDDLEWARE_MANIFEST),
'utf8'
)
)
@ -2145,7 +2102,7 @@ export default async function build(
// remove server bundles that were exported
for (const page of staticPages) {
const serverBundle = getPagePath(page, distDir, isLikeServerless)
const serverBundle = getPagePath(page, distDir)
await promises.unlink(serverBundle)
}
@ -2214,15 +2171,11 @@ export default async function build(
.traceAsyncFn(async () => {
file = `${file}.${ext}`
const orig = path.join(exportOptions.outdir, file)
const pagePath = getPagePath(
originPage,
distDir,
isLikeServerless
)
const pagePath = getPagePath(originPage, distDir)
const relativeDest = path
.relative(
path.join(distDir, serverDir),
path.join(distDir, SERVER_DIRECTORY),
path.join(
path.join(
pagePath,
@ -2253,7 +2206,7 @@ export default async function build(
pagesManifest[page] = relativeDest
}
const dest = path.join(distDir, serverDir, relativeDest)
const dest = path.join(distDir, SERVER_DIRECTORY, relativeDest)
const isNotFound = ssgNotFoundPaths.includes(page)
// for SSG files with i18n the non-prerendered variants are
@ -2299,7 +2252,7 @@ export default async function build(
)
const updatedDest = path.join(
distDir,
serverDir,
SERVER_DIRECTORY,
updatedRelativeDest
)
@ -2651,7 +2604,7 @@ export default async function build(
})
await nextBuildSpan.traceChild('print-tree-view').traceAsyncFn(() =>
printTreeView(pageKeys, allPageInfos, isLikeServerless, {
printTreeView(pageKeys, allPageInfos, {
distPath: distDir,
buildId: buildId,
pagesDir,

View file

@ -293,7 +293,6 @@ export async function printTreeView(
app?: ReadonlyArray<string>
},
pageInfos: Map<string, PageInfo>,
serverless: boolean,
{
distPath,
buildId,
@ -627,7 +626,7 @@ export async function printTreeView(
],
usedSymbols.has('λ') && [
'λ',
serverless ? '(Lambda)' : '(Server)',
'(Server)',
`server-side renders at runtime (uses ${chalk.cyan(
'getInitialProps'
)} or ${chalk.cyan('getServerSideProps')})`,
@ -1165,7 +1164,6 @@ export async function buildAppStaticPaths({
export async function isPageStatic({
page,
distDir,
serverless,
configFileName,
runtimeEnvConfig,
httpAgentOptions,
@ -1181,7 +1179,6 @@ export async function isPageStatic({
}: {
page: string
distDir: string
serverless: boolean
configFileName: string
runtimeEnvConfig: any
httpAgentOptions: NextConfigComplete['httpAgentOptions']
@ -1250,7 +1247,6 @@ export async function isPageStatic({
componentsResult = await loadComponents({
distDir,
pathname: originalAppPath || page,
serverless,
hasServerComponents: !!hasServerComponents,
isAppPath: pageType === 'app',
})
@ -1430,7 +1426,6 @@ export async function isPageStatic({
export async function hasCustomGetInitialProps(
page: string,
distDir: string,
isLikeServerless: boolean,
runtimeEnvConfig: any,
checkingApp: boolean
): Promise<boolean> {
@ -1439,7 +1434,6 @@ export async function hasCustomGetInitialProps(
const components = await loadComponents({
distDir,
pathname: page,
serverless: isLikeServerless,
hasServerComponents: false,
isAppPath: false,
})
@ -1457,14 +1451,12 @@ export async function hasCustomGetInitialProps(
export async function getNamedExports(
page: string,
distDir: string,
isLikeServerless: boolean,
runtimeEnvConfig: any
): Promise<Array<string>> {
require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig)
const components = await loadComponents({
distDir,
pathname: page,
serverless: isLikeServerless,
hasServerComponents: false,
isAppPath: false,
})
@ -1726,16 +1718,6 @@ export function getPossibleMiddlewareFilenames(
)
}
export class MiddlewareInServerlessTargetError extends Error {
constructor() {
super(
'Next.js Middleware is not supported in the deprecated serverless target.\n' +
'Please remove `target: "serverless" from your next.config.js to use Middleware.'
)
this.name = 'MiddlewareInServerlessTargetError'
}
}
export class NestedMiddlewareError extends Error {
constructor(nestedFileNames: string[], mainDir: string, pagesDir: string) {
super(

View file

@ -24,7 +24,6 @@ import {
CLIENT_STATIC_FILES_RUNTIME_WEBPACK,
MIDDLEWARE_REACT_LOADABLE_MANIFEST,
REACT_LOADABLE_MANIFEST,
SERVERLESS_DIRECTORY,
SERVER_DIRECTORY,
COMPILER_NAMES,
CompilerNameValues,
@ -43,7 +42,6 @@ import { DropClientPage } from './webpack/plugins/next-drop-client-page-plugin'
import PagesManifestPlugin from './webpack/plugins/pages-manifest-plugin'
import { ProfilingPlugin } from './webpack/plugins/profiling-plugin'
import { ReactLoadablePlugin } from './webpack/plugins/react-loadable-plugin'
import { ServerlessPlugin } from './webpack/plugins/serverless-plugin'
import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin'
import { regexLikeCss } from './webpack/config/blocks/css'
import { CopyFilePlugin } from './webpack/plugins/copy-file-plugin'
@ -719,16 +717,9 @@ export default async function getBaseWebpackConfig(
const pageExtensions = config.pageExtensions
// Intentionally not using isTargetLikeServerless helper
const isLikeServerless =
target === 'serverless' || target === 'experimental-serverless-trace'
const outputPath =
isNodeServer || isEdgeServer
? path.join(
distDir,
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY
)
? path.join(distDir, SERVER_DIRECTORY)
: distDir
const clientEntries = isClient
@ -1242,8 +1233,7 @@ export default async function getBaseWebpackConfig(
]
: []),
]
: target !== 'serverless'
? [
: [
({
context,
request,
@ -1296,15 +1286,6 @@ export default async function getBaseWebpackConfig(
})
}
),
]
: [
// When the 'serverless' target is used all node_modules will be compiled into the output bundles
// So that the 'serverless' bundles have 0 runtime dependencies
'next/dist/compiled/@ampproject/toolbox-optimizer', // except this one
// Mark this as external if not enabled so it doesn't cause a
// webpack error from being missing
...(config.experimental.optimizeCss ? [] : ['critters']),
],
optimization: {
emitOnErrors: !dev,
@ -1848,7 +1829,6 @@ export default async function getBaseWebpackConfig(
}),
(isClient || isEdgeServer) && new DropClientPage(),
config.outputFileTracing &&
!isLikeServerless &&
(isNodeServer || isEdgeServer) &&
!dev &&
new (require('./webpack/plugins/next-trace-entrypoints-plugin').TraceEntryPointsPlugin)(
@ -1893,12 +1873,8 @@ export default async function getBaseWebpackConfig(
resourceRegExp: /react-is/,
contextRegExp: /next[\\/]dist[\\/]/,
}),
target === 'serverless' &&
(isNodeServer || isEdgeServer) &&
new ServerlessPlugin(),
(isNodeServer || isEdgeServer) &&
new PagesManifestPlugin({
serverless: isLikeServerless,
dev,
isEdgeRuntime: isEdgeServer,
appDirEnabled: hasAppDir,
@ -1931,7 +1907,6 @@ export default async function getBaseWebpackConfig(
FontStylesheetGatheringPlugin: typeof import('./webpack/plugins/font-stylesheet-gathering-plugin').FontStylesheetGatheringPlugin
}
return new FontStylesheetGatheringPlugin({
isLikeServerless,
adjustFontFallbacks: config.experimental.adjustFontFallbacks,
adjustFontFallbacksWithSizeAdjust:
config.experimental.adjustFontFallbacksWithSizeAdjust,

View file

@ -52,20 +52,16 @@ export class FontStylesheetGatheringPlugin {
compiler?: webpack.Compiler
gatheredStylesheets: Array<string> = []
manifestContent: FontManifest = []
isLikeServerless: boolean
adjustFontFallbacks?: boolean
adjustFontFallbacksWithSizeAdjust?: boolean
constructor({
isLikeServerless,
adjustFontFallbacks,
adjustFontFallbacksWithSizeAdjust,
}: {
isLikeServerless: boolean
adjustFontFallbacks?: boolean
adjustFontFallbacksWithSizeAdjust?: boolean
}) {
this.isLikeServerless = isLikeServerless
this.adjustFontFallbacks = adjustFontFallbacks
this.adjustFontFallbacksWithSizeAdjust = adjustFontFallbacksWithSizeAdjust
}
@ -184,25 +180,6 @@ export class FontStylesheetGatheringPlugin {
this.parserHandler
)
compiler.hooks.make.tapAsync(this.constructor.name, (compilation, cb) => {
if (this.isLikeServerless) {
/**
* Inline font manifest for serverless case only.
* For target: server drive the manifest through physical file and less of webpack magic.
*/
const mainTemplate = compilation.mainTemplate
mainTemplate.hooks.requireExtensions.tap(
this.constructor.name,
(source: string) => {
return `${source}
// Font manifest declaration
__webpack_require__.__NEXT_FONT_MANIFEST__ = ${JSON.stringify(
this.manifestContent
)};
// Enable feature:
process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(true);`
}
)
}
compilation.hooks.finishModules.tapAsync(
this.constructor.name,
async (modules: any, modulesFinished: Function) => {

View file

@ -19,23 +19,19 @@ let nodeServerAppPaths = {}
export default class PagesManifestPlugin
implements webpack.WebpackPluginInstance
{
serverless: boolean
dev: boolean
isEdgeRuntime: boolean
appDirEnabled: boolean
constructor({
serverless,
dev,
isEdgeRuntime,
appDirEnabled,
}: {
serverless: boolean
dev: boolean
isEdgeRuntime: boolean
appDirEnabled: boolean
}) {
this.serverless = serverless
this.dev = dev
this.isEdgeRuntime = isEdgeRuntime
this.appDirEnabled = appDirEnabled

View file

@ -1,37 +0,0 @@
import { webpack } from 'next/dist/compiled/webpack/webpack'
/**
* Makes sure there are no dynamic chunks when the target is serverless
* The dynamic chunks are integrated back into their parent chunk
* This is to make sure there is a single render bundle instead of that bundle importing dynamic chunks
*/
export class ServerlessPlugin {
apply(compiler: webpack.Compiler) {
compiler.hooks.compilation.tap('ServerlessPlugin', (compilation) => {
const hook = compilation.hooks.optimizeChunks
hook.tap('ServerlessPlugin', (chunks) => {
for (const chunk of chunks) {
// If chunk is not an entry point skip them
if (compilation.chunkGraph.getNumberOfEntryModules(chunk) === 0) {
continue
}
// Async chunks are usages of import() for example
const dynamicChunks = chunk.getAllAsyncChunks()
for (const dynamicChunk of dynamicChunks) {
for (const module of compilation.chunkGraph.getChunkModulesIterable(
dynamicChunk
)) {
// Add module back into the entry chunk
if (!compilation.chunkGraph.isModuleInChunk(module, chunk)) {
compilation.chunkGraph.connectChunkAndModule(chunk, module)
}
}
}
}
})
})
}
}

File diff suppressed because one or more lines are too long

View file

@ -28,11 +28,9 @@ import {
PAGES_MANIFEST,
PHASE_EXPORT,
PRERENDER_MANIFEST,
SERVERLESS_DIRECTORY,
SERVER_DIRECTORY,
} from '../shared/lib/constants'
import loadConfig from '../server/config'
import { isTargetLikeServerless } from '../server/utils'
import { ExportPathMap, NextConfigComplete } from '../server/config-shared'
import { eventCliSession } from '../telemetry/events'
import { hasNextSupport } from '../telemetry/ci-info'
@ -180,7 +178,6 @@ export default async function exportApp(
}
const subFolders = nextConfig.trailingSlash && !options.buildExport
const isLikeServerless = nextConfig.target !== 'server'
if (!options.silent && !options.buildExport) {
Log.info(`using build directory: ${distDir}`)
@ -215,7 +212,7 @@ export default async function exportApp(
!options.pages &&
(require(join(
distDir,
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY,
SERVER_DIRECTORY,
PAGES_MANIFEST
)) as PagesManifest)
@ -611,7 +608,6 @@ export default async function exportApp(
serverRuntimeConfig,
subFolders,
buildExport: options.buildExport,
serverless: isTargetLikeServerless(nextConfig.target),
optimizeFonts: nextConfig.optimizeFonts as FontConfig,
optimizeCss: nextConfig.experimental.optimizeCss,
disableOptimizedLoading:
@ -673,7 +669,7 @@ export default async function exportApp(
}
route = normalizePagePath(route)
const pagePath = getPagePath(pageName, distDir, isLikeServerless)
const pagePath = getPagePath(pageName, distDir)
const distPagesDir = join(
pagePath,
// strip leading / and then recurse number of nested dirs

View file

@ -1,6 +1,4 @@
import type { ComponentType } from 'react'
import type { FontManifest, FontConfig } from '../server/font-utils'
import type { GetStaticProps } from '../types'
import type { IncomingMessage, ServerResponse } from 'http'
import type { DomainLocale, NextConfigComplete } from '../server/config-shared'
import type { NextParsedUrlQuery } from '../server/request-meta'
@ -8,7 +6,6 @@ import type { NextParsedUrlQuery } from '../server/request-meta'
import '../server/node-polyfill-fetch'
import loadRequireHook from '../build/webpack/require-hook'
import url from 'url'
import { extname, join, dirname, sep } from 'path'
import { renderToHTML } from '../server/render'
import { promises } from 'fs'
@ -62,7 +59,6 @@ interface ExportPageInput {
buildExport?: boolean
serverRuntimeConfig: { [key: string]: any }
subFolders?: boolean
serverless: boolean
optimizeFonts: FontConfig
optimizeCss: any
disableOptimizedLoading: any
@ -99,11 +95,6 @@ interface RenderOpts {
supportsDynamicHTML?: boolean
}
type ComponentModule = ComponentType<{}> & {
renderReqToHTML: typeof renderToHTML
getStaticProps?: GetStaticProps
}
export default async function exportPage({
parentSpanId,
path,
@ -115,7 +106,6 @@ export default async function exportPage({
buildExport,
serverRuntimeConfig,
subFolders,
serverless,
optimizeFonts,
optimizeCss,
disableOptimizedLoading,
@ -191,12 +181,9 @@ export default async function exportPage({
getRouteMatcher(getRouteRegex(normalizedPage))(updatedPath) ||
undefined
if (params) {
// we have to pass these separately for serverless
if (!serverless) {
query = {
...query,
...params,
}
query = {
...query,
...params,
}
} else {
throw new Error(
@ -267,8 +254,7 @@ export default async function exportPage({
)
// If the ssg path has .html extension, and it's not builtin paths, use it directly
// Otherwise, use that as the filename instead
const isHtmlExtPath =
!serverless && !isBuiltinPaths && path.endsWith('.html')
const isHtmlExtPath = !isBuiltinPaths && path.endsWith('.html')
htmlFilename = isHtmlExtPath ? getHtmlFilename(path) : path
} else if (path === '/') {
// If the path is the root, just use index.html
@ -289,205 +275,118 @@ export default async function exportPage({
return !buildExport && getStaticProps && !isDynamicRoute(path)
}
if (serverless) {
const curUrl = url.parse(req.url!, true)
req.url = url.format({
...curUrl,
query: {
...curUrl.query,
...query,
},
})
const {
Component,
ComponentMod,
getServerSideProps,
getStaticProps,
pageConfig,
} = await loadComponents({
distDir,
pathname: page,
serverless,
hasServerComponents: !!serverComponents,
isAppPath: isAppDir,
})
const ampState = {
ampFirst: pageConfig?.amp === true,
hasQuery: Boolean(query.amp),
hybrid: pageConfig?.amp === 'hybrid',
}
inAmpMode = isInAmpMode(ampState)
hybridAmp = ampState.hybrid
const components = await loadComponents({
distDir,
pathname: page,
hasServerComponents: !!serverComponents,
isAppPath: isAppDir,
})
curRenderOpts = {
...components,
...renderOpts,
ampPath: renderAmpPath,
params,
optimizeFonts,
optimizeCss,
disableOptimizedLoading,
fontManifest: optimizeFonts ? requireFontManifest(distDir) : null,
locale: locale as string,
supportsDynamicHTML: false,
}
if (getServerSideProps) {
throw new Error(
`Error for page ${page}: ${SERVER_PROPS_EXPORT_ERROR}`
)
}
// during build we attempt rendering app dir paths
// and bail when dynamic dependencies are detected
// only fully static paths are fully generated here
if (isAppDir) {
const { renderToHTMLOrFlight } =
require('../server/app-render') as typeof import('../server/app-render')
// if it was auto-exported the HTML is loaded here
if (typeof Component === 'string') {
renderResult = RenderResult.fromStatic(Component)
queryWithAutoExportWarn()
} else {
// for non-dynamic SSG pages we should have already
// prerendered the file
if (renderedDuringBuild(getStaticProps))
return { ...results, duration: Date.now() - start }
try {
curRenderOpts.params ||= {}
if (getStaticProps && !htmlFilepath.endsWith('.html')) {
// make sure it ends with .html if the name contains a dot
htmlFilename += '.html'
htmlFilepath += '.html'
}
renderMethod = (ComponentMod as ComponentModule).renderReqToHTML
const result = await renderMethod(
req,
res,
'export',
{
ampPath: renderAmpPath,
/// @ts-ignore
optimizeFonts,
/// @ts-ignore
optimizeCss,
disableOptimizedLoading,
distDir,
fontManifest: optimizeFonts
? requireFontManifest(distDir, serverless)
: null,
locale: locale!,
locales: renderOpts.locales!,
},
// @ts-ignore
params
)
curRenderOpts = (result as any).renderOpts || {}
renderResult = (result as any).html
}
if (!renderResult && !(curRenderOpts as any).isNotFound) {
throw new Error(`Failed to render serverless page`)
}
} else {
const components = await loadComponents({
distDir,
pathname: page,
serverless,
hasServerComponents: !!serverComponents,
isAppPath: isAppDir,
})
curRenderOpts = {
...components,
...renderOpts,
ampPath: renderAmpPath,
params,
optimizeFonts,
optimizeCss,
disableOptimizedLoading,
fontManifest: optimizeFonts
? requireFontManifest(distDir, serverless)
: null,
locale: locale as string,
supportsDynamicHTML: false,
}
// during build we attempt rendering app dir paths
// and bail when dynamic dependencies are detected
// only fully static paths are fully generated here
if (isAppDir) {
const { renderToHTMLOrFlight } =
require('../server/app-render') as typeof import('../server/app-render')
try {
curRenderOpts.params ||= {}
const result = await renderToHTMLOrFlight(
req as any,
res as any,
page,
query,
curRenderOpts as any
)
const html = result?.toUnchunkedString()
const flightData = (curRenderOpts as any).pageData
const revalidate = (curRenderOpts as any).revalidate
results.fromBuildExportRevalidate = revalidate
if (revalidate !== 0) {
await promises.writeFile(htmlFilepath, html, 'utf8')
await promises.writeFile(
htmlFilepath.replace(/\.html$/, '.rsc'),
flightData
)
}
} catch (err: any) {
if (
err.digest !== DYNAMIC_ERROR_CODE &&
err.digest !== NOT_FOUND_ERROR_CODE &&
!err.digest?.startsWith(REDIRECT_ERROR_CODE)
) {
throw err
}
}
return { ...results, duration: Date.now() - start }
}
const ampState = {
ampFirst: components.pageConfig?.amp === true,
hasQuery: Boolean(query.amp),
hybrid: components.pageConfig?.amp === 'hybrid',
}
inAmpMode = isInAmpMode(ampState)
hybridAmp = ampState.hybrid
if (components.getServerSideProps) {
throw new Error(
`Error for page ${page}: ${SERVER_PROPS_EXPORT_ERROR}`
)
}
// for non-dynamic SSG pages we should have already
// prerendered the file
if (renderedDuringBuild(components.getStaticProps)) {
return { ...results, duration: Date.now() - start }
}
// TODO: de-dupe the logic here between serverless and server mode
if (components.getStaticProps && !htmlFilepath.endsWith('.html')) {
// make sure it ends with .html if the name contains a dot
htmlFilepath += '.html'
htmlFilename += '.html'
}
if (typeof components.Component === 'string') {
renderResult = RenderResult.fromStatic(components.Component)
queryWithAutoExportWarn()
} else {
/**
* This sets environment variable to be used at the time of static export by head.tsx.
* Using this from process.env allows targeting both serverless and SSR by calling
* `process.env.__NEXT_OPTIMIZE_FONTS`.
* TODO(prateekbh@): Remove this when experimental.optimizeFonts are being cleaned up.
*/
if (optimizeFonts) {
process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(optimizeFonts)
}
if (optimizeCss) {
process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true)
}
renderResult = await renderMethod(
req,
res,
const result = await renderToHTMLOrFlight(
req as any,
res as any,
page,
query,
// @ts-ignore
curRenderOpts
curRenderOpts as any
)
const html = result?.toUnchunkedString()
const flightData = (curRenderOpts as any).pageData
const revalidate = (curRenderOpts as any).revalidate
results.fromBuildExportRevalidate = revalidate
if (revalidate !== 0) {
await promises.writeFile(htmlFilepath, html, 'utf8')
await promises.writeFile(
htmlFilepath.replace(/\.html$/, '.rsc'),
flightData
)
}
} catch (err: any) {
if (
err.digest !== DYNAMIC_ERROR_CODE &&
err.digest !== NOT_FOUND_ERROR_CODE &&
!err.digest?.startsWith(REDIRECT_ERROR_CODE)
) {
throw err
}
}
return { ...results, duration: Date.now() - start }
}
const ampState = {
ampFirst: components.pageConfig?.amp === true,
hasQuery: Boolean(query.amp),
hybrid: components.pageConfig?.amp === 'hybrid',
}
inAmpMode = isInAmpMode(ampState)
hybridAmp = ampState.hybrid
if (components.getServerSideProps) {
throw new Error(`Error for page ${page}: ${SERVER_PROPS_EXPORT_ERROR}`)
}
// for non-dynamic SSG pages we should have already
// prerendered the file
if (renderedDuringBuild(components.getStaticProps)) {
return { ...results, duration: Date.now() - start }
}
// TODO: de-dupe the logic here between serverless and server mode
if (components.getStaticProps && !htmlFilepath.endsWith('.html')) {
// make sure it ends with .html if the name contains a dot
htmlFilepath += '.html'
htmlFilename += '.html'
}
if (typeof components.Component === 'string') {
renderResult = RenderResult.fromStatic(components.Component)
queryWithAutoExportWarn()
} else {
/**
* This sets environment variable to be used at the time of static export by head.tsx.
* Using this from process.env allows targeting both serverless and SSR by calling
* `process.env.__NEXT_OPTIMIZE_FONTS`.
* TODO(prateekbh@): Remove this when experimental.optimizeFonts are being cleaned up.
*/
if (optimizeFonts) {
process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(optimizeFonts)
}
if (optimizeCss) {
process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true)
}
renderResult = await renderMethod(
req,
res,
page,
query,
// @ts-ignore
curRenderOpts
)
}
results.ssgNotFound = (curRenderOpts as any).isNotFound
const validateAmp = async (
@ -529,29 +428,14 @@ export default async function exportPage({
await promises.access(ampHtmlFilepath)
} catch (_) {
// make sure it doesn't exist from manual mapping
let ampRenderResult
if (serverless) {
req.url += (req.url!.includes('?') ? '&' : '?') + 'amp=1'
let ampRenderResult = await renderMethod(
req,
res,
page,
// @ts-ignore
ampRenderResult = (
await (renderMethod as any)(
req,
res,
'export',
curRenderOpts,
params
)
).html
} else {
ampRenderResult = await renderMethod(
req,
res,
page,
// @ts-ignore
{ ...query, amp: '1' },
curRenderOpts as any
)
}
{ ...query, amp: '1' },
curRenderOpts as any
)
const ampHtml = ampRenderResult
? ampRenderResult.toUnchunkedString()

View file

@ -166,21 +166,6 @@ type ResponsePayload = {
revalidateOptions?: any
}
export function prepareServerlessUrl(
req: BaseNextRequest,
query: ParsedUrlQuery
): void {
const curUrl = parseUrl(req.url!, true)
req.url = formatUrl({
...curUrl,
search: undefined,
query: {
...curUrl.query,
...query,
},
})
}
export default abstract class Server<ServerOptions extends Options = Options> {
protected dir: string
protected quiet: boolean
@ -1000,10 +985,6 @@ export default abstract class Server<ServerOptions extends Options = Options> {
const is404Page = pathname === '/404'
const is500Page = pathname === '/500'
const isAppPath = components.isAppPath
const isLikeServerless =
typeof components.ComponentMod === 'object' &&
typeof (components.ComponentMod as any).renderReqToHTML === 'function'
const hasServerProps = !!components.getServerSideProps
let hasStaticPaths = !!components.getStaticPaths
@ -1157,11 +1138,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
// be static so we can collect revalidate and populate the
// cache if there are no dynamic data requirements
opts.supportsDynamicHTML =
!isSSG &&
!isLikeServerless &&
!isBotRequest &&
!query.amp &&
isSupportedDocument
!isSSG && !isBotRequest && !query.amp && isSupportedDocument
}
const defaultLocale = isSSG
@ -1285,87 +1262,64 @@ export default abstract class Server<ServerOptions extends Options = Options> {
let isNotFound: boolean | undefined
let isRedirect: boolean | undefined
// handle serverless
if (isLikeServerless) {
const renderResult = await (
components.ComponentMod as any
).renderReqToHTML(req, res, 'passthrough', {
locale,
locales,
defaultLocale,
optimizeFonts: this.renderOpts.optimizeFonts,
optimizeCss: this.renderOpts.optimizeCss,
nextScriptWorkers: this.renderOpts.nextScriptWorkers,
distDir: this.distDir,
fontManifest: this.renderOpts.fontManifest,
domainLocales: this.renderOpts.domainLocales,
const origQuery = parseUrl(req.url || '', true).query
// clear any dynamic route params so they aren't in
// the resolvedUrl
if (opts.params) {
Object.keys(opts.params).forEach((key) => {
delete origQuery[key]
})
body = renderResult.html
pageData = renderResult.renderOpts.pageData
sprRevalidate = renderResult.renderOpts.revalidate
isNotFound = renderResult.renderOpts.isNotFound
isRedirect = renderResult.renderOpts.isRedirect
} else {
const origQuery = parseUrl(req.url || '', true).query
// clear any dynamic route params so they aren't in
// the resolvedUrl
if (opts.params) {
Object.keys(opts.params).forEach((key) => {
delete origQuery[key]
})
}
const hadTrailingSlash =
urlPathname !== '/' && this.nextConfig.trailingSlash
const resolvedUrl = formatUrl({
pathname: `${resolvedUrlPathname}${hadTrailingSlash ? '/' : ''}`,
// make sure to only add query values from original URL
query: origQuery,
})
const renderOpts: RenderOpts = {
...components,
...opts,
isDataReq,
resolvedUrl,
locale,
locales,
defaultLocale,
// For getServerSideProps and getInitialProps we need to ensure we use the original URL
// and not the resolved URL to prevent a hydration mismatch on
// asPath
resolvedAsPath:
hasServerProps || hasGetInitialProps
? formatUrl({
// we use the original URL pathname less the _next/data prefix if
// present
pathname: `${urlPathname}${hadTrailingSlash ? '/' : ''}`,
query: origQuery,
})
: resolvedUrl,
}
if (isSSG || hasStaticPaths) {
renderOpts.supportsDynamicHTML = false
}
const renderResult = await this.renderHTML(
req,
res,
pathname,
query,
renderOpts
)
body = renderResult
// TODO: change this to a different passing mechanism
pageData = (renderOpts as any).pageData
sprRevalidate = (renderOpts as any).revalidate
isNotFound = (renderOpts as any).isNotFound
isRedirect = (renderOpts as any).isRedirect
}
const hadTrailingSlash =
urlPathname !== '/' && this.nextConfig.trailingSlash
const resolvedUrl = formatUrl({
pathname: `${resolvedUrlPathname}${hadTrailingSlash ? '/' : ''}`,
// make sure to only add query values from original URL
query: origQuery,
})
const renderOpts: RenderOpts = {
...components,
...opts,
isDataReq,
resolvedUrl,
locale,
locales,
defaultLocale,
// For getServerSideProps and getInitialProps we need to ensure we use the original URL
// and not the resolved URL to prevent a hydration mismatch on
// asPath
resolvedAsPath:
hasServerProps || hasGetInitialProps
? formatUrl({
// we use the original URL pathname less the _next/data prefix if
// present
pathname: `${urlPathname}${hadTrailingSlash ? '/' : ''}`,
query: origQuery,
})
: resolvedUrl,
}
if (isSSG || hasStaticPaths) {
renderOpts.supportsDynamicHTML = false
}
const renderResult = await this.renderHTML(
req,
res,
pathname,
query,
renderOpts
)
body = renderResult
// TODO: change this to a different passing mechanism
pageData = (renderOpts as any).pageData
sprRevalidate = (renderOpts as any).revalidate
isNotFound = (renderOpts as any).isNotFound
isRedirect = (renderOpts as any).isRedirect
let value: ResponseCacheValue | null
if (isNotFound) {
@ -1479,9 +1433,6 @@ export default abstract class Server<ServerOptions extends Options = Options> {
// We need to generate the fallback on-demand for development.
else {
query.__nextFallback = 'true'
if (isLikeServerless) {
prepareServerlessUrl(req, query)
}
const result = await doRender()
if (!result) {
return null

View file

@ -22,13 +22,10 @@ import {
VALID_LOADERS,
} from '../shared/lib/image-config'
import { loadEnvConfig } from '@next/env'
import { hasNextSupport } from '../telemetry/ci-info'
import { gte as semverGte } from 'next/dist/compiled/semver'
export { DomainLocale, NextConfig, normalizeConfig } from './config-shared'
const targets = ['server', 'serverless', 'experimental-serverless-trace']
const experimentalWarning = execOnce(
(configFileName: string, features: string[]) => {
const s = features.length > 1 ? 's' : ''
@ -805,17 +802,9 @@ export default async function loadConfig(
)
}
if (userConfig.target && !targets.includes(userConfig.target)) {
throw new Error(
`Specified target is invalid. Provided: "${
userConfig.target
}" should be one of ${targets.join(', ')}`
)
}
if (userConfig.target && userConfig.target !== 'server') {
Log.warn(
'The `target` config is deprecated and will be removed in a future version.\n' +
throw new Error(
`The "target" property is no longer supported in ${configFileName}.\n` +
'See more info here https://nextjs.org/docs/messages/deprecated-target-config'
)
}
@ -829,10 +818,6 @@ export default async function loadConfig(
: canonicalBase) || ''
}
if (process.env.NEXT_PRIVATE_TARGET || hasNextSupport) {
userConfig.target = process.env.NEXT_PRIVATE_TARGET || 'server'
}
return assignDefaults({
configOrigin: relative(dir, path),
configFile: path,

View file

@ -448,7 +448,6 @@ export default class HotReloader {
pagesDir: this.pagesDir,
previewMode: this.previewProps,
rootDir: this.dir,
target: 'server',
pageExtensions: this.config.pageExtensions,
})
)
@ -519,7 +518,6 @@ export default class HotReloader {
pagesDir: this.pagesDir,
previewMode: this.previewProps,
rootDir: this.dir,
target: 'server',
pageExtensions: this.config.pageExtensions,
})
).client,

View file

@ -1297,7 +1297,6 @@ export default class DevServer extends Server {
const pathsResult = await this.getStaticPathsWorker().loadStaticPaths({
distDir: this.distDir,
pathname,
serverless: !this.renderOpts.dev && this._isLikeServerless,
config: {
configFileName,
publicRuntimeConfig,

View file

@ -19,7 +19,6 @@ let workerWasUsed = false
export async function loadStaticPaths({
distDir,
pathname,
serverless,
config,
httpAgentOptions,
enableUndici,
@ -30,7 +29,6 @@ export async function loadStaticPaths({
}: {
distDir: string
pathname: string
serverless: boolean
config: RuntimeConfig
httpAgentOptions: NextConfigComplete['httpAgentOptions']
enableUndici: NextConfigComplete['enableUndici']
@ -59,7 +57,6 @@ export async function loadStaticPaths({
const components = await loadComponents({
distDir,
pathname: originalAppPath || pathname,
serverless,
hasServerComponents: false,
isAppPath: !!isAppPath,
})

View file

@ -65,63 +65,24 @@ export async function loadDefaultErrorComponents(distDir: string) {
export async function loadComponents({
distDir,
pathname,
serverless,
hasServerComponents,
isAppPath,
}: {
distDir: string
pathname: string
serverless: boolean
hasServerComponents: boolean
isAppPath: boolean
}): Promise<LoadComponentsReturnType> {
if (serverless) {
const ComponentMod = await requirePage(pathname, distDir, serverless)
if (typeof ComponentMod === 'string') {
return {
Component: ComponentMod as any,
pageConfig: {},
ComponentMod,
} as LoadComponentsReturnType
}
let {
default: Component,
getStaticProps,
getStaticPaths,
getServerSideProps,
} = ComponentMod
Component = await Component
getStaticProps = await getStaticProps
getStaticPaths = await getStaticPaths
getServerSideProps = await getServerSideProps
const pageConfig = (await ComponentMod.config) || {}
return {
Component,
pageConfig,
getStaticProps,
getStaticPaths,
getServerSideProps,
ComponentMod,
} as LoadComponentsReturnType
}
let DocumentMod = {}
let AppMod = {}
if (!isAppPath) {
;[DocumentMod, AppMod] = await Promise.all([
Promise.resolve().then(() =>
requirePage('/_document', distDir, serverless, false)
),
Promise.resolve().then(() =>
requirePage('/_app', distDir, serverless, false)
),
Promise.resolve().then(() => requirePage('/_document', distDir, false)),
Promise.resolve().then(() => requirePage('/_app', distDir, false)),
])
}
const ComponentMod = await Promise.resolve().then(() =>
requirePage(pathname, distDir, serverless, isAppPath)
requirePage(pathname, distDir, isAppPath)
)
const [buildManifest, reactLoadableManifest, serverComponentManifest] =

View file

@ -43,7 +43,6 @@ import {
CLIENT_PUBLIC_FILES_PATH,
APP_PATHS_MANIFEST,
FLIGHT_SERVER_CSS_MANIFEST,
SERVERLESS_DIRECTORY,
SERVER_DIRECTORY,
FONT_LOADER_MANIFEST,
} from '../shared/lib/constants'
@ -72,7 +71,6 @@ import loadRequireHook from '../build/webpack/require-hook'
import BaseServer, {
Options,
FindComponentsResult,
prepareServerlessUrl,
MiddlewareRoutingItem,
RoutingItem,
NoFallbackError,
@ -97,7 +95,7 @@ import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-
import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info'
import { getClonableBody } from './body-streams'
import { checkIsManualRevalidate } from './api-utils'
import { shouldUseReactRoot, isTargetLikeServerless } from './utils'
import { shouldUseReactRoot } from './utils'
import ResponseCache from './response-cache'
import { IncrementalCache } from './lib/incremental-cache'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
@ -252,24 +250,21 @@ export default class NextNodeServer extends BaseServer {
loadComponents({
distDir: this.distDir,
pathname: '/_document',
serverless: this._isLikeServerless,
hasServerComponents: false,
isAppPath: false,
}).catch(() => {})
loadComponents({
distDir: this.distDir,
pathname: '/_app',
serverless: this._isLikeServerless,
hasServerComponents: false,
isAppPath: false,
}).catch(() => {})
}
}
private compression =
this.nextConfig.compress && this.nextConfig.target === 'server'
? (compression() as ExpressMiddleware)
: undefined
private compression = this.nextConfig.compress
? (compression() as ExpressMiddleware)
: undefined
protected loadEnvConfig({
dev,
@ -785,14 +780,6 @@ export default class NextNodeServer extends BaseServer {
delete query.__nextLocale
delete query.__nextDefaultLocale
if (!this.renderOpts.dev && this._isLikeServerless) {
if (typeof pageModule.default === 'function') {
prepareServerlessUrl(req, query)
await pageModule.default(req, res)
return true
}
}
await apiResolver(
(req as NodeNextRequest).originalRequest,
(res as NodeNextResponse).originalResponse,
@ -882,14 +869,7 @@ export default class NextNodeServer extends BaseServer {
}
protected getPagePath(pathname: string, locales?: string[]): string {
return getPagePath(
pathname,
this.distDir,
this._isLikeServerless,
this.renderOpts.dev,
locales,
this.hasAppDir
)
return getPagePath(pathname, this.distDir, locales, this.hasAppDir)
}
protected async renderPageComponent(
@ -958,7 +938,6 @@ export default class NextNodeServer extends BaseServer {
const components = await loadComponents({
distDir: this.distDir,
pathname: pagePath,
serverless: !this.renderOpts.dev && this._isLikeServerless,
hasServerComponents: !!this.renderOpts.serverComponents,
isAppPath,
})
@ -1000,7 +979,7 @@ export default class NextNodeServer extends BaseServer {
}
protected getFontManifest(): FontManifest {
return requireFontManifest(this.distDir, this._isLikeServerless)
return requireFontManifest(this.distDir)
}
protected getServerComponentManifest() {
@ -2156,14 +2135,7 @@ export default class NextNodeServer extends BaseServer {
return result
}
protected get _isLikeServerless(): boolean {
return isTargetLikeServerless(this.nextConfig.target)
}
protected get serverDistDir() {
return join(
this.distDir,
this._isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY
)
return join(this.distDir, SERVER_DIRECTORY)
}
}

View file

@ -4,7 +4,6 @@ import {
FONT_MANIFEST,
PAGES_MANIFEST,
SERVER_DIRECTORY,
SERVERLESS_DIRECTORY,
APP_PATHS_MANIFEST,
} from '../shared/lib/constants'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
@ -16,15 +15,10 @@ import { PageNotFoundError, MissingStaticPage } from '../shared/lib/utils'
export function getPagePath(
page: string,
distDir: string,
serverless: boolean,
dev?: boolean,
locales?: string[],
appDirEnabled?: boolean
): string {
const serverBuildPath = join(
distDir,
serverless && !dev ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY
)
const serverBuildPath = join(distDir, SERVER_DIRECTORY)
let appPathsManifest: undefined | PagesManifest
if (appDirEnabled) {
@ -75,17 +69,9 @@ export function getPagePath(
export function requirePage(
page: string,
distDir: string,
serverless: boolean,
appDirEnabled?: boolean
): any {
const pagePath = getPagePath(
page,
distDir,
serverless,
false,
undefined,
appDirEnabled
)
const pagePath = getPagePath(page, distDir, undefined, appDirEnabled)
if (pagePath.endsWith('.html')) {
return promises.readFile(pagePath, 'utf8').catch((err) => {
throw new MissingStaticPage(page, err.message)
@ -94,11 +80,8 @@ export function requirePage(
return require(pagePath)
}
export function requireFontManifest(distDir: string, serverless: boolean) {
const serverBuildPath = join(
distDir,
serverless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY
)
export function requireFontManifest(distDir: string) {
const serverBuildPath = join(distDir, SERVER_DIRECTORY)
const fontManifest = require(join(serverBuildPath, FONT_MANIFEST))
return fontManifest
}

View file

@ -16,11 +16,5 @@ export function cleanAmpPath(pathname: string): string {
return pathname
}
export function isTargetLikeServerless(target: string) {
const isServerless = target === 'serverless'
const isServerlessTrace = target === 'experimental-serverless-trace'
return isServerless || isServerlessTrace
}
// When react version is >= 18 opt-in using reactRoot
export const shouldUseReactRoot = parseInt(React.version) >= 18

View file

@ -40,7 +40,6 @@ export const DEV_MIDDLEWARE_MANIFEST = '_devMiddlewareManifest.json'
export const REACT_LOADABLE_MANIFEST = 'react-loadable-manifest.json'
export const FONT_MANIFEST = 'font-manifest.json'
export const SERVER_DIRECTORY = 'server'
export const SERVERLESS_DIRECTORY = 'serverless'
export const CONFIG_FILES = ['next.config.js', 'next.config.mjs']
export const BUILD_ID_FILE = 'BUILD_ID'
export const BLOCKED_PAGES = ['/_document', '/_app', '/_error']

View file

@ -7,8 +7,6 @@ const glob = require('glob')
// eslint-disable-next-line import/no-extraneous-dependencies
const fs = require('fs-extra')
// eslint-disable-next-line import/no-extraneous-dependencies
const escapeRegex = require('escape-string-regexp')
// eslint-disable-next-line import/no-extraneous-dependencies
const resolveFrom = require('resolve-from')
export async function next__polyfill_nomodule(task, opts) {
@ -98,26 +96,6 @@ export async function ncc_node_html_parser(task, opts) {
)
.ncc({ packageName: 'node-html-parser', externals, target: 'es5' })
.target('compiled/node-html-parser')
const filePath = join(__dirname, 'compiled/node-html-parser/index.js')
const content = fs.readFileSync(filePath, 'utf8')
// remove AMD define branch as this forces the module to not
// be treated as commonjs in serverless mode
// TODO: this can be removed after serverless target is removed
fs.writeFileSync(
filePath,
content.replace(
new RegExp(
escapeRegex(
'if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define((function(){return '
) +
'\\w' +
escapeRegex('}))}else '),
'g'
),
''
)
)
}
// eslint-disable-next-line camelcase

View file

@ -91,7 +91,7 @@ export function eventCliSession(
hasNowJson: event.hasNowJson,
isCustomServer: event.isCustomServer,
hasNextConfig: nextConfig.configOrigin !== 'default',
buildTarget: nextConfig.target === 'server' ? 'default' : nextConfig.target,
buildTarget: 'default',
hasWebpackConfig: typeof nextConfig?.webpack === 'function',
hasBabelConfig: hasBabelConfig(dir),
imageEnabled: !!images,

View file

@ -14,7 +14,6 @@ import {
} from 'next-test-utils'
const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
let appPort
let app
@ -71,33 +70,6 @@ describe('Default 404 Page with custom _error', () => {
runTests('server')
})
describe.skip('serverless mode', () => {
afterAll(async () => {
await fs.remove(nextConfig)
await killApp(app)
})
it('should build successfully', async () => {
await fs.writeFile(
nextConfig,
`
module.exports = { target: 'experimental-serverless-trace' }
`
)
const { code } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
expect(code).toBe(0)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
runTests('serverless')
})
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()

View file

@ -13,11 +13,9 @@ import {
} from 'next-test-utils'
const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
const gip404Err =
/`pages\/404` can not have getInitialProps\/getServerSideProps/
let nextConfigContent
let stdout
let stderr
let buildId
@ -82,7 +80,6 @@ describe('404 Page Support SSG', () => {
afterAll(() => killApp(app))
it('should build successfully', async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
const {
code,
stderr: buildStderr,
@ -114,50 +111,6 @@ describe('404 Page Support SSG', () => {
runTests()
})
describe.skip('serverless mode', () => {
afterAll(async () => {
await fs.writeFile(nextConfig, nextConfigContent)
await killApp(app)
})
it('should build successfully', async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`
module.exports = { target: 'experimental-serverless-trace' }
`
)
const {
code,
stderr: buildStderr,
stdout: buildStdout,
} = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
expect(code).toBe(0)
expect(buildStderr).not.toMatch(gip404Err)
expect(buildStdout).not.toMatch(gip404Err)
appPort = await findPort()
stderr = ''
stdout = ''
app = await nextStart(appDir, appPort, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
runTests()
})
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()

View file

@ -16,11 +16,9 @@ import {
const appDir = join(__dirname, '../')
const pages404 = join(appDir, 'pages/404.js')
const nextConfig = join(appDir, 'next.config.js')
const gip404Err =
/`pages\/404` can not have getInitialProps\/getServerSideProps/
let nextConfigContent
let appPort
let app
@ -92,29 +90,6 @@ describe('404 Page Support', () => {
runTests('server')
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`
module.exports = {
target: 'serverless'
}
`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await fs.writeFile(nextConfig, nextConfigContent)
await killApp(app)
})
runTests('serverless')
})
it('should not cache for custom 404 page with gssp and revalidate disabled', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(

View file

@ -21,11 +21,9 @@ const appDir = join(__dirname, '../')
const pages500 = join(appDir, 'pages/500.js')
const pagesApp = join(appDir, 'pages/_app.js')
const pagesError = join(appDir, 'pages/_error.js')
const nextConfig = join(appDir, 'next.config.js')
const gip500Err =
/`pages\/500` can not have getInitialProps\/getServerSideProps/
let nextConfigContent
let appPort
let app
@ -85,30 +83,6 @@ describe('500 Page Support', () => {
runTests('server')
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`
module.exports = {
target: 'serverless'
}
`
)
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await fs.writeFile(nextConfig, nextConfigContent)
await killApp(app)
})
runTests('serverless')
})
it('does not build 500 statically with getInitialProps in _app', async () => {
await fs.writeFile(
pagesApp,

View file

@ -15,7 +15,6 @@ import {
} from 'next-test-utils'
const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
let builtServerPagesDir
let appPort
let app
@ -99,27 +98,6 @@ const runTests = (isDev = false) => {
}
describe('AMP SSG Support', () => {
describe.skip('serverless mode', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`
module.exports = {
target: 'experimental-serverless-trace'
}
`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
builtServerPagesDir = join(appDir, '.next/serverless/pages')
})
afterAll(async () => {
await fs.remove(nextConfig)
await killApp(app)
})
runTests()
})
describe('server mode', () => {
beforeAll(async () => {
await nextBuild(appDir)

View file

@ -1,5 +1,4 @@
/* eslint-env jest */
import fs from 'fs-extra'
import { join } from 'path'
import {
killApp,
@ -11,7 +10,6 @@ import {
} from 'next-test-utils'
const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
let appPort
let app
@ -72,22 +70,4 @@ describe('API routes', () => {
runTests()
})
describe.skip('Serverless support', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
})
})

View file

@ -19,7 +19,6 @@ import {
import json from '../big.json'
const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
let appPort
let stderr
let mode
@ -619,23 +618,4 @@ describe('API routes', () => {
runTests()
})
describe.skip('Serverless support', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await nextBuild(appDir)
mode = 'serverless'
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
})
})

View file

@ -1,7 +1,6 @@
/* eslint-env jest */
import path from 'path'
import fs from 'fs-extra'
import webdriver from 'next-webdriver'
import {
nextBuild,
@ -14,7 +13,6 @@ import {
} from 'next-test-utils'
const appDir = path.join(__dirname, '..')
const nextConfig = path.join(appDir, 'next.config.js')
let appPort
let app
@ -68,21 +66,4 @@ describe('AppTree', () => {
afterAll(() => killApp(app))
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
})
})

View file

@ -1,5 +1,4 @@
module.exports = {
// target: 'experimental-serverless-trace',
webpack: (config) => {
config.experiments = config.experiments || {}
config.experiments.topLevelAwait = true

View file

@ -11,7 +11,6 @@ import {
launchApp,
nextBuild,
nextStart,
File,
check,
} from 'next-test-utils'
import { join } from 'path'
@ -19,7 +18,6 @@ import { join } from 'path'
let app
let appPort
const appDir = join(__dirname, '../')
const nextConfig = new File(join(appDir, 'next.config.js'))
function runTests(dev = false) {
it('ssr async page modules', async () => {
@ -130,19 +128,4 @@ describe('Async modules', () => {
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
nextConfig.replace('// target:', 'target:')
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await nextConfig.restore()
await killApp(app)
})
runTests()
})
})

View file

@ -1,11 +1,9 @@
/* eslint-env jest */
import path from 'path'
import fs from 'fs-extra'
import { nextBuild } from 'next-test-utils'
const appDir = path.join(__dirname, '..')
const nextConfig = path.join(appDir, 'next.config.js')
const runTests = () => {
it('should not opt-out of auto static optimization from invalid _error', async () => {
@ -35,20 +33,4 @@ describe('Auto Export _error bail', () => {
describe('server mode', () => {
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(() =>
fs.writeFile(
nextConfig,
`
module.exports = {
target: 'experimental-serverless-trace'
}
`
)
)
afterAll(() => fs.remove(nextConfig))
runTests()
})
})

View file

@ -1,5 +1,4 @@
module.exports = {
// target: 'serverless',
exportPathMap() {
return {
'/': { page: '/hello', query: { first: 'second' } },

View file

@ -1,12 +1,10 @@
/* eslint-env jest */
import path from 'path'
import fs from 'fs-extra'
import { nextBuild, nextExport } from 'next-test-utils'
const appDir = path.join(__dirname, '..')
const outdir = path.join(__dirname, 'out')
const nextConfig = path.join(appDir, 'next.config.js')
let stderr
let exitCode
@ -23,8 +21,6 @@ const runTests = () => {
})
}
let origNextConfig
describe('Auto Export', () => {
describe('server mode', () => {
beforeAll(async () => {
@ -40,23 +36,4 @@ describe('Auto Export', () => {
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
origNextConfig = await fs.readFile(nextConfig, 'utf8')
await nextBuild(appDir)
const { stderr: curStderr, code: curCode } = await nextExport(
appDir,
{ outdir },
{ stderr: true }
)
stderr = curStderr
exitCode = curCode
})
afterAll(async () => {
await fs.writeFile(nextConfig, origNextConfig)
})
runTests()
})
})

View file

@ -1,3 +0,0 @@
module.exports = {
target: 'serverless',
}

View file

@ -1,2 +0,0 @@
// eslint-disable-next-line
export default () => something.error

View file

@ -1,21 +0,0 @@
/* eslint-env jest */
import fs from 'fs'
import path from 'path'
import { nextBuild } from 'next-test-utils'
const appDir = path.join(__dirname, '..')
describe.skip('Auto Export Error Serverless', () => {
it('fails to emit the page', async () => {
const { stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(
fs.existsSync(path.join(appDir, '.next/serverless/pages/index.html'))
).toBe(false)
expect(stderr).toContain('ReferenceError')
expect(stderr).toContain('Build error occurred')
})
})

View file

@ -1,3 +0,0 @@
module.exports = {
target: 'serverless',
}

View file

@ -1,7 +0,0 @@
import { useRouter } from 'next/router'
export default () => {
const { query } = useRouter()
return <p>post: {query.post}</p>
}

View file

@ -1,25 +0,0 @@
/* eslint-env jest */
import webdriver from 'next-webdriver'
import path from 'path'
import { nextBuild, nextStart, findPort, killApp } from 'next-test-utils'
const appDir = path.join(__dirname, '..')
let appPort
let app
describe.skip('Auto Export Serverless', () => {
it('Refreshes query on mount', async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const browser = await webdriver(appPort, '/post-1')
const html = await browser.eval('document.body.innerHTML')
expect(html).toMatch(/post.*post-1/)
expect(html).toMatch(/nextExport/)
await killApp(app)
await browser.close()
})
})

View file

@ -1,4 +1,3 @@
module.exports = {
// target: 'experimental-serverless-trace',
basePath: '/docs',
}

View file

@ -3,7 +3,6 @@ import {
findPort,
killApp,
nextStart,
File,
launchApp,
} from 'next-test-utils'
import webdriver from 'next-webdriver'
@ -15,7 +14,6 @@ let app
let appPort
let buildId
const appDir = join(__dirname, '..')
const nextConfig = new File(join(appDir, 'next.config.js'))
const runTests = () => {
it('should use correct data URL for root catch-all', async () => {
@ -50,18 +48,3 @@ describe('production mode', () => {
afterAll(() => killApp(app))
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
nextConfig.replace('// target', 'target')
await nextBuild(appDir)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
nextConfig.restore()
await killApp(app)
})
runTests()
})

View file

@ -54,22 +54,4 @@ describe('bigint API route support', () => {
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
})
})

View file

@ -1,7 +1,6 @@
/* eslint-env jest */
import { join } from 'path'
import fs from 'fs-extra'
import {
renderViaHTTP,
nextBuild,
@ -11,12 +10,9 @@ import {
} from 'next-test-utils'
const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
const errorRegex = /getStaticPaths was added without a getStaticProps in/
describe('Catches Missing getStaticProps', () => {
afterAll(() => fs.remove(nextConfig))
it('should catch it in dev mode', async () => {
const appPort = await findPort()
const app = await launchApp(appDir, appPort)
@ -27,19 +23,6 @@ describe('Catches Missing getStaticProps', () => {
})
it('should catch it in server build mode', async () => {
await fs.remove(nextConfig)
const { stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(stderr).toMatch(errorRegex)
})
it('should catch it in serverless mode', async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`,
'utf8'
)
const { stderr } = await nextBuild(appDir, [], {
stderr: true,
})

View file

@ -33,25 +33,6 @@ describe('Cleaning distDir', () => {
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`
module.exports = {
target: 'serverless'
}
`
)
})
afterAll(async () => {
await fs.writeFile(nextConfig, nextConfigContent)
})
runTests()
})
describe('disabled write', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')

View file

@ -19,7 +19,6 @@ describe('Config Experimental Warning', () => {
configFile.write(`
module.exports = (phase, { defaultConfig }) => {
return {
target: 'server',
...defaultConfig,
}
}
@ -32,7 +31,7 @@ describe('Config Experimental Warning', () => {
it('should not show warning with config from object', async () => {
configFile.write(`
module.exports = {
target: 'server'
images: {},
}
`)
const { stderr } = await nextBuild(appDir, [], { stderr: true })
@ -42,7 +41,6 @@ describe('Config Experimental Warning', () => {
it('should show warning with config from object with experimental', async () => {
configFile.write(`
module.exports = {
target: 'server',
experimental: {
workerThreads: true
}
@ -57,7 +55,6 @@ describe('Config Experimental Warning', () => {
it('should show warning with config from function with experimental', async () => {
configFile.write(`
module.exports = (phase) => ({
target: 'server',
experimental: {
workerThreads: true
}
@ -72,7 +69,6 @@ describe('Config Experimental Warning', () => {
it('should not show warning with default value', async () => {
configFile.write(`
module.exports = (phase) => ({
target: 'server',
experimental: {
workerThreads: false
}

View file

@ -77,21 +77,3 @@ describe('CSS optimization for SSR apps', () => {
})
runTests()
})
describe.skip('Font optimization for emulated serverless apps', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'experimental-serverless-trace', experimental: {optimizeCss: true} }`,
'utf8'
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
})

View file

@ -13,7 +13,6 @@ import {
const appDir = join(__dirname, '..')
const page404 = join(appDir, 'pages/404.js')
const nextConfig = join(appDir, 'next.config.js')
let appPort
let app
@ -102,22 +101,4 @@ describe('Custom _error', () => {
runTests()
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
})
})

View file

@ -1,4 +1,3 @@
module.exports = {
target: 'server',
pageExtensions: ['page.js'],
}

View file

@ -8,7 +8,6 @@ import {
launchApp,
killApp,
renderViaHTTP,
File,
} from 'next-test-utils'
const appDir = join(__dirname, '..')
@ -46,19 +45,4 @@ describe('Custom page extension', () => {
afterAll(() => killApp(app))
runTests()
})
describe.skip('serverless mode', () => {
const nextConfig = new File(join(appDir, 'next.config.js'))
beforeAll(async () => {
nextConfig.replace('server', 'serverless')
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
nextConfig.restore()
})
runTests()
})
})

View file

@ -1,5 +1,4 @@
module.exports = {
// target: 'serverless',
async rewrites() {
// no-rewrites comment
return {

View file

@ -20,7 +20,6 @@ import {
getBrowserBodyText,
waitFor,
normalizeRegEx,
initNextServerScript,
nextExport,
hasRedbox,
check,
@ -2426,96 +2425,6 @@ describe('Custom routes', () => {
})
})
describe.skip('serverless mode', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfigPath, 'utf8')
await fs.writeFile(
nextConfigPath,
nextConfigContent.replace(/\/\/ target/, 'target'),
'utf8'
)
const { stdout: buildStdout } = await nextBuild(appDir, ['-d'], {
stdout: true,
})
stdout = buildStdout
appPort = await findPort()
app = await nextStart(appDir, appPort, {
onStdout: (msg) => {
stdout += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(async () => {
await fs.writeFile(nextConfigPath, nextConfigContent, 'utf8')
await killApp(app)
})
runTests()
})
describe.skip('raw serverless mode', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfigPath, 'utf8')
await fs.writeFile(
nextConfigPath,
nextConfigContent.replace(/\/\/ target/, 'target'),
'utf8'
)
await nextBuild(appDir)
appPort = await findPort()
app = await initNextServerScript(join(appDir, 'server.js'), /ready on/, {
...process.env,
PORT: appPort,
})
})
afterAll(async () => {
await fs.writeFile(nextConfigPath, nextConfigContent, 'utf8')
await killApp(app)
})
it('should apply rewrites in lambda correctly for page route', async () => {
const html = await renderViaHTTP(appPort, '/query-rewrite/first/second')
const data = JSON.parse(cheerio.load(html)('p').text())
expect(data).toEqual({
first: 'first',
second: 'second',
section: 'first',
name: 'second',
})
})
it('should apply rewrites in lambda correctly for dynamic route', async () => {
const html = await renderViaHTTP(appPort, '/blog/post-1')
expect(html).toContain('post-2')
})
it('should apply rewrites in lambda correctly for API route', async () => {
const data = JSON.parse(
await renderViaHTTP(appPort, '/api-hello-param/first')
)
expect(data).toEqual({
query: {
name: 'first',
hello: 'first',
},
})
})
it('should apply rewrites in lambda correctly for dynamic API route', async () => {
const data = JSON.parse(
await renderViaHTTP(appPort, '/api-dynamic-param/first')
)
expect(data).toEqual({
query: {
slug: 'first',
hello: 'first',
},
})
})
})
describe('should load custom routes when only one type is used', () => {
const runSoloTests = (isDev) => {
const buildAndStart = async () => {

View file

@ -81,29 +81,4 @@ describe('Dynamic Optional Routing Root Fallback', () => {
runTests()
})
describe.skip('serverless mode', () => {
let origNextConfig
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await fs.writeFile(nextConfig, origNextConfig)
await killApp(app)
})
runTests()
})
})

View file

@ -65,27 +65,4 @@ describe('Dynamic Optional Routing', () => {
runTests()
})
describe.skip('serverless mode', () => {
let origNextConfig
beforeAll(async () => {
origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await fs.writeFile(nextConfig, origNextConfig)
await killApp(app)
})
runTests()
})
})

View file

@ -11,7 +11,6 @@ import {
nextStart,
renderViaHTTP,
check,
initNextServerScript,
} from 'next-test-utils'
import { join } from 'path'
@ -313,133 +312,4 @@ describe('Dynamic Optional Routing', () => {
}
})
})
describe.skip('serverless mode', () => {
let origNextConfig
beforeAll(async () => {
origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`module.exports = { target: 'experimental-serverless-trace' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await fs.writeFile(nextConfig, origNextConfig)
await killApp(app)
})
runTests()
})
describe.skip('raw serverless mode', () => {
let origNextConfig
beforeAll(async () => {
origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`module.exports = { target: 'experimental-serverless-trace' }`
)
await nextBuild(appDir)
appPort = await findPort()
app = await initNextServerScript(join(appDir, 'server.js'), /ready on/, {
...process.env,
PORT: appPort,
})
})
afterAll(async () => {
await fs.writeFile(nextConfig, origNextConfig)
await killApp(app)
})
const render = (path, query) => {
return fetchViaHTTP(appPort, path, query, {
headers: {
// force relying on query values
'x-vercel-id': 'hi',
},
}).then((res) => res.text())
}
it('should render normal (non-dynamic) page', async () => {
const html = await render('/about')
const $ = cheerio.load(html)
expect($('#content').text()).toBe('about')
})
it('should render top level optional catch-all root', async () => {
const html = await render('/', { optionalName: '' })
const $ = cheerio.load(html)
expect($('#route').text()).toBe('top level route param: undefined')
expect($('#keys').text()).toBe('[]')
expect($('#asPath').text()).toBe('/')
})
it('should render top level optional catch-all one level', async () => {
const html = await render('/hello', { optionalName: 'hello' })
const $ = cheerio.load(html)
expect($('#route').text()).toBe('top level route param: [hello]')
expect($('#keys').text()).toBe('["optionalName"]')
expect($('#asPath').text()).toBe('/hello')
})
it('should render top level optional catch-all two levels', async () => {
const html = await render('/hello/world', { optionalName: 'hello/world' })
const $ = cheerio.load(html)
expect($('#route').text()).toBe('top level route param: [hello|world]')
expect($('#keys').text()).toBe('["optionalName"]')
expect($('#asPath').text()).toBe('/hello/world')
})
it('should render nested optional catch-all root', async () => {
const html = await render('/nested', { optionalName: '' })
const $ = cheerio.load(html)
expect($('#route').text()).toBe('nested route param: undefined')
expect($('#keys').text()).toBe('[]')
expect($('#asPath').text()).toBe('/nested')
})
it('should render nested optional catch-all one level', async () => {
const html = await render('/nested/hello', { optionalName: 'hello' })
const $ = cheerio.load(html)
expect($('#route').text()).toBe('nested route param: [hello]')
expect($('#keys').text()).toBe('["optionalName"]')
expect($('#asPath').text()).toBe('/nested/hello')
})
it('should render nested optional catch-all two levels', async () => {
const html = await render('/nested/hello/world', {
optionalName: 'hello/world',
})
const $ = cheerio.load(html)
expect($('#route').text()).toBe('nested route param: [hello|world]')
expect($('#keys').text()).toBe('["optionalName"]')
expect($('#asPath').text()).toBe('/nested/hello/world')
})
it('should render optional catch-all api root', async () => {
const text = await render('/api/post', { slug: '' })
const data = JSON.parse(text)
expect(data).toEqual({})
})
it('should render optional catch-all api root one level', async () => {
const text = await render('/api/post/hello', { slug: 'hello' })
const data = JSON.parse(text)
expect(data).toEqual({ slug: ['hello'] })
})
it('should render optional catch-all api root two levels', async () => {
const text = await render('/api/post/hello', { slug: 'hello/world' })
const data = JSON.parse(text)
expect(data).toEqual({ slug: ['hello', 'world'] })
})
})
})

View file

@ -27,7 +27,7 @@ let buildId
const appDir = join(__dirname, '../')
const buildIdPath = join(appDir, '.next/BUILD_ID')
function runTests({ dev, serverless }) {
function runTests({ dev }) {
if (!dev) {
it('should have correct cache entries on prefetch', async () => {
const browser = await webdriver(appPort, '/')
@ -1429,42 +1429,40 @@ function runTests({ dev, serverless }) {
})
})
if (!serverless) {
it('should output a pages-manifest correctly', async () => {
const manifest = await fs.readJson(
join(appDir, '.next/server/pages-manifest.json')
)
it('should output a pages-manifest correctly', async () => {
const manifest = await fs.readJson(
join(appDir, '.next/server/pages-manifest.json')
)
expect(manifest).toEqual({
'/[name]/[comment]': 'pages/[name]/[comment].js',
'/[name]/comments': 'pages/[name]/comments.js',
'/[name]': 'pages/[name].js',
'/[name]/on-mount-redir': 'pages/[name]/on-mount-redir.html',
'/another': 'pages/another.html',
'/b/[123]': 'pages/b/[123].js',
'/blog/[name]/comment/[id]': 'pages/blog/[name]/comment/[id].js',
'/c/[alongparamnameshouldbeallowedeventhoughweird]':
'pages/c/[alongparamnameshouldbeallowedeventhoughweird].js',
'/catchall-dash/[...hello-world]':
'pages/catchall-dash/[...hello-world].html',
'/d/[id]': 'pages/d/[id].html',
'/dash/[hello-world]': 'pages/dash/[hello-world].html',
'/': 'pages/index.html',
'/index/[...slug]': 'pages/index/[...slug].html',
'/on-mount/[post]': 'pages/on-mount/[post].html',
'/p1/p2/all-ssg/[...rest]': 'pages/p1/p2/all-ssg/[...rest].js',
'/p1/p2/all-ssr/[...rest]': 'pages/p1/p2/all-ssr/[...rest].js',
'/p1/p2/nested-all-ssg/[...rest]':
'pages/p1/p2/nested-all-ssg/[...rest].js',
'/p1/p2/predefined-ssg/[...rest]':
'pages/p1/p2/predefined-ssg/[...rest].js',
'/_app': 'pages/_app.js',
'/_error': 'pages/_error.js',
'/_document': 'pages/_document.js',
'/404': 'pages/404.html',
})
expect(manifest).toEqual({
'/[name]/[comment]': 'pages/[name]/[comment].js',
'/[name]/comments': 'pages/[name]/comments.js',
'/[name]': 'pages/[name].js',
'/[name]/on-mount-redir': 'pages/[name]/on-mount-redir.html',
'/another': 'pages/another.html',
'/b/[123]': 'pages/b/[123].js',
'/blog/[name]/comment/[id]': 'pages/blog/[name]/comment/[id].js',
'/c/[alongparamnameshouldbeallowedeventhoughweird]':
'pages/c/[alongparamnameshouldbeallowedeventhoughweird].js',
'/catchall-dash/[...hello-world]':
'pages/catchall-dash/[...hello-world].html',
'/d/[id]': 'pages/d/[id].html',
'/dash/[hello-world]': 'pages/dash/[hello-world].html',
'/': 'pages/index.html',
'/index/[...slug]': 'pages/index/[...slug].html',
'/on-mount/[post]': 'pages/on-mount/[post].html',
'/p1/p2/all-ssg/[...rest]': 'pages/p1/p2/all-ssg/[...rest].js',
'/p1/p2/all-ssr/[...rest]': 'pages/p1/p2/all-ssr/[...rest].js',
'/p1/p2/nested-all-ssg/[...rest]':
'pages/p1/p2/nested-all-ssg/[...rest].js',
'/p1/p2/predefined-ssg/[...rest]':
'pages/p1/p2/predefined-ssg/[...rest].js',
'/_app': 'pages/_app.js',
'/_error': 'pages/_error.js',
'/_document': 'pages/_document.js',
'/404': 'pages/404.html',
})
}
})
}
}
@ -1498,7 +1496,7 @@ describe('Dynamic Routing', () => {
})
afterAll(() => killApp(app))
runTests({ dev: true, serverless: false })
runTests({ dev: true })
})
describe('production mode', () => {
@ -1513,6 +1511,6 @@ describe('Dynamic Routing', () => {
})
afterAll(() => killApp(app))
runTests({ dev: false, serverless: false })
runTests({ dev: false })
})
})

View file

@ -384,60 +384,4 @@ describe('Env Config', () => {
runTests('server')
})
describe.skip('serverless mode', () => {
let nextConfigContent = ''
const nextConfigPath = join(appDir, 'next.config.js')
const envFiles = [
'.env',
'.env.development',
'.env.development.local',
'.env.local',
'.env.production',
'.env.production.local',
'.env.test',
'.env.test.local',
].map((file) => join(appDir, file))
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfigPath, 'utf8')
await fs.writeFile(
nextConfigPath,
nextConfigContent.replace(
'// update me',
`target: 'experimental-serverless-trace',`
)
)
const { code } = await nextBuild(appDir, [], {
env: {
PROCESS_ENV_KEY: 'processenvironment',
ENV_FILE_PROCESS_ENV: 'env-cli',
},
})
if (code !== 0) throw new Error(`Build failed with exit code ${code}`)
appPort = await findPort()
// rename the files so they aren't loaded by `next start`
// to test that they were bundled into the serverless files
for (const file of envFiles) {
await fs.rename(file, `${file}.bak`)
}
app = await nextStart(appDir, appPort, {
env: {
ENV_FILE_PROCESS_ENV: 'env-cli',
},
})
})
afterAll(async () => {
for (const file of envFiles) {
await fs.rename(`${file}.bak`, file)
}
await fs.writeFile(nextConfigPath, nextConfigContent)
await killApp(app)
})
runTests('serverless')
})
})

View file

@ -1,3 +0,0 @@
module.exports = {
target: 'serverless',
}

View file

@ -1,5 +0,0 @@
import { useAmp } from 'next/amp'
export const config = { amp: 'hybrid' }
export default () => <p>I'm an {useAmp() ? 'AMP' : 'normal'} page</p>

View file

@ -1,2 +0,0 @@
export default () => <p>Simple hybrid amp/non-amp page</p>
export const config = { amp: 'hybrid' }

View file

@ -1,5 +0,0 @@
import { useAmp } from 'next/amp'
export const config = { amp: 'hybrid' }
export default () => <p>I'm an {useAmp() ? 'AMP' : 'normal'} page</p>

View file

@ -1,2 +0,0 @@
export default () => <p>I am an AMP only page</p>
export const config = { amp: true }

View file

@ -1,3 +0,0 @@
export const config = { amp: 'hybrid' }
export default () => <p>I'm an AMP page</p>

View file

@ -1,3 +0,0 @@
export default function Docs(props) {
return <div>Hello again 👋</div>
}

View file

@ -1,3 +0,0 @@
export default function Index(props) {
return <div>Hello 👋</div>
}

View file

@ -1,65 +0,0 @@
/* eslint-env jest */
import { promises } from 'fs'
import { join } from 'path'
import cheerio from 'cheerio'
import { nextBuild, nextExport } from 'next-test-utils'
const { access, readFile } = promises
const appDir = join(__dirname, '../')
const outdir = join(appDir, 'out')
describe('Export with default map', () => {
beforeAll(async () => {
await nextBuild(appDir)
await nextExport(appDir, { outdir })
})
it('should export with folder that has dot in name', async () => {
expect.assertions(1)
await expect(access(join(outdir, 'v1.12.html'))).resolves.toBe(undefined)
})
it('should export an amp only page to clean path', async () => {
expect.assertions(1)
await expect(access(join(outdir, 'docs.html'))).resolves.toBe(undefined)
})
it('should export hybrid amp page correctly', async () => {
expect.assertions(2)
await expect(access(join(outdir, 'some.html'))).resolves.toBe(undefined)
await expect(access(join(outdir, 'some.amp.html'))).resolves.toBe(undefined)
})
it('should export nested hybrid amp page correctly', async () => {
expect.assertions(3)
await expect(access(join(outdir, 'docs.html'))).resolves.toBe(undefined)
await expect(access(join(outdir, 'docs.amp.html'))).resolves.toBe(undefined)
const html = await readFile(join(outdir, 'docs.html'))
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').attr('href')).toBe('/docs.amp')
})
it('should export nested hybrid amp page correctly with folder', async () => {
expect.assertions(3)
await expect(access(join(outdir, 'info.html'))).resolves.toBe(undefined)
await expect(access(join(outdir, 'info.amp.html'))).resolves.toBe(undefined)
const html = await readFile(join(outdir, 'info.html'))
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').attr('href')).toBe('/info.amp')
})
it('should export hybrid index amp page correctly', async () => {
expect.assertions(3)
await expect(access(join(outdir, 'index.html'))).resolves.toBe(undefined)
await expect(access(join(outdir, 'index.amp.html'))).resolves.toBe(
undefined
)
const html = await readFile(join(outdir, 'index.html'))
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').attr('href')).toBe('/index.amp')
})
})

View file

@ -1,8 +0,0 @@
module.exports = {
target: 'serverless',
exportPathMap() {
return {
'/regression/jeff-is-cool': { page: '/regression/[slug]' },
}
},
}

View file

@ -1,13 +0,0 @@
import { useRouter } from 'next/router'
function Regression() {
const { asPath } = useRouter()
if (typeof window !== 'undefined') {
window.__AS_PATHS = [...new Set([...(window.__AS_PATHS || []), asPath])]
}
return <div id="asPath">{asPath}</div>
}
Regression.getInitialProps = () => ({})
export default Regression

View file

@ -1,49 +0,0 @@
/* eslint-env jest */
import { join } from 'path'
import cheerio from 'cheerio'
import webdriver from 'next-webdriver'
import {
nextBuild,
nextExport,
startCleanStaticServer,
stopApp,
renderViaHTTP,
} from 'next-test-utils'
const appDir = join(__dirname, '../')
const outdir = join(appDir, 'out')
describe('Export Dynamic Pages', () => {
let server
let port
beforeAll(async () => {
await nextBuild(appDir)
await nextExport(appDir, { outdir })
server = await startCleanStaticServer(outdir)
port = server.address().port
})
afterAll(async () => {
await stopApp(server)
})
it('should of exported with correct asPath', async () => {
const html = await renderViaHTTP(port, '/regression/jeff-is-cool')
const $ = cheerio.load(html)
expect($('#asPath').text()).toBe('/regression/jeff-is-cool')
})
it('should hydrate with correct asPath', async () => {
expect.assertions(1)
const browser = await webdriver(port, '/regression/jeff-is-cool')
try {
expect(await browser.eval(`window.__AS_PATHS`)).toEqual([
'/regression/jeff-is-cool',
])
} finally {
await browser.close()
}
})
})

View file

@ -1 +0,0 @@
.next-dev

View file

@ -1 +0,0 @@
export default () => <p>Welcome to dynamic imports.</p>

View file

@ -1,38 +0,0 @@
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants')
module.exports = (phase) => {
return {
target: 'serverless',
distDir: phase === PHASE_DEVELOPMENT_SERVER ? '.next-dev' : '.next',
exportTrailingSlash: true,
exportPathMap: function () {
return {
'/': { page: '/' },
'/about': { page: '/about' },
'/button-link': { page: '/button-link' },
'/get-initial-props-with-no-query': {
page: '/get-initial-props-with-no-query',
},
'/counter': { page: '/counter' },
'/dynamic-imports': { page: '/dynamic-imports' },
'/dynamic': { page: '/dynamic', query: { text: 'cool dynamic text' } },
'/dynamic/one': {
page: '/dynamic',
query: { text: 'next export is nice' },
},
'/dynamic/two': {
page: '/dynamic',
query: { text: 'Vercel is awesome' },
},
'/file-name.md': {
page: '/dynamic',
query: { text: 'this file has an extension' },
},
'/query': { page: '/query', query: { a: 'blue' } },
'/query-update': { page: '/query-update', query: { a: 'blue' } },
// API route
'/blog/nextjs/comment/test': { page: '/blog/[post]/comment/[id]' },
}
}, // end exportPathMap
}
}

View file

@ -1,16 +0,0 @@
import Link from 'next/link'
const About = ({ bar }) => (
<div id="about-page">
<div>
<Link href="/">Go Back</Link>
</div>
<p>{`This is the About page foo${bar || ''}`}</p>
</div>
)
About.getInitialProps = async () => {
return { bar: typeof window === 'undefined' ? 'bar' : '' }
}
export default About

View file

@ -1,3 +0,0 @@
export default (req, res) => {
res.send('Hello World')
}

View file

@ -1,16 +0,0 @@
import { useRouter } from 'next/router'
const Page = () => {
const router = useRouter()
const { post, id } = router.query
return (
<>
<p>{`Blog post ${post} comment ${id || '(all)'}`}</p>
</>
)
}
Page.getInitialProps = () => ({})
export default Page

View file

@ -1,12 +0,0 @@
import Link from 'next/link'
export default () => (
<div id="button-link-page">
<div>
<Link href="/" passHref legacyBehavior>
<button>Go Back</button>
</Link>
</div>
<p>This is the About page</p>
</div>
)

View file

@ -1,27 +0,0 @@
import React from 'react'
import Link from 'next/link'
let counter = 0
export default class Counter extends React.Component {
increaseCounter() {
counter++
this.forceUpdate()
}
render() {
return (
<div id="counter-page">
<div>
<Link href="/" id="go-back">
Go Back
</Link>
</div>
<p>Counter: {counter}</p>
<button id="counter-increase" onClick={() => this.increaseCounter()}>
Increase
</button>
</div>
)
}
}

View file

@ -1,13 +0,0 @@
import Link from 'next/link'
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() => import('../components/hello'))
export default () => (
<div id="dynamic-imports-page">
<div>
<Link href="/">Go Back</Link>
</div>
<DynamicComponent />
</div>
)

View file

@ -1,31 +0,0 @@
/* global location */
import React from 'react'
import Link from 'next/link'
export default class DynamicPage extends React.Component {
static getInitialProps({ query }) {
return { text: query.text }
}
state = {}
componentDidMount() {
const [, hash] = location.href.split('#')
this.setState({ hash })
}
render() {
const { text } = this.props
const { hash } = this.state
return (
<div id="dynamic-page">
<div>
<Link href="/">Go Back</Link>
</div>
<p>{text}</p>
<div id="hash">Hash: {hash}</div>
</div>
)
}
}

View file

@ -1,7 +0,0 @@
const Page = ({ query }) => <div>{`Query is: ${query}`}</div>
Page.getInitialProps = ({ query }) => {
return { query: JSON.stringify(query) }
}
export default Page

View file

@ -1,65 +0,0 @@
import Link from 'next/link'
import Router from 'next/router'
function routeToAbout(e) {
e.preventDefault()
Router.push('/about')
}
export default () => (
<div id="home-page">
<div>
<Link href="/about" legacyBehavior>
<a id="about-via-link">About via Link</a>
</Link>
<a href="#" onClick={routeToAbout} id="about-via-router">
About via Router
</a>
<Link href="/counter" legacyBehavior>
<a id="counter">Counter</a>
</Link>
<Link href="/dynamic?text=cool+dynamic+text" legacyBehavior>
<a id="get-initial-props">getInitialProps</a>
</Link>
<Link
href="/dynamic?text=next+export+is+nice"
as="/dynamic/one"
legacyBehavior
>
<a id="dynamic-1">Dynamic 1</a>
</Link>
<Link
href="/dynamic?text=Vercel+is+awesome"
as="/dynamic/two"
legacyBehavior
>
<a id="dynamic-2">Dynamic 2</a>
</Link>
<Link href="/dynamic?text=Vercel+is+awesome#cool" legacyBehavior>
<a id="with-hash">With Hash</a>
</Link>
<Link
href="/dynamic?text=this+file+has+an+extension"
as="/file-name.md"
legacyBehavior
>
<a id="path-with-extension">Path with extension</a>
</Link>
<Link href="/level1" legacyBehavior>
<a id="level1-home-page">Level1 home page</a>
</Link>
<Link href="/level1/about" legacyBehavior>
<a id="level1-about-page">Level1 about page</a>
</Link>
<Link href="/dynamic-imports" legacyBehavior>
<a id="dynamic-imports-link">Dynamic imports page</a>
</Link>
</div>
<p>This is the home page</p>
<style jsx>{`
a {
margin: 0 10px 0 0;
}
`}</style>
</div>
)

View file

@ -1,10 +0,0 @@
import Link from 'next/link'
export default () => (
<div id="level1-about-page">
<div>
<Link href="/">Go Back</Link>
</div>
<p>This is the Level1 about page</p>
</div>
)

View file

@ -1,10 +0,0 @@
import Link from 'next/link'
export default () => (
<div id="level1-home-page">
<div>
<Link href="/">Go Back</Link>
</div>
<p>This is the Level1 home page</p>
</div>
)

View file

@ -1,7 +0,0 @@
import { useRouter } from 'next/router'
const Page = () => <div id="query">{JSON.stringify(useRouter().query)}</div>
Page.getInitialProps = () => ({ hello: 'world' })
export default Page

View file

@ -1,12 +0,0 @@
import { Component } from 'react'
class Page extends Component {
static getInitialProps({ query }) {
return { query }
}
render() {
return JSON.stringify(this.props.query, null, 2)
}
}
export default Page

View file

@ -1,28 +0,0 @@
/* 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 { stderr } = await runNextCommand(
['export', context.appDir, '--outdir', outdir],
{ stderr: true }
)
expect(stderr).toContain(
'https://nextjs.org/docs/messages/api-routes-static-export'
)
})
})
}

View file

@ -1,224 +0,0 @@
/* eslint-env jest */
import webdriver from 'next-webdriver'
import { check, getBrowserBodyText } from 'next-test-utils'
export default function (context) {
describe('Render via browser', () => {
it('should render the home page', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser.elementByCss('#home-page p').text()
expect(text).toBe('This is the home page')
await browser.close()
})
it('should add trailing slash on Link', async () => {
const browser = await webdriver(context.port, '/')
const link = await browser
.elementByCss('#about-via-link')
.getAttribute('href')
expect(link.slice(-1)).toBe('/')
})
it('should not add trailing slash on Link when disabled', async () => {
const browser = await webdriver(context.portNoTrailSlash, '/')
const link = await browser
.elementByCss('#about-via-link')
.getAttribute('href')
expect(link.slice(-1)).not.toBe('/')
})
it('should do navigations via Link', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser
.elementByCss('#about-via-link')
.click()
.waitForElementByCss('#about-page')
.elementByCss('#about-page p')
.text()
expect(text).toBe('This is the About page foo')
await browser.close()
})
it('should do navigations via Router', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser
.elementByCss('#about-via-router')
.click()
.waitForElementByCss('#about-page')
.elementByCss('#about-page p')
.text()
expect(text).toBe('This is the About page foo')
await browser.close()
})
it('should do run client side javascript', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser
.elementByCss('#counter')
.click()
.waitForElementByCss('#counter-page')
.elementByCss('#counter-increase')
.click()
.elementByCss('#counter-increase')
.click()
.elementByCss('#counter-page p')
.text()
expect(text).toBe('Counter: 2')
await browser.close()
})
it('should render pages using getInitialProps', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser
.elementByCss('#get-initial-props')
.click()
.waitForElementByCss('#dynamic-page')
.elementByCss('#dynamic-page p')
.text()
expect(text).toBe('cool dynamic text')
await browser.close()
})
it('should render dynamic pages with custom urls', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser
.elementByCss('#dynamic-1')
.click()
.waitForElementByCss('#dynamic-page')
.elementByCss('#dynamic-page p')
.text()
expect(text).toBe('next export is nice')
await browser.close()
})
it('should support client side navigation', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser
.elementByCss('#counter')
.click()
.waitForElementByCss('#counter-page')
.elementByCss('#counter-increase')
.click()
.elementByCss('#counter-increase')
.click()
.elementByCss('#counter-page p')
.text()
expect(text).toBe('Counter: 2')
// let's go back and come again to this page:
const textNow = await browser
.elementByCss('#go-back')
.click()
.waitForElementByCss('#home-page')
.elementByCss('#counter')
.click()
.waitForElementByCss('#counter-page')
.elementByCss('#counter-page p')
.text()
expect(textNow).toBe('Counter: 2')
await browser.close()
})
it('should render dynamic import components in the client', async () => {
const browser = await webdriver(context.port, '/')
await browser
.elementByCss('#dynamic-imports-link')
.click()
.waitForElementByCss('#dynamic-imports-page')
await check(
() => getBrowserBodyText(browser),
/Welcome to dynamic imports/
)
await browser.close()
})
it('should render pages with url hash correctly', async () => {
let browser
try {
browser = await webdriver(context.port, '/')
// Check for the query string content
const text = await browser
.elementByCss('#with-hash')
.click()
.waitForElementByCss('#dynamic-page')
.elementByCss('#dynamic-page p')
.text()
expect(text).toBe('Vercel is awesome')
await check(() => browser.elementByCss('#hash').text(), /cool/)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should navigate even if used a button inside <Link />', async () => {
const browser = await webdriver(context.port, '/button-link')
const text = await browser
.elementByCss('button')
.click()
.waitForElementByCss('#home-page')
.elementByCss('#home-page p')
.text()
expect(text).toBe('This is the home page')
await browser.close()
})
it('should update query after mount', async () => {
const browser = await webdriver(context.port, '/query-update?hello=world')
const query = await browser.elementByCss('#query').text()
expect(JSON.parse(query)).toEqual({ hello: 'world', a: 'blue' })
await browser.close()
})
describe('pages in the nested level: level1', () => {
it('should render the home page', async () => {
const browser = await webdriver(context.port, '/')
await browser.eval(
'document.getElementById("level1-home-page").click()'
)
await check(
() => getBrowserBodyText(browser),
/This is the Level1 home page/
)
await browser.close()
})
it('should render the about page', async () => {
const browser = await webdriver(context.port, '/')
await browser.eval(
'document.getElementById("level1-about-page").click()'
)
await check(
() => getBrowserBodyText(browser),
/This is the Level1 about page/
)
await browser.close()
})
})
})
}

View file

@ -1,46 +0,0 @@
/* eslint-env jest */
import webdriver from 'next-webdriver'
import { renderViaHTTP, getBrowserBodyText, check } from 'next-test-utils'
import cheerio from 'cheerio'
const loadJSONInPage = (pageContent) => {
const page = cheerio.load(pageContent)
return JSON.parse(page('#__next').text())
}
export default function (context) {
describe('Render in development mode', () => {
it('should render the home page', async () => {
const browser = await webdriver(context.port, '/')
await check(() => getBrowserBodyText(browser), /This is the home page/)
await browser.close()
})
it('should render pages only existent in exportPathMap page', async () => {
const browser = await webdriver(context.port, '/dynamic/one')
const text = await browser.elementByCss('#dynamic-page p').text()
expect(text).toBe('next export is nice')
await browser.close()
})
})
describe(`ExportPathMap's query in development mode`, () => {
it('should be present in ctx.query', async () => {
const pageContent = await renderViaHTTP(context.port, '/query')
const json = loadJSONInPage(pageContent)
expect(json).toEqual({ a: 'blue' })
})
it('should replace url query params in ctx.query when conflicting', async () => {
const pageContent = await renderViaHTTP(context.port, '/query?a=red')
const json = loadJSONInPage(pageContent)
expect(json).toEqual({ a: 'blue' })
})
it('should be merged with url query params in ctx.query', async () => {
const pageContent = await renderViaHTTP(context.port, '/query?b=green')
const json = loadJSONInPage(pageContent)
expect(json).toEqual({ a: 'blue', b: 'green' })
})
})
}

View file

@ -1,27 +0,0 @@
/* eslint-env jest */
import { join } from 'path'
import { File, runNextCommand } from 'next-test-utils'
export default function (context) {
describe('Dynamic routes export', () => {
const nextConfig = new File(join(context.appDir, 'next.config.js'))
beforeEach(() => {
nextConfig.replace('/blog/nextjs/comment/test', '/bad/path')
})
afterEach(() => {
nextConfig.restore()
})
it('Should throw error not matched route', async () => {
const outdir = join(context.appDir, 'outDynamic')
const { stderr } = await runNextCommand(
['export', context.appDir, '--outdir', outdir],
{ stderr: true }
).catch((err) => err)
expect(stderr).toContain(
'https://nextjs.org/docs/messages/export-path-mismatch'
)
})
})
}

View file

@ -1,96 +0,0 @@
/* eslint-env jest */
import { join } from 'path'
import { promises } from 'fs'
import {
nextBuild,
nextExport,
startStaticServer,
launchApp,
stopApp,
killApp,
findPort,
renderViaHTTP,
File,
} from 'next-test-utils'
import ssr from './ssr'
import browser from './browser'
import dev from './dev'
import dynamic from './dynamic'
import apiRoutes from './api-routes'
const { access, mkdir, writeFile } = promises
const appDir = join(__dirname, '../')
const context = {}
context.appDir = appDir
const devContext = {}
const nextConfig = new File(join(appDir, 'next.config.js'))
describe('Static Export', () => {
it('should delete existing exported files', async () => {
const outdir = join(appDir, 'out')
const tempfile = join(outdir, 'temp.txt')
await mkdir(outdir).catch((e) => {
if (e.code !== 'EEXIST') throw e
})
await writeFile(tempfile, 'Hello there')
await nextBuild(appDir)
await nextExport(appDir, { outdir })
let doesNotExist = false
await access(tempfile).catch((e) => {
if (e.code === 'ENOENT') doesNotExist = true
})
expect(doesNotExist).toBe(true)
})
beforeAll(async () => {
const outdir = join(appDir, 'out')
const outNoTrailSlash = join(appDir, 'outNoTrailSlash')
await nextBuild(appDir)
await nextExport(appDir, { outdir })
nextConfig.replace(
`exportTrailingSlash: true`,
`exportTrailingSlash: false`
)
await nextBuild(appDir)
await nextExport(appDir, { outdir: outNoTrailSlash })
nextConfig.restore()
context.server = await startStaticServer(outdir)
context.port = context.server.address().port
context.serverNoTrailSlash = await startStaticServer(outNoTrailSlash)
context.portNoTrailSlash = context.serverNoTrailSlash.address().port
devContext.port = await findPort()
devContext.server = await launchApp(
join(__dirname, '../'),
devContext.port,
true
)
// pre-build all pages at the start
await Promise.all([
renderViaHTTP(devContext.port, '/'),
renderViaHTTP(devContext.port, '/dynamic/one'),
])
})
afterAll(async () => {
await Promise.all([
stopApp(context.server),
killApp(devContext.server),
stopApp(context.serverNoTrailSlash),
])
})
ssr(context)
browser(context)
dev(devContext)
dynamic(context)
apiRoutes(context)
})

View file

@ -1,88 +0,0 @@
/* eslint-env jest */
import { renderViaHTTP } from 'next-test-utils'
import cheerio from 'cheerio'
export default function (context) {
describe('Render via SSR', () => {
it('should render the home page', async () => {
const html = await renderViaHTTP(context.port, '/')
expect(html).toMatch(/This is the home page/)
})
it('should render the about page', async () => {
const html = await renderViaHTTP(context.port, '/about')
expect(html).toMatch(/This is the About page foobar/)
})
it('should render links correctly', async () => {
const html = await renderViaHTTP(context.port, '/')
const $ = cheerio.load(html)
const dynamicLink = $('#dynamic-1').prop('href')
const filePathLink = $('#path-with-extension').prop('href')
expect(dynamicLink).toEqual('/dynamic/one/')
expect(filePathLink).toEqual('/file-name.md')
})
it('should render a page with getInitialProps', async () => {
const html = await renderViaHTTP(context.port, '/dynamic')
expect(html).toMatch(/cool dynamic text/)
})
it('should render a dynamically rendered custom url page', async () => {
const html = await renderViaHTTP(context.port, '/dynamic/one')
expect(html).toMatch(/next export is nice/)
})
it('should render pages with dynamic imports', async () => {
const html = await renderViaHTTP(context.port, '/dynamic-imports')
expect(html).toMatch(/Welcome to dynamic imports/)
})
it('should render paths with extensions', async () => {
const html = await renderViaHTTP(context.port, '/file-name.md')
expect(html).toMatch(/this file has an extension/)
})
it('should give empty object for query if there is no query', async () => {
const html = await renderViaHTTP(
context.port,
'/get-initial-props-with-no-query'
)
expect(html).toMatch(/Query is: {}/)
})
it('should render _error on 404.html even if not provided in exportPathMap', async () => {
const html = await renderViaHTTP(context.port, '/404.html')
// The default error page from the test server
// contains "404", so need to be specific here
expect(html).toMatch(/404.*page.*not.*found/i)
})
it('should render _error on /404/index.html', async () => {
const html = await renderViaHTTP(context.port, '/404/index.html')
// The default error page from the test server
// contains "404", so need to be specific here
expect(html).toMatch(/404.*page.*not.*found/i)
})
it('Should serve static files', async () => {
const data = await renderViaHTTP(context.port, '/static/data/item.txt')
expect(data).toBe('item')
})
it('Should serve public files', async () => {
const html = await renderViaHTTP(context.port, '/about')
const data = await renderViaHTTP(context.port, '/about/data.txt')
expect(html).toMatch(/This is the About page foobar/)
expect(data).toBe('data')
})
it('Should render dynamic files with query', async () => {
const html = await renderViaHTTP(
context.port,
'/blog/nextjs/comment/test'
)
expect(html).toMatch(/Blog post nextjs comment test/)
})
})
}

View file

@ -1,3 +0,0 @@
module.exports = {
target: 'serverless',
}

View file

@ -1 +0,0 @@
export default () => <p>I am an about page</p>

View file

@ -1 +0,0 @@
export default () => <p>I am a home page</p>

Some files were not shown because too many files have changed in this diff Show more