feat(experimental): option to polyfill fetch
using undici
in Node.js <18 (#40318)
This PR adds a new `experimental.enableUndici` option to let the developer switch from `next-fetch` to `undici` as the underlying polyfill for `fetch` in Node.js. In the current implementation, Next.js makes sure that `fetch` is always available by using `node-fetch`. However, we do not polyfill in Node.js 18+, since those versions come with their own `fetch` implementation already, built-in. Node.js 18+ uses `undici` under the hood, so letting the developer use `undici` earlier could make the migration easier later on. Eventually, we hope to be able to stop polyfilling `fetch` in an upcoming major version of Next.js, shipping less code. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples) Co-authored-by: Balázs Orbán <info@balazsorban.com> Co-authored-by: Sukka <isukkaw@gmail.com> Co-authored-by: JJ Kasper <jj@jjsweb.site> Co-authored-by: Steven <steven@ceriously.com>
This commit is contained in:
parent
bc685142aa
commit
e0cc9cd44f
29 changed files with 493 additions and 157 deletions
|
@ -3,6 +3,7 @@ const nextJest = require('next/jest')
|
|||
const createJestConfig = nextJest()
|
||||
|
||||
// Any custom config you want to pass to Jest
|
||||
/** @type {import('jest').Config} */
|
||||
const customJestConfig = {
|
||||
testMatch: ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx'],
|
||||
setupFilesAfterEnv: ['<rootDir>/jest-setup-after-env.ts'],
|
||||
|
@ -10,6 +11,9 @@ const customJestConfig = {
|
|||
rootDir: 'test',
|
||||
modulePaths: ['<rootDir>/lib'],
|
||||
transformIgnorePatterns: ['/next[/\\\\]dist/', '/\\.next/'],
|
||||
globals: {
|
||||
AbortSignal: global.AbortSignal,
|
||||
},
|
||||
}
|
||||
|
||||
// createJestConfig is exported in this way to ensure that next/jest can load the Next.js config which is async
|
||||
|
|
|
@ -1205,6 +1205,7 @@ export default async function build(
|
|||
configFileName,
|
||||
runtimeEnvConfig,
|
||||
httpAgentOptions: config.httpAgentOptions,
|
||||
enableUndici: config.experimental.enableUndici,
|
||||
locales: config.i18n?.locales,
|
||||
defaultLocale: config.i18n?.defaultLocale,
|
||||
pageRuntime: config.experimental.runtime,
|
||||
|
@ -1352,6 +1353,7 @@ export default async function build(
|
|||
configFileName,
|
||||
runtimeEnvConfig,
|
||||
httpAgentOptions: config.httpAgentOptions,
|
||||
enableUndici: config.experimental.enableUndici,
|
||||
locales: config.i18n?.locales,
|
||||
defaultLocale: config.i18n?.defaultLocale,
|
||||
parentId: isPageStaticSpan.id,
|
||||
|
|
|
@ -41,7 +41,7 @@ import {
|
|||
LoadComponentsReturnType,
|
||||
} from '../server/load-components'
|
||||
import { trace } from '../trace'
|
||||
import { setHttpAgentOptions } from '../server/config'
|
||||
import { setHttpClientAndAgentOptions } from '../server/config'
|
||||
import { recursiveDelete } from '../lib/recursive-delete'
|
||||
import { Sema } from 'next/dist/compiled/async-sema'
|
||||
import { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
|
||||
|
@ -1169,6 +1169,7 @@ export async function isPageStatic({
|
|||
configFileName,
|
||||
runtimeEnvConfig,
|
||||
httpAgentOptions,
|
||||
enableUndici,
|
||||
locales,
|
||||
defaultLocale,
|
||||
parentId,
|
||||
|
@ -1184,6 +1185,7 @@ export async function isPageStatic({
|
|||
configFileName: string
|
||||
runtimeEnvConfig: any
|
||||
httpAgentOptions: NextConfigComplete['httpAgentOptions']
|
||||
enableUndici?: NextConfigComplete['experimental']['enableUndici']
|
||||
locales?: string[]
|
||||
defaultLocale?: string
|
||||
parentId?: any
|
||||
|
@ -1210,7 +1212,10 @@ export async function isPageStatic({
|
|||
return isPageStaticSpan
|
||||
.traceAsyncFn(async () => {
|
||||
require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig)
|
||||
setHttpAgentOptions(httpAgentOptions)
|
||||
setHttpClientAndAgentOptions({
|
||||
httpAgentOptions,
|
||||
experimental: { enableUndici },
|
||||
})
|
||||
|
||||
let componentsResult: LoadComponentsReturnType
|
||||
let prerenderRoutes: Array<string> | undefined
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
21
packages/next/compiled/undici/LICENSE
Normal file
21
packages/next/compiled/undici/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Matteo Collina and Undici contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
packages/next/compiled/undici/index.js
Normal file
2
packages/next/compiled/undici/index.js
Normal file
File diff suppressed because one or more lines are too long
1
packages/next/compiled/undici/package.json
Normal file
1
packages/next/compiled/undici/package.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"undici","main":"index.js","author":"Matteo Collina <hello@matteocollina.com>","license":"MIT"}
|
|
@ -619,6 +619,7 @@ export default async function exportApp(
|
|||
httpAgentOptions: nextConfig.httpAgentOptions,
|
||||
serverComponents: !!nextConfig.experimental.appDir,
|
||||
appPaths: options.appPaths || [],
|
||||
enableUndici: nextConfig.experimental.enableUndici,
|
||||
})
|
||||
|
||||
for (const validation of result.ampValidations || []) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { requireFontManifest } from '../server/require'
|
|||
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
|
||||
import { trace } from '../trace'
|
||||
import { isInAmpMode } from '../shared/lib/amp-mode'
|
||||
import { setHttpAgentOptions } from '../server/config'
|
||||
import { setHttpClientAndAgentOptions } from '../server/config'
|
||||
import RenderResult from '../server/render-result'
|
||||
import isError from '../lib/is-error'
|
||||
import { addRequestMeta } from '../server/request-meta'
|
||||
|
@ -70,6 +70,7 @@ interface ExportPageInput {
|
|||
httpAgentOptions: NextConfigComplete['httpAgentOptions']
|
||||
serverComponents?: boolean
|
||||
appPaths: string[]
|
||||
enableUndici: NextConfigComplete['experimental']['enableUndici']
|
||||
}
|
||||
|
||||
interface ExportPageResults {
|
||||
|
@ -119,8 +120,12 @@ export default async function exportPage({
|
|||
disableOptimizedLoading,
|
||||
httpAgentOptions,
|
||||
serverComponents,
|
||||
enableUndici,
|
||||
}: ExportPageInput): Promise<ExportPageResults> {
|
||||
setHttpAgentOptions(httpAgentOptions)
|
||||
setHttpClientAndAgentOptions({
|
||||
httpAgentOptions,
|
||||
experimental: { enableUndici },
|
||||
})
|
||||
const exportPageSpan = trace('export-page-worker', parentSpanId)
|
||||
|
||||
return exportPageSpan.traceAsyncFn(async () => {
|
||||
|
|
|
@ -264,6 +264,7 @@
|
|||
"timers-browserify": "2.0.12",
|
||||
"tty-browserify": "0.0.1",
|
||||
"ua-parser-js": "0.7.28",
|
||||
"undici": "5.10.0",
|
||||
"unistore": "3.4.1",
|
||||
"util": "0.12.4",
|
||||
"uuid": "8.3.2",
|
||||
|
|
|
@ -390,6 +390,9 @@ const configSchema = {
|
|||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableUndici: {
|
||||
type: 'boolean',
|
||||
},
|
||||
workerThreads: {
|
||||
type: 'boolean',
|
||||
},
|
||||
|
|
|
@ -146,6 +146,7 @@ export interface ExperimentalConfig {
|
|||
* [webpack/webpack#ModuleNotoundError.js#L13-L42](https://github.com/webpack/webpack/blob/2a0536cf510768111a3a6dceeb14cb79b9f59273/lib/ModuleNotFoundError.js#L13-L42)
|
||||
*/
|
||||
fallbackNodePolyfills?: false
|
||||
enableUndici?: boolean
|
||||
sri?: {
|
||||
algorithm?: SubresourceIntegrityAlgorithm
|
||||
}
|
||||
|
@ -585,6 +586,7 @@ export const defaultConfig: NextConfig = {
|
|||
amp: undefined,
|
||||
urlImports: undefined,
|
||||
modularizeImports: undefined,
|
||||
enableUndici: false,
|
||||
adjustFontFallbacks: false,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -139,6 +139,7 @@ export function loadWebpackHook() {
|
|||
'next/dist/compiled/@babel/runtime/package.json',
|
||||
],
|
||||
['node-fetch', 'next/dist/compiled/node-fetch'],
|
||||
['undici', 'next/dist/compiled/undici'],
|
||||
].map(
|
||||
// Use dynamic require.resolve to avoid statically analyzable since they're only for build time
|
||||
([request, replacement]) => [request, require.resolve(replacement)]
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
ExperimentalConfig,
|
||||
NextConfigComplete,
|
||||
validateConfig,
|
||||
NextConfig,
|
||||
} from './config-shared'
|
||||
import { loadWebpackHook } from './config-utils'
|
||||
import {
|
||||
|
@ -22,6 +23,7 @@ import {
|
|||
} 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'
|
||||
|
||||
|
@ -45,12 +47,22 @@ const experimentalWarning = execOnce(
|
|||
}
|
||||
)
|
||||
|
||||
export function setHttpAgentOptions(
|
||||
options: NextConfigComplete['httpAgentOptions']
|
||||
) {
|
||||
export function setHttpClientAndAgentOptions(options: NextConfig) {
|
||||
if (semverGte(process.version, '16.8.0')) {
|
||||
if (semverGte(process.version, '18.0.0')) {
|
||||
Log.warn(
|
||||
'`enableUndici` option is unnecessary in Node.js v18.0.0 or greater.'
|
||||
)
|
||||
}
|
||||
;(global as any).__NEXT_USE_UNDICI = options.experimental?.enableUndici
|
||||
} else if (options.experimental?.enableUndici) {
|
||||
Log.warn(
|
||||
'`enableUndici` option requires Node.js v16.8.0 or greater. Falling back to `node-fetch`'
|
||||
)
|
||||
}
|
||||
if ((global as any).__NEXT_HTTP_AGENT) {
|
||||
// We only need to assign once because we want
|
||||
// to resuse the same agent for all requests.
|
||||
// to reuse the same agent for all requests.
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -58,8 +70,9 @@ export function setHttpAgentOptions(
|
|||
throw new Error('Expected config.httpAgentOptions to be an object')
|
||||
}
|
||||
|
||||
;(global as any).__NEXT_HTTP_AGENT = new HttpAgent(options)
|
||||
;(global as any).__NEXT_HTTPS_AGENT = new HttpsAgent(options)
|
||||
;(global as any).__NEXT_HTTP_AGENT_OPTIONS = options.httpAgentOptions
|
||||
;(global as any).__NEXT_HTTP_AGENT = new HttpAgent(options.httpAgentOptions)
|
||||
;(global as any).__NEXT_HTTPS_AGENT = new HttpsAgent(options.httpAgentOptions)
|
||||
}
|
||||
|
||||
function assignDefaults(userConfig: { [key: string]: any }) {
|
||||
|
@ -545,9 +558,7 @@ function assignDefaults(userConfig: { [key: string]: any }) {
|
|||
|
||||
// TODO: Change defaultConfig type to NextConfigComplete
|
||||
// so we don't need "!" here.
|
||||
setHttpAgentOptions(
|
||||
result.httpAgentOptions || defaultConfig.httpAgentOptions!
|
||||
)
|
||||
setHttpClientAndAgentOptions(result || defaultConfig)
|
||||
|
||||
if (result.i18n) {
|
||||
const { i18n } = result
|
||||
|
@ -855,6 +866,6 @@ export default async function loadConfig(
|
|||
// reactRoot can be updated correctly even with no next.config.js
|
||||
const completeConfig = assignDefaults(defaultConfig) as NextConfigComplete
|
||||
completeConfig.configFileName = configFileName
|
||||
setHttpAgentOptions(completeConfig.httpAgentOptions)
|
||||
setHttpClientAndAgentOptions(completeConfig)
|
||||
return completeConfig
|
||||
}
|
||||
|
|
|
@ -1286,6 +1286,7 @@ export default class DevServer extends Server {
|
|||
publicRuntimeConfig,
|
||||
serverRuntimeConfig,
|
||||
httpAgentOptions,
|
||||
experimental: { enableUndici },
|
||||
} = this.nextConfig
|
||||
const { locales, defaultLocale } = this.nextConfig.i18n || {}
|
||||
|
||||
|
@ -1299,6 +1300,7 @@ export default class DevServer extends Server {
|
|||
serverRuntimeConfig,
|
||||
},
|
||||
httpAgentOptions,
|
||||
enableUndici,
|
||||
locales,
|
||||
defaultLocale,
|
||||
originalAppPath,
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
collectGenerateParams,
|
||||
} from '../../build/utils'
|
||||
import { loadComponents } from '../load-components'
|
||||
import { setHttpAgentOptions } from '../config'
|
||||
import { setHttpClientAndAgentOptions } from '../config'
|
||||
|
||||
type RuntimeConfig = any
|
||||
|
||||
|
@ -22,6 +22,7 @@ export async function loadStaticPaths({
|
|||
serverless,
|
||||
config,
|
||||
httpAgentOptions,
|
||||
enableUndici,
|
||||
locales,
|
||||
defaultLocale,
|
||||
isAppPath,
|
||||
|
@ -32,6 +33,7 @@ export async function loadStaticPaths({
|
|||
serverless: boolean
|
||||
config: RuntimeConfig
|
||||
httpAgentOptions: NextConfigComplete['httpAgentOptions']
|
||||
enableUndici: NextConfigComplete['enableUndici']
|
||||
locales?: string[]
|
||||
defaultLocale?: string
|
||||
isAppPath?: boolean
|
||||
|
@ -49,7 +51,10 @@ export async function loadStaticPaths({
|
|||
|
||||
// update work memory runtime-config
|
||||
require('../../shared/lib/runtime-config').setConfig(config)
|
||||
setHttpAgentOptions(httpAgentOptions)
|
||||
setHttpClientAndAgentOptions({
|
||||
httpAgentOptions,
|
||||
experimental: { enableUndici },
|
||||
})
|
||||
|
||||
const components = await loadComponents({
|
||||
distDir,
|
||||
|
|
|
@ -1,23 +1,56 @@
|
|||
import fetch, {
|
||||
Headers,
|
||||
Request,
|
||||
Response,
|
||||
} from 'next/dist/compiled/node-fetch'
|
||||
|
||||
// Polyfill fetch() in the Node.js environment
|
||||
|
||||
if (!global.fetch) {
|
||||
function getFetchImpl() {
|
||||
return global.__NEXT_USE_UNDICI
|
||||
? require('next/dist/compiled/undici')
|
||||
: require('next/dist/compiled/node-fetch')
|
||||
}
|
||||
// Due to limitation of global configuration, we have to do this resolution at runtime
|
||||
global.fetch = (...args) => {
|
||||
const fetchImpl = getFetchImpl()
|
||||
|
||||
if (global.__NEXT_USE_UNDICI) {
|
||||
// Undici does not support the `keepAlive` option,
|
||||
// instead we have to pass a custom dispatcher
|
||||
if (
|
||||
!global.__NEXT_HTTP_AGENT_OPTIONS?.keepAlive &&
|
||||
!global.__NEXT_UNDICI_AGENT_SET
|
||||
) {
|
||||
global.__NEXT_UNDICI_AGENT_SET = true
|
||||
fetchImpl.setGlobalDispatcher(new fetchImpl.Agent({ pipelining: 0 }))
|
||||
}
|
||||
return fetchImpl.fetch(...args)
|
||||
}
|
||||
const agent = ({ protocol }) =>
|
||||
protocol === 'http:' ? global.__NEXT_HTTP_AGENT : global.__NEXT_HTTPS_AGENT
|
||||
const fetchWithAgent = (url, opts, ...rest) => {
|
||||
if (!opts) {
|
||||
opts = { agent }
|
||||
} else if (!opts.agent) {
|
||||
opts.agent = agent
|
||||
protocol === 'http:'
|
||||
? global.__NEXT_HTTP_AGENT
|
||||
: global.__NEXT_HTTPS_AGENT
|
||||
|
||||
if (!args[1]) {
|
||||
args[1] = { agent }
|
||||
} else if (!args[1].agent) {
|
||||
args[1].agent = agent
|
||||
}
|
||||
return fetch(url, opts, ...rest)
|
||||
|
||||
return fetchImpl(...args)
|
||||
}
|
||||
global.fetch = fetchWithAgent
|
||||
global.Headers = Headers
|
||||
global.Request = Request
|
||||
global.Response = Response
|
||||
|
||||
Object.defineProperties(global, {
|
||||
Headers: {
|
||||
get() {
|
||||
return getFetchImpl().Headers
|
||||
},
|
||||
},
|
||||
Request: {
|
||||
get() {
|
||||
return getFetchImpl().Request
|
||||
},
|
||||
},
|
||||
Response: {
|
||||
get() {
|
||||
return getFetchImpl().Response
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -86,6 +86,8 @@ const externals = {
|
|||
'next/dist/build/webpack/plugins/terser-webpack-plugin',
|
||||
|
||||
// TODO: Add @swc/helpers to externals once @vercel/ncc switch to swc-loader
|
||||
|
||||
undici: 'undici',
|
||||
}
|
||||
// eslint-disable-next-line camelcase
|
||||
externals['node-html-parser'] = 'next/dist/compiled/node-html-parser'
|
||||
|
@ -167,6 +169,14 @@ export async function ncc_node_fetch(task, opts) {
|
|||
.target('compiled/node-fetch')
|
||||
}
|
||||
|
||||
externals['undici'] = 'next/dist/compiled/undici'
|
||||
export async function ncc_undici(task, opts) {
|
||||
await task
|
||||
.source(opts.src || relative(__dirname, require.resolve('undici')))
|
||||
.ncc({ packageName: 'undici', externals })
|
||||
.target('compiled/undici')
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function compile_config_schema(task, opts) {
|
||||
const { configSchema } = require('./dist/server/config-schema')
|
||||
|
@ -1818,6 +1828,7 @@ export async function ncc(task, opts) {
|
|||
'ncc_get_orientation',
|
||||
'ncc_hapi_accept',
|
||||
'ncc_node_fetch',
|
||||
'ncc_undici',
|
||||
'ncc_acorn',
|
||||
'ncc_amphtml_validator',
|
||||
'ncc_arg',
|
||||
|
|
2
packages/next/types/misc.d.ts
vendored
2
packages/next/types/misc.d.ts
vendored
|
@ -28,6 +28,8 @@ declare module 'next/dist/compiled/node-fetch' {
|
|||
export * from 'node-fetch'
|
||||
}
|
||||
|
||||
declare module 'next/dist/compiled/undici' {}
|
||||
|
||||
declare module 'next/dist/compiled/jest-worker' {
|
||||
export * from 'jest-worker'
|
||||
}
|
||||
|
|
177
pnpm-lock.yaml
177
pnpm-lock.yaml
|
@ -601,6 +601,7 @@ importers:
|
|||
timers-browserify: 2.0.12
|
||||
tty-browserify: 0.0.1
|
||||
ua-parser-js: 0.7.28
|
||||
undici: 5.10.0
|
||||
unistore: 3.4.1
|
||||
use-sync-external-store: 1.2.0
|
||||
util: 0.12.4
|
||||
|
@ -789,6 +790,7 @@ importers:
|
|||
timers-browserify: 2.0.12
|
||||
tty-browserify: 0.0.1
|
||||
ua-parser-js: 0.7.28
|
||||
undici: 5.10.0
|
||||
unistore: 3.4.1
|
||||
util: 0.12.4
|
||||
uuid: 8.3.2
|
||||
|
@ -3896,7 +3898,7 @@ packages:
|
|||
integrity: sha512-OoUX2+yhtBH6FGtPoI3gP0YQfjDyLWUzifuvZ3cZwF8AF8Gs7DWM9Lg8/9OfhP4I9ZL8DAuK+hSwxOKdvOLXew==,
|
||||
}
|
||||
dependencies:
|
||||
'@edge-runtime/vm': 1.1.0-beta.33
|
||||
'@edge-runtime/vm': 1.1.0-beta.34
|
||||
'@jest/environment': 28.1.3
|
||||
'@jest/fake-timers': 28.1.3
|
||||
'@jest/types': 28.1.3
|
||||
|
@ -3911,15 +3913,6 @@ packages:
|
|||
}
|
||||
dev: true
|
||||
|
||||
/@edge-runtime/vm/1.1.0-beta.33:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Aifd/elNDeI01oEzUnCF5URPtMgBIVDhnuy/F6SgS2OMJvzts/U5Rl2hxYliViU2OpC8ZkM/XT/t+Q7rQPJsgw==,
|
||||
}
|
||||
dependencies:
|
||||
'@edge-runtime/primitives': 1.1.0-beta.34
|
||||
dev: true
|
||||
|
||||
/@edge-runtime/vm/1.1.0-beta.34:
|
||||
resolution:
|
||||
{
|
||||
|
@ -4346,13 +4339,6 @@ packages:
|
|||
purgecss: 1.4.2
|
||||
dev: true
|
||||
|
||||
/@gar/promisify/1.1.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==,
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@grpc/grpc-js/0.8.1:
|
||||
resolution:
|
||||
{
|
||||
|
@ -5280,7 +5266,7 @@ packages:
|
|||
fs-extra: 9.1.0
|
||||
npm-package-arg: 8.1.0
|
||||
npmlog: 4.1.2
|
||||
signal-exit: 3.0.7
|
||||
signal-exit: 3.0.3
|
||||
write-pkg: 4.0.0
|
||||
dev: true
|
||||
|
||||
|
@ -5789,16 +5775,6 @@ packages:
|
|||
}
|
||||
dev: true
|
||||
|
||||
/@npmcli/fs/1.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==,
|
||||
}
|
||||
dependencies:
|
||||
'@gar/promisify': 1.1.3
|
||||
semver: 7.3.7
|
||||
dev: true
|
||||
|
||||
/@npmcli/git/2.0.4:
|
||||
resolution:
|
||||
{
|
||||
|
@ -8267,7 +8243,7 @@ packages:
|
|||
dependencies:
|
||||
acorn: 7.4.1
|
||||
|
||||
/acorn-jsx/5.3.1_acorn@8.8.0:
|
||||
/acorn-jsx/5.3.1_acorn@8.6.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==,
|
||||
|
@ -8275,7 +8251,7 @@ packages:
|
|||
peerDependencies:
|
||||
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
acorn: 8.6.0
|
||||
dev: true
|
||||
|
||||
/acorn-walk/7.1.1:
|
||||
|
@ -9988,14 +9964,13 @@ packages:
|
|||
engines: { node: '>= 0.8' }
|
||||
dev: true
|
||||
|
||||
/cacache/15.3.0:
|
||||
/cacache/15.0.5:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==,
|
||||
integrity: sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==,
|
||||
}
|
||||
engines: { node: '>= 10' }
|
||||
dependencies:
|
||||
'@npmcli/fs': 1.1.1
|
||||
'@npmcli/move-file': 1.0.1
|
||||
chownr: 2.0.0
|
||||
fs-minipass: 2.1.0
|
||||
|
@ -11074,7 +11049,10 @@ packages:
|
|||
dev: true
|
||||
|
||||
/concat-map/0.0.1:
|
||||
resolution: { integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= }
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==,
|
||||
}
|
||||
|
||||
/concat-stream/1.6.2:
|
||||
resolution:
|
||||
|
@ -13246,10 +13224,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/err-code/1.1.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-CJAN+O0/yA1CKfRn9SXOGctSpEM7DCon/r/5r2eXFMY2zCCJBasFhcM5I+1kh3Ap11FsQCX+vGHceNPvpWKhoA==,
|
||||
}
|
||||
resolution: { integrity: sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA= }
|
||||
dev: true
|
||||
|
||||
/err-code/2.0.3:
|
||||
|
@ -14127,7 +14102,7 @@ packages:
|
|||
merge-stream: 2.0.0
|
||||
npm-run-path: 4.0.1
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
signal-exit: 3.0.3
|
||||
strip-final-newline: 2.0.0
|
||||
dev: true
|
||||
|
||||
|
@ -15112,10 +15087,10 @@ packages:
|
|||
console-control-strings: 1.1.0
|
||||
has-unicode: 2.0.1
|
||||
object-assign: 4.1.1
|
||||
signal-exit: 3.0.7
|
||||
signal-exit: 3.0.3
|
||||
string-width: 1.0.2
|
||||
strip-ansi: 3.0.1
|
||||
wide-align: 1.1.5
|
||||
wide-align: 1.1.3
|
||||
dev: true
|
||||
|
||||
/generic-names/2.0.1:
|
||||
|
@ -16702,10 +16677,7 @@ packages:
|
|||
engines: { node: '>=0.8.19' }
|
||||
|
||||
/indent-string/2.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==,
|
||||
}
|
||||
resolution: { integrity: sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
dependencies:
|
||||
repeating: 2.0.1
|
||||
|
@ -18747,7 +18719,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
abab: 2.0.5
|
||||
acorn: 8.8.0
|
||||
acorn: 8.6.0
|
||||
acorn-globals: 6.0.0
|
||||
cssom: 0.4.4
|
||||
cssstyle: 2.3.0
|
||||
|
@ -19195,10 +19167,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/levn/0.3.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==,
|
||||
}
|
||||
resolution: { integrity: sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= }
|
||||
engines: { node: '>= 0.8.0' }
|
||||
dependencies:
|
||||
prelude-ls: 1.1.2
|
||||
|
@ -19816,7 +19785,7 @@ packages:
|
|||
engines: { node: '>=0.10.0' }
|
||||
dependencies:
|
||||
currently-unhandled: 0.4.1
|
||||
signal-exit: 3.0.7
|
||||
signal-exit: 3.0.3
|
||||
dev: true
|
||||
|
||||
/lower-case-first/1.0.2:
|
||||
|
@ -19939,7 +19908,7 @@ packages:
|
|||
engines: { node: '>= 10' }
|
||||
dependencies:
|
||||
agentkeepalive: 4.1.4
|
||||
cacache: 15.3.0
|
||||
cacache: 15.0.5
|
||||
http-cache-semantics: 4.1.0
|
||||
http-proxy-agent: 4.0.1
|
||||
https-proxy-agent: 5.0.0
|
||||
|
@ -20198,10 +20167,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/meow/3.7.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==,
|
||||
}
|
||||
resolution: { integrity: sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
dependencies:
|
||||
camelcase-keys: 2.1.0
|
||||
|
@ -20423,8 +20389,8 @@ packages:
|
|||
integrity: sha512-NQuiYA0lw+eFDtSG4+c7ao3RG9dM4P0Kx/sn8OLyPhxtIc6k+9n14k5VfLxRKfAxYRTo8c5PLZPaRNmslGWxJw==,
|
||||
}
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
acorn-jsx: 5.3.1_acorn@8.8.0
|
||||
acorn: 8.6.0
|
||||
acorn-jsx: 5.3.1_acorn@8.6.0
|
||||
micromark: 2.11.4
|
||||
micromark-extension-mdx-expression: 0.3.2
|
||||
micromark-extension-mdx-jsx: 0.3.3
|
||||
|
@ -22222,7 +22188,7 @@ packages:
|
|||
'@npmcli/installed-package-contents': 1.0.7
|
||||
'@npmcli/promise-spawn': 1.3.2
|
||||
'@npmcli/run-script': 1.8.3
|
||||
cacache: 15.3.0
|
||||
cacache: 15.0.5
|
||||
chownr: 2.0.0
|
||||
fs-minipass: 2.1.0
|
||||
infer-owner: 1.0.4
|
||||
|
@ -23961,10 +23927,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/prelude-ls/1.1.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==,
|
||||
}
|
||||
resolution: { integrity: sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= }
|
||||
engines: { node: '>= 0.8.0' }
|
||||
dev: true
|
||||
|
||||
|
@ -24111,10 +24074,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/process-nextick-args/1.0.7:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==,
|
||||
}
|
||||
resolution: { integrity: sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= }
|
||||
dev: true
|
||||
|
||||
/process-nextick-args/2.0.1:
|
||||
|
@ -24163,10 +24123,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/promise-retry/1.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-StEy2osPr28o17bIW776GtwO6+Q+M9zPiZkYfosciUUMYqjhU/ffwRAH0zN2+uvGyUsn8/YICIHRzLbPacpZGw==,
|
||||
}
|
||||
resolution: { integrity: sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0= }
|
||||
engines: { node: '>=0.12' }
|
||||
dependencies:
|
||||
err-code: 1.1.2
|
||||
|
@ -24773,10 +24730,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/read-pkg-up/1.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==,
|
||||
}
|
||||
resolution: { integrity: sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
dependencies:
|
||||
find-up: 1.1.2
|
||||
|
@ -24792,10 +24746,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/read-pkg-up/3.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==,
|
||||
}
|
||||
resolution: { integrity: sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= }
|
||||
engines: { node: '>=4' }
|
||||
dependencies:
|
||||
find-up: 2.1.0
|
||||
|
@ -24872,10 +24823,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/readable-stream/1.1.14:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==,
|
||||
}
|
||||
resolution: { integrity: sha1-fPTFTvZI44EwhMY23SB54WbAgdk= }
|
||||
dependencies:
|
||||
core-util-is: 1.0.2
|
||||
inherits: 2.0.4
|
||||
|
@ -24990,10 +24938,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/redent/1.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==,
|
||||
}
|
||||
resolution: { integrity: sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
dependencies:
|
||||
indent-string: 2.1.0
|
||||
|
@ -25453,7 +25398,10 @@ packages:
|
|||
dev: true
|
||||
|
||||
/replace-ext/1.0.0:
|
||||
resolution: { integrity: sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= }
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-vuNYXC7gG7IeVNBC1xUllqCcZKRbJoSPOBhnTEcAIiKCsbuef6zO3F0Rve3isPMMoNoQRWjQwbAgAjHUHniyEA==,
|
||||
}
|
||||
engines: { node: '>= 0.10' }
|
||||
dev: true
|
||||
|
||||
|
@ -25501,10 +25449,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/require-directory/2.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==,
|
||||
}
|
||||
resolution: { integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
|
@ -25647,7 +25592,7 @@ packages:
|
|||
engines: { node: '>=4' }
|
||||
dependencies:
|
||||
onetime: 2.0.1
|
||||
signal-exit: 3.0.7
|
||||
signal-exit: 3.0.3
|
||||
dev: true
|
||||
|
||||
/restore-cursor/3.1.0:
|
||||
|
@ -25658,7 +25603,7 @@ packages:
|
|||
engines: { node: '>=8' }
|
||||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
signal-exit: 3.0.3
|
||||
|
||||
/restore-cursor/4.0.0:
|
||||
resolution:
|
||||
|
@ -25668,7 +25613,7 @@ packages:
|
|||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
signal-exit: 3.0.3
|
||||
dev: true
|
||||
|
||||
/ret/0.1.15:
|
||||
|
@ -26422,6 +26367,7 @@ packages:
|
|||
{
|
||||
integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==,
|
||||
}
|
||||
dev: true
|
||||
|
||||
/simple-swizzle/0.2.2:
|
||||
resolution:
|
||||
|
@ -26474,10 +26420,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/slice-ansi/0.0.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw==,
|
||||
}
|
||||
resolution: { integrity: sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
|
@ -28121,10 +28064,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/trim-newlines/1.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==,
|
||||
}
|
||||
resolution: { integrity: sha1-WIeWa7WCpFA6QetST301ARgVphM= }
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
|
@ -28331,10 +28271,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/type-check/0.3.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==,
|
||||
}
|
||||
resolution: { integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= }
|
||||
engines: { node: '>= 0.8.0' }
|
||||
dependencies:
|
||||
prelude-ls: 1.1.2
|
||||
|
@ -28535,6 +28472,14 @@ packages:
|
|||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
/undici/5.10.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==,
|
||||
}
|
||||
engines: { node: '>=12.18' }
|
||||
dev: true
|
||||
|
||||
/unfetch/4.1.0:
|
||||
resolution:
|
||||
{
|
||||
|
@ -29260,7 +29205,10 @@ packages:
|
|||
dev: true
|
||||
|
||||
/verror/1.10.0:
|
||||
resolution: { integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= }
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==,
|
||||
}
|
||||
engines: { '0': node >=0.6.0 }
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
|
@ -29761,13 +29709,13 @@ packages:
|
|||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
/wide-align/1.1.5:
|
||||
/wide-align/1.1.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==,
|
||||
integrity: sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==,
|
||||
}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
string-width: 2.1.1
|
||||
dev: true
|
||||
|
||||
/widest-line/3.1.0:
|
||||
|
@ -29792,10 +29740,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/wrap-ansi/3.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==,
|
||||
}
|
||||
resolution: { integrity: sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= }
|
||||
engines: { node: '>=4' }
|
||||
dependencies:
|
||||
string-width: 2.1.1
|
||||
|
@ -29861,7 +29806,7 @@ packages:
|
|||
dependencies:
|
||||
imurmurhash: 0.1.4
|
||||
is-typedarray: 1.0.0
|
||||
signal-exit: 3.0.3
|
||||
signal-exit: 3.0.7
|
||||
typedarray-to-buffer: 3.1.5
|
||||
dev: true
|
||||
|
||||
|
|
129
test/e2e/undici-fetch/index.test.ts
Normal file
129
test/e2e/undici-fetch/index.test.ts
Normal file
|
@ -0,0 +1,129 @@
|
|||
import { createNext } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { fetchViaHTTP } from 'next-test-utils'
|
||||
import semver from 'semver'
|
||||
|
||||
if (
|
||||
semver.lt(process.version, '16.8.0') ||
|
||||
semver.gte(process.version, '18.0.0')
|
||||
) {
|
||||
it('skipping for Node.js versions <16.8.0 and >18.0.0', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
} else {
|
||||
describe('undici fetch', () => {
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: {
|
||||
'pages/api/globalFetch.js': `
|
||||
import { ReadableStream } from 'node:stream/web';
|
||||
export default async function globalFetch(req, res) {
|
||||
try {
|
||||
const response = await fetch('https://example.vercel.sh')
|
||||
res.json({ value: response.body instanceof ReadableStream })
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.send(error);
|
||||
}
|
||||
}
|
||||
`,
|
||||
'pages/api/globalHeaders.js': `
|
||||
export default async function globalHeaders(req, res) {
|
||||
res.json({
|
||||
value: (new Headers())[Symbol.iterator].name === 'entries'
|
||||
})
|
||||
}
|
||||
`,
|
||||
'pages/api/globalRequest.js': `
|
||||
export default async function globalRequest(req, res) {
|
||||
res.json({
|
||||
value: (new Request('https://example.vercel.sh')).headers[Symbol.iterator].name === 'entries'
|
||||
})
|
||||
}
|
||||
`,
|
||||
'pages/api/globalResponse.js': `
|
||||
export default async function globalResponse(req, res) {
|
||||
res.json({
|
||||
value: (new Response()).headers[Symbol.iterator].name === 'entries'
|
||||
})
|
||||
}
|
||||
`,
|
||||
},
|
||||
dependencies: {},
|
||||
nextConfig: {
|
||||
experimental: {
|
||||
enableUndici: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
describe('undici', () => {
|
||||
it('global fetch should return true when undici is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalFetch')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(true)
|
||||
})
|
||||
|
||||
it('global Headers should return true when undici is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalHeaders')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(true)
|
||||
})
|
||||
|
||||
it('global Request should return true when undici is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalRequest')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(true)
|
||||
})
|
||||
|
||||
it('global Response should return true when undici is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalResponse')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('node-fetch', () => {
|
||||
beforeAll(async () => {
|
||||
await next.stop()
|
||||
await next.patchFile(
|
||||
'next.config.js',
|
||||
`module.exports = ${JSON.stringify({
|
||||
experimental: {
|
||||
enableUndici: false,
|
||||
},
|
||||
})}`
|
||||
)
|
||||
await next.start()
|
||||
})
|
||||
|
||||
it('global fetch should return false when node-fetch is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalFetch')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(false)
|
||||
})
|
||||
|
||||
it('global Headers should return false when node-fetch is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalHeaders')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(false)
|
||||
})
|
||||
|
||||
it('global Request should return false when node-fetch is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalRequest')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(false)
|
||||
})
|
||||
|
||||
it('global Response should return false when node-fetch is used', async () => {
|
||||
const result = await fetchViaHTTP(next.url, '/api/globalResponse')
|
||||
const data = await result.json()
|
||||
expect(data.value).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
5
test/integration/undici-keep-alive/next.config.js
Normal file
5
test/integration/undici-keep-alive/next.config.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
experimental: {
|
||||
enableUndici: true,
|
||||
},
|
||||
}
|
5
test/integration/undici-keep-alive/pages/api/json.js
Normal file
5
test/integration/undici-keep-alive/pages/api/json.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default async function handler(_req, res) {
|
||||
const fetchRes = await fetch('http://localhost:44001')
|
||||
const props = await fetchRes.json()
|
||||
res.json(props)
|
||||
}
|
23
test/integration/undici-keep-alive/pages/blog/[slug].js
Normal file
23
test/integration/undici-keep-alive/pages/blog/[slug].js
Normal file
|
@ -0,0 +1,23 @@
|
|||
export default function Blog(props) {
|
||||
return <pre id="props">{JSON.stringify(props)}</pre>
|
||||
}
|
||||
|
||||
export async function getStaticProps({ params: { slug } }) {
|
||||
return { props: { slug } }
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const res = await fetch('http://localhost:44001')
|
||||
const obj = await res.json()
|
||||
if (obj.connection === 'keep-alive') {
|
||||
return {
|
||||
paths: [{ params: { slug: 'first' } }],
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
paths: [],
|
||||
fallback: false,
|
||||
}
|
||||
}
|
9
test/integration/undici-keep-alive/pages/ssg.js
Normal file
9
test/integration/undici-keep-alive/pages/ssg.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
export default function SSG(props) {
|
||||
return <pre id="props">{JSON.stringify(props)}</pre>
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const res = await fetch('http://localhost:44001')
|
||||
const props = await res.json()
|
||||
return { props }
|
||||
}
|
9
test/integration/undici-keep-alive/pages/ssr.js
Normal file
9
test/integration/undici-keep-alive/pages/ssr.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
export default function SSR(props) {
|
||||
return <pre id="props">{JSON.stringify(props)}</pre>
|
||||
}
|
||||
|
||||
export async function getServerSideProps() {
|
||||
const res = await fetch('http://localhost:44001')
|
||||
const props = await res.json()
|
||||
return { props }
|
||||
}
|
99
test/integration/undici-keep-alive/test/index.test.js
Normal file
99
test/integration/undici-keep-alive/test/index.test.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import { join } from 'path'
|
||||
import { createServer } from 'http'
|
||||
import {
|
||||
fetchViaHTTP,
|
||||
nextBuild,
|
||||
findPort,
|
||||
nextStart,
|
||||
launchApp,
|
||||
killApp,
|
||||
} from 'next-test-utils'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
const appDir = join(__dirname, '../')
|
||||
|
||||
let appPort
|
||||
let app
|
||||
let mockServer
|
||||
|
||||
describe('undici-keep-alive', () => {
|
||||
describe('dev', () => {
|
||||
beforeAll(async () => {
|
||||
mockServer = createServer((req, res) => {
|
||||
// we can test request headers by sending them
|
||||
// back with the response
|
||||
const { connection } = req.headers
|
||||
res.end(JSON.stringify({ connection }))
|
||||
})
|
||||
mockServer.listen(44001)
|
||||
appPort = await findPort()
|
||||
app = await launchApp(appDir, appPort)
|
||||
})
|
||||
afterAll(async () => {
|
||||
await killApp(app)
|
||||
mockServer.close()
|
||||
})
|
||||
|
||||
runTests()
|
||||
})
|
||||
|
||||
describe('production', () => {
|
||||
beforeAll(async () => {
|
||||
mockServer = createServer((req, res) => {
|
||||
// we can test request headers by sending them
|
||||
// back with the response
|
||||
const { connection } = req.headers
|
||||
res.end(JSON.stringify({ connection }))
|
||||
})
|
||||
mockServer.listen(44001)
|
||||
const { stdout, stderr } = await nextBuild(appDir, [], {
|
||||
stdout: true,
|
||||
stderr: true,
|
||||
})
|
||||
if (stdout) console.log(stdout)
|
||||
if (stderr) console.error(stderr)
|
||||
appPort = await findPort()
|
||||
app = await nextStart(appDir, appPort)
|
||||
})
|
||||
afterAll(async () => {
|
||||
await killApp(app)
|
||||
mockServer.close()
|
||||
})
|
||||
|
||||
runTests()
|
||||
})
|
||||
|
||||
function runTests() {
|
||||
it('should send keep-alive for json API', async () => {
|
||||
const res = await fetchViaHTTP(appPort, '/api/json')
|
||||
const obj = await res.json()
|
||||
expect(obj).toEqual({ connection: 'keep-alive' })
|
||||
})
|
||||
|
||||
it('should send keep-alive for getStaticProps', async () => {
|
||||
const browser = await webdriver(appPort, '/ssg')
|
||||
const props = await browser.elementById('props').text()
|
||||
const obj = JSON.parse(props)
|
||||
expect(obj).toEqual({ connection: 'keep-alive' })
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should send keep-alive for getStaticPaths', async () => {
|
||||
const browser = await webdriver(appPort, '/blog/first')
|
||||
const props = await browser.elementById('props').text()
|
||||
const obj = JSON.parse(props)
|
||||
expect(obj).toEqual({ slug: 'first' })
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should send keep-alive for getServerSideProps', async () => {
|
||||
const browser = await webdriver(appPort, '/ssr')
|
||||
const props = await browser.elementById('props').text()
|
||||
const obj = JSON.parse(props)
|
||||
expect(obj).toEqual({ connection: 'keep-alive' })
|
||||
await browser.close()
|
||||
})
|
||||
}
|
||||
})
|
Loading…
Reference in a new issue