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:
parent
ebae05eeed
commit
9c5bb5bfe9
224 changed files with 309 additions and 5884 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]]
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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] =
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
module.exports = {
|
||||
// target: 'experimental-serverless-trace',
|
||||
webpack: (config) => {
|
||||
config.experiments = config.experiments || {}
|
||||
config.experiments.topLevelAwait = true
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
module.exports = {
|
||||
// target: 'serverless',
|
||||
exportPathMap() {
|
||||
return {
|
||||
'/': { page: '/hello', query: { first: 'second' } },
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
target: 'serverless',
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// eslint-disable-next-line
|
||||
export default () => something.error
|
|
@ -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')
|
||||
})
|
||||
})
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
target: 'serverless',
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { useRouter } from 'next/router'
|
||||
|
||||
export default () => {
|
||||
const { query } = useRouter()
|
||||
|
||||
return <p>post: {query.post}</p>
|
||||
}
|
|
@ -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()
|
||||
})
|
||||
})
|
|
@ -1,4 +1,3 @@
|
|||
module.exports = {
|
||||
// target: 'experimental-serverless-trace',
|
||||
basePath: '/docs',
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
module.exports = {
|
||||
target: 'server',
|
||||
pageExtensions: ['page.js'],
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
module.exports = {
|
||||
// target: 'serverless',
|
||||
async rewrites() {
|
||||
// no-rewrites comment
|
||||
return {
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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'] })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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 })
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
target: 'serverless',
|
||||
}
|
|
@ -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>
|
|
@ -1,2 +0,0 @@
|
|||
export default () => <p>Simple hybrid amp/non-amp page</p>
|
||||
export const config = { amp: 'hybrid' }
|
|
@ -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>
|
|
@ -1,2 +0,0 @@
|
|||
export default () => <p>I am an AMP only page</p>
|
||||
export const config = { amp: true }
|
|
@ -1,3 +0,0 @@
|
|||
export const config = { amp: 'hybrid' }
|
||||
|
||||
export default () => <p>I'm an AMP page</p>
|
|
@ -1,3 +0,0 @@
|
|||
export default function Docs(props) {
|
||||
return <div>Hello again 👋</div>
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export default function Index(props) {
|
||||
return <div>Hello 👋</div>
|
||||
}
|
|
@ -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')
|
||||
})
|
||||
})
|
|
@ -1,8 +0,0 @@
|
|||
module.exports = {
|
||||
target: 'serverless',
|
||||
exportPathMap() {
|
||||
return {
|
||||
'/regression/jeff-is-cool': { page: '/regression/[slug]' },
|
||||
}
|
||||
},
|
||||
}
|
|
@ -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
|
|
@ -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()
|
||||
}
|
||||
})
|
||||
})
|
|
@ -1 +0,0 @@
|
|||
.next-dev
|
|
@ -1 +0,0 @@
|
|||
export default () => <p>Welcome to dynamic imports.</p>
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
export default (req, res) => {
|
||||
res.send('Hello World')
|
||||
}
|
|
@ -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
|
|
@ -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>
|
||||
)
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
)
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
const Page = ({ query }) => <div>{`Query is: ${query}`}</div>
|
||||
|
||||
Page.getInitialProps = ({ query }) => {
|
||||
return { query: JSON.stringify(query) }
|
||||
}
|
||||
|
||||
export default Page
|
|
@ -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>
|
||||
)
|
|
@ -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>
|
||||
)
|
|
@ -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>
|
||||
)
|
|
@ -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
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
data
|
|
@ -1 +0,0 @@
|
|||
item
|
|
@ -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'
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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' })
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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'
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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)
|
||||
})
|
|
@ -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/)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
target: 'serverless',
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export default () => <p>I am an about page</p>
|
|
@ -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
Loading…
Reference in a new issue