diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 88b3f63263b06c5..6d6918437e6bf52 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -95,10 +95,10 @@ import { parseNextUrl } from '../shared/lib/router/utils/parse-next-url' import isError from '../lib/is-error' import { getMiddlewareInfo } from './require' import { MIDDLEWARE_ROUTE } from '../lib/constants' +import { NextResponse } from './web/spec-extension/response' import { run } from './web/sandbox' import { addRequestMeta, getRequestMeta } from './request-meta' import { toNodeHeaders } from './web/utils' -import { relativizeURL } from '../shared/lib/router/utils/relativize-url' const getCustomRouteMatcher = pathMatch(true) @@ -384,13 +384,7 @@ export default class Server { parsedUrl.query = parseQs(parsedUrl.query) } - // When there are hostname and port we build an absolute URL - const initUrl = - this.hostname && this.port - ? `http://${this.hostname}:${this.port}${req.url}` - : req.url - - addRequestMeta(req, '__NEXT_INIT_URL', initUrl) + addRequestMeta(req, '__NEXT_INIT_URL', req.url) addRequestMeta(req, '__NEXT_INIT_QUERY', { ...parsedUrl.query }) const url = parseNextUrl({ @@ -673,14 +667,6 @@ export default class Server { }): Promise { this.middlewareBetaWarning() - // For middleware to "fetch" we must always provide an absolute URL - const url = getRequestMeta(params.request, '__NEXT_INIT_URL')! - if (!url.startsWith('http')) { - throw new Error( - 'To use middleware you must provide a `hostname` and `port` to the Next.js Server' - ) - } - const page: { name?: string; params?: { [key: string]: string } } = {} if (await this.hasPage(params.parsedUrl.pathname)) { page.name = params.parsedUrl.pathname @@ -695,6 +681,8 @@ export default class Server { } } + const subreq = params.request.headers[`x-middleware-subrequest`] + const subrequests = typeof subreq === 'string' ? subreq.split(':') : [] const allHeaders = new Headers() let result: FetchEventResult | null = null @@ -714,6 +702,14 @@ export default class Server { serverless: this._isLikeServerless, }) + if (subrequests.includes(middlewareInfo.name)) { + result = { + response: NextResponse.next(), + waitUntil: Promise.resolve(), + } + continue + } + result = await run({ name: middlewareInfo.name, paths: middlewareInfo.paths, @@ -725,7 +721,7 @@ export default class Server { i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash, }, - url: url, + url: getRequestMeta(params.request, '__NEXT_INIT_URL')!, page: page, }, useCache: !this.nextConfig.experimental.concurrentFeatures, @@ -1185,13 +1181,9 @@ export default class Server { type: 'route', name: 'middleware catchall', fn: async (req, res, _params, parsed) => { - if (!this.middleware?.length) { - return { finished: false } - } - - const initUrl = getRequestMeta(req, '__NEXT_INIT_URL')! + const fullUrl = getRequestMeta(req, '__NEXT_INIT_URL') const parsedUrl = parseNextUrl({ - url: initUrl, + url: fullUrl, headers: req.headers, nextConfig: { basePath: this.nextConfig.basePath, @@ -1230,18 +1222,6 @@ export default class Server { return { finished: true } } - if (result.response.headers.has('x-middleware-rewrite')) { - const value = result.response.headers.get('x-middleware-rewrite')! - const rel = relativizeURL(value, initUrl) - result.response.headers.set('x-middleware-rewrite', rel) - } - - if (result.response.headers.has('Location')) { - const value = result.response.headers.get('Location')! - const rel = relativizeURL(value, initUrl) - result.response.headers.set('Location', rel) - } - if ( !result.response.headers.has('x-middleware-rewrite') && !result.response.headers.has('x-middleware-next') && diff --git a/packages/next/server/web/adapter.ts b/packages/next/server/web/adapter.ts index ff7f3559453c35c..33325dc873776e5 100644 --- a/packages/next/server/web/adapter.ts +++ b/packages/next/server/web/adapter.ts @@ -1,9 +1,8 @@ import type { NextMiddleware, RequestData, FetchEventResult } from './types' -import type { RequestInit } from './spec-extension/request' import { DeprecationError } from './error' import { fromNodeHeaders } from './utils' import { NextFetchEvent } from './spec-extension/fetch-event' -import { NextRequest } from './spec-extension/request' +import { NextRequest, RequestInit } from './spec-extension/request' import { NextResponse } from './spec-extension/response' import { waitUntilSymbol } from './spec-compliant/fetch-event' @@ -12,9 +11,13 @@ export async function adapter(params: { page: string request: RequestData }): Promise { + const url = params.request.url.startsWith('/') + ? `https://${params.request.headers.host}${params.request.url}` + : params.request.url + const request = new NextRequestHint({ page: params.page, - input: params.request.url, + input: url, init: { geo: params.request.geo, headers: fromNodeHeaders(params.request.headers), diff --git a/packages/next/server/web/next-url.ts b/packages/next/server/web/next-url.ts index e1287b45dc2948e..cba854d0d3079ef 100644 --- a/packages/next/server/web/next-url.ts +++ b/packages/next/server/web/next-url.ts @@ -1,77 +1,66 @@ import type { PathLocale } from '../../shared/lib/i18n/normalize-locale-path' import type { DomainLocale, I18NConfig } from '../config-shared' import { getLocaleMetadata } from '../../shared/lib/i18n/get-locale-metadata' -import { replaceBasePath } from '../router' import cookie from 'next/dist/compiled/cookie' +import { replaceBasePath } from '../router' + +/** + * TODO + * + * - Add comments to the URLNext API. + * - Move internals to be using symbols for its shape. + * - Make sure logging does not show any implementation details. + * - Include in the event payload the nextJS configuration + */ interface Options { - base?: string | URL basePath?: string headers?: { [key: string]: string | string[] | undefined } i18n?: I18NConfig | null trailingSlash?: boolean } -const Internal = Symbol('NextURLInternal') +const REGEX_LOCALHOST_HOSTNAME = + /(?!^https?:\/\/)(127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}|::1)/ export class NextURL extends URL { - [Internal]: { - url: URL - options: Options - basePath: string - locale?: { - defaultLocale: string - domain?: DomainLocale - locale: string - path: PathLocale - redirect?: string - trailingSlash?: boolean - } + private _basePath: string + private _locale?: { + defaultLocale: string + domain?: DomainLocale + locale: string + path: PathLocale + redirect?: string + trailingSlash?: boolean + } + private _options: Options + private _url: URL + + constructor(input: string, options: Options = {}) { + const url = createWHATWGURL(input) + super(url) + this._options = options + this._basePath = '' + this._url = url + this.analyzeUrl() } - constructor(input: string | URL, base?: string | URL, opts?: Options) - constructor(input: string | URL, opts?: Options) - constructor( - input: string | URL, - baseOrOpts?: string | URL | Options, - opts?: Options - ) { - super('http://127.0.0.1') // This works as a placeholder - - let base: undefined | string | URL - let options: Options - - if (baseOrOpts instanceof URL || typeof baseOrOpts === 'string') { - base = baseOrOpts - options = opts || {} - } else { - options = opts || baseOrOpts || {} - } - - this[Internal] = { - url: parseURL(input, base ?? options.base), - options: options, - basePath: '', - } - - this.analyzeUrl() + get absolute() { + return this._url.hostname !== 'localhost' } - private analyzeUrl() { - const { headers = {}, basePath, i18n } = this[Internal].options + analyzeUrl() { + const { headers = {}, basePath, i18n } = this._options - if (basePath && this[Internal].url.pathname.startsWith(basePath)) { - this[Internal].url.pathname = replaceBasePath( - this[Internal].url.pathname, - basePath - ) - this[Internal].basePath = basePath + if (basePath && this._url.pathname.startsWith(basePath)) { + this._url.pathname = replaceBasePath(this._url.pathname, basePath) + this._basePath = basePath } else { - this[Internal].basePath = '' + this._basePath = '' } if (i18n) { - this[Internal].locale = getLocaleMetadata({ + this._locale = getLocaleMetadata({ cookies: () => { const value = headers['cookie'] return value @@ -84,156 +73,154 @@ export class NextURL extends URL { i18n: i18n, }, url: { - hostname: this[Internal].url.hostname || null, - pathname: this[Internal].url.pathname, + hostname: this._url.hostname || null, + pathname: this._url.pathname, }, }) - if (this[Internal].locale?.path.detectedLocale) { - this[Internal].url.pathname = this[Internal].locale!.path.pathname + if (this._locale?.path.detectedLocale) { + this._url.pathname = this._locale.path.pathname } } } - private formatPathname() { - const { i18n } = this[Internal].options - let pathname = this[Internal].url.pathname + formatPathname() { + const { i18n } = this._options + let pathname = this._url.pathname - if ( - this[Internal].locale?.locale && - i18n?.defaultLocale !== this[Internal].locale?.locale - ) { - pathname = `/${this[Internal].locale?.locale}${pathname}` + if (this._locale?.locale && i18n?.defaultLocale !== this._locale?.locale) { + pathname = `/${this._locale?.locale}${pathname}` } - if (this[Internal].basePath) { - pathname = `${this[Internal].basePath}${pathname}` + if (this._basePath) { + pathname = `${this._basePath}${pathname}` } return pathname } - public get locale() { - return this[Internal].locale?.locale ?? '' + get locale() { + if (!this._locale) { + throw new TypeError(`The URL is not configured with i18n`) + } + + return this._locale.locale } - public set locale(locale: string) { - if ( - !this[Internal].locale || - !this[Internal].options.i18n?.locales.includes(locale) - ) { - throw new TypeError( - `The NextURL configuration includes no locale "${locale}"` - ) + set locale(locale: string) { + if (!this._locale) { + throw new TypeError(`The URL is not configured with i18n`) } - this[Internal].locale!.locale = locale + this._locale.locale = locale } get defaultLocale() { - return this[Internal].locale?.defaultLocale + return this._locale?.defaultLocale } get domainLocale() { - return this[Internal].locale?.domain + return this._locale?.domain } get searchParams() { - return this[Internal].url.searchParams + return this._url.searchParams } get host() { - return this[Internal].url.host + return this.absolute ? this._url.host : '' } set host(value: string) { - this[Internal].url.host = value + this._url.host = value } get hostname() { - return this[Internal].url.hostname + return this.absolute ? this._url.hostname : '' } set hostname(value: string) { - this[Internal].url.hostname = value + this._url.hostname = value || 'localhost' } get port() { - return this[Internal].url.port + return this.absolute ? this._url.port : '' } set port(value: string) { - this[Internal].url.port = value + this._url.port = value } get protocol() { - return this[Internal].url.protocol + return this.absolute ? this._url.protocol : '' } set protocol(value: string) { - this[Internal].url.protocol = value + this._url.protocol = value } get href() { const pathname = this.formatPathname() - return `${this.protocol}//${this.host}${pathname}${this[Internal].url.search}` + return this.absolute + ? `${this.protocol}//${this.host}${pathname}${this._url.search}` + : `${pathname}${this._url.search}` } set href(url: string) { - this[Internal].url = parseURL(url) + this._url = createWHATWGURL(url) this.analyzeUrl() } get origin() { - return this[Internal].url.origin + return this.absolute ? this._url.origin : '' } get pathname() { - return this[Internal].url.pathname + return this._url.pathname } set pathname(value: string) { - this[Internal].url.pathname = value + this._url.pathname = value } get hash() { - return this[Internal].url.hash + return this._url.hash } set hash(value: string) { - this[Internal].url.hash = value + this._url.hash = value } get search() { - return this[Internal].url.search + return this._url.search } set search(value: string) { - this[Internal].url.search = value + this._url.search = value } get password() { - return this[Internal].url.password + return this._url.password } set password(value: string) { - this[Internal].url.password = value + this._url.password = value } get username() { - return this[Internal].url.username + return this._url.username } set username(value: string) { - this[Internal].url.username = value + this._url.username = value } get basePath() { - return this[Internal].basePath + return this._basePath } set basePath(value: string) { - this[Internal].basePath = value.startsWith('/') ? value : `/${value}` + this._basePath = value.startsWith('/') ? value : `/${value}` } toString() { @@ -245,12 +232,13 @@ export class NextURL extends URL { } } -const REGEX_LOCALHOST_HOSTNAME = - /(?!^https?:\/\/)(127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}|::1|localhost)/ +function createWHATWGURL(url: string) { + url = url.replace(REGEX_LOCALHOST_HOSTNAME, 'localhost') + return isRelativeURL(url) + ? new URL(url.replace(/^\/+/, '/'), new URL('https://localhost')) + : new URL(url) +} -function parseURL(url: string | URL, base?: string | URL) { - return new URL( - String(url).replace(REGEX_LOCALHOST_HOSTNAME, 'localhost'), - base && String(base).replace(REGEX_LOCALHOST_HOSTNAME, 'localhost') - ) +function isRelativeURL(url: string) { + return url.startsWith('/') } diff --git a/packages/next/server/web/sandbox/sandbox.ts b/packages/next/server/web/sandbox/sandbox.ts index 7a98d4d1223432f..6fb8012c921aa74 100644 --- a/packages/next/server/web/sandbox/sandbox.ts +++ b/packages/next/server/web/sandbox/sandbox.ts @@ -18,19 +18,6 @@ export async function run(params: { runInContext(paramPath) } - const subreq = params.request.headers[`x-middleware-subrequest`] - const subrequests = typeof subreq === 'string' ? subreq.split(':') : [] - if (subrequests.includes(params.name)) { - return { - waitUntil: Promise.resolve(), - response: new context.Response(null, { - headers: { - 'x-middleware-next': '1', - }, - }), - } - } - return context._ENTRIES[`middleware_${params.name}`].default({ request: params.request, }) diff --git a/packages/next/shared/lib/router/utils/relativize-url.ts b/packages/next/shared/lib/router/utils/relativize-url.ts deleted file mode 100644 index 2bc97102f08e64b..000000000000000 --- a/packages/next/shared/lib/router/utils/relativize-url.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Given a URL as a string and a base URL it will make the URL relative - * if the parsed protocol and host is the same as the one in the base - * URL. Otherwise it returns the same URL string. - */ -export function relativizeURL(url: string | string, base: string | URL) { - const baseURL = typeof base === 'string' ? new URL(base) : base - const relative = new URL(url, base) - const origin = `${baseURL.protocol}//${baseURL.host}` - return `${relative.protocol}//${relative.host}` === origin - ? relative.toString().replace(origin, '') - : relative.toString() -} diff --git a/test/integration/custom-server/server.js b/test/integration/custom-server/server.js index 29550fb1906f468..0b34500af9e57ad 100644 --- a/test/integration/custom-server/server.js +++ b/test/integration/custom-server/server.js @@ -1,7 +1,3 @@ -if (process.env.POLYFILL_FETCH) { - global.fetch = require('node-fetch').default -} - const http = require('http') const next = require('next') diff --git a/test/integration/custom-server/test/index.test.js b/test/integration/custom-server/test/index.test.js index ef0dee74715b5d4..272000993f8f2dc 100644 --- a/test/integration/custom-server/test/index.test.js +++ b/test/integration/custom-server/test/index.test.js @@ -195,14 +195,4 @@ describe('Custom Server', () => { } ) }) - - describe('with a custom fetch polyfill', () => { - beforeAll(() => startServer({ POLYFILL_FETCH: 'true' })) - afterAll(() => killApp(server)) - - it('should serve internal file from render', async () => { - const data = await renderViaHTTP(appPort, '/static/hello.txt') - expect(data).toMatch(/hello world/) - }) - }) }) diff --git a/test/integration/middleware/core/pages/interface/[id]/_middleware.js b/test/integration/middleware/core/pages/interface/[id]/_middleware.js deleted file mode 100644 index 679ab02834e00b6..000000000000000 --- a/test/integration/middleware/core/pages/interface/[id]/_middleware.js +++ /dev/null @@ -1,7 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware() { - const response = NextResponse.next() - response.headers.set('x-dynamic-path', 'true') - return response -} diff --git a/test/integration/middleware/core/pages/interface/_middleware.js b/test/integration/middleware/core/pages/interface/_middleware.js index 53e52ae12f1d7ee..a3ac20fd7c379c7 100644 --- a/test/integration/middleware/core/pages/interface/_middleware.js +++ b/test/integration/middleware/core/pages/interface/_middleware.js @@ -54,7 +54,9 @@ export async function middleware(request) { } if (url.pathname.endsWith('/root-subrequest')) { - return fetch(url) + return fetch( + `http://${request.headers.get('host')}/interface/root-subrequest` + ) } return new Response(null, { diff --git a/test/integration/middleware/core/pages/responses/_middleware.js b/test/integration/middleware/core/pages/responses/_middleware.js index a19326aad8f0fd6..255f1c3180a1b84 100644 --- a/test/integration/middleware/core/pages/responses/_middleware.js +++ b/test/integration/middleware/core/pages/responses/_middleware.js @@ -57,11 +57,11 @@ export async function middleware(request, ev) { ev.waitUntil( (async () => { writer.write(encoder.encode('this is a streamed '.repeat(10))) - await sleep(200) + await sleep(2000) writer.write(encoder.encode('after 2 seconds '.repeat(10))) - await sleep(200) + await sleep(2000) writer.write(encoder.encode('after 4 seconds '.repeat(10))) - await sleep(200) + await sleep(2000) writer.close() })() ) diff --git a/test/integration/middleware/core/test/index.test.js b/test/integration/middleware/core/test/index.test.js index 6c3e90e01a56a76..f0e10c23284144b 100644 --- a/test/integration/middleware/core/test/index.test.js +++ b/test/integration/middleware/core/test/index.test.js @@ -447,14 +447,6 @@ function interfaceTests(locale = '') { const element = await browser.elementByCss('.title') expect(await element.text()).toEqual('Dynamic route') }) - - it(`${locale} allows subrequests without infinite loops`, async () => { - const res = await fetchViaHTTP( - context.appPort, - `/interface/root-subrequest` - ) - expect(res.headers.get('x-dynamic-path')).toBe('true') - }) } function getCookieFromResponse(res, cookieName) { diff --git a/test/unit/web-runtime/next-url.test.ts b/test/unit/web-runtime/next-url.test.ts index 7acbdd25b2be0a0..43508d4d3253e37 100644 --- a/test/unit/web-runtime/next-url.test.ts +++ b/test/unit/web-runtime/next-url.test.ts @@ -1,69 +1,74 @@ /* eslint-env jest */ import { NextURL } from 'next/dist/server/web/next-url' -it('has the right shape and prototype', () => { - const parsed = new NextURL('/about?param1=value1', 'http://127.0.0.1') +it('has the right shape', () => { + const parsed = new NextURL('/about?param1=value1') expect(parsed).toBeInstanceOf(URL) }) -it('allows to the pathname', async () => { - const parsed = new NextURL('/about?param1=value1', 'http://127.0.0.1:3000') +it('allows to format relative urls', async () => { + const parsed = new NextURL('/about?param1=value1') expect(parsed.basePath).toEqual('') - expect(parsed.hostname).toEqual('localhost') - expect(parsed.host).toEqual('localhost:3000') - expect(parsed.href).toEqual('http://localhost:3000/about?param1=value1') + expect(parsed.hostname).toEqual('') + expect(parsed.host).toEqual('') + expect(parsed.href).toEqual('/about?param1=value1') parsed.pathname = '/hihi' - expect(parsed.href).toEqual('http://localhost:3000/hihi?param1=value1') + expect(parsed.href).toEqual('/hihi?param1=value1') }) -it('allows to change the host', () => { - const parsed = new NextURL('/about?param1=value1', 'http://127.0.0.1') - expect(parsed.hostname).toEqual('localhost') - expect(parsed.host).toEqual('localhost') - expect(parsed.href).toEqual('http://localhost/about?param1=value1') +it('allows to change the host of a relative url', () => { + const parsed = new NextURL('/about?param1=value1') + expect(parsed.hostname).toEqual('') + expect(parsed.host).toEqual('') + expect(parsed.href).toEqual('/about?param1=value1') parsed.hostname = 'foo.com' expect(parsed.hostname).toEqual('foo.com') expect(parsed.host).toEqual('foo.com') - expect(parsed.href).toEqual('http://foo.com/about?param1=value1') - expect(parsed.toString()).toEqual('http://foo.com/about?param1=value1') + expect(parsed.href).toEqual('https://foo.com/about?param1=value1') + expect(parsed.toString()).toEqual('https://foo.com/about?param1=value1') }) -it('does noop changing to an invalid hostname', () => { +it('allows to change the hostname of a relative url', () => { + const url = new NextURL('/example') + url.hostname = 'foo.com' + expect(url.toString()).toEqual('https://foo.com/example') +}) + +it('allows to remove the hostname of an absolute url', () => { const url = new NextURL('https://foo.com/example') url.hostname = '' - expect(url.toString()).toEqual('https://foo.com/example') + expect(url.toString()).toEqual('/example') }) -it('allows to change the whole href', () => { +it('allows to change the whole href of an absolute url', () => { const url = new NextURL('https://localhost.com/foo') expect(url.hostname).toEqual('localhost.com') expect(url.protocol).toEqual('https:') expect(url.host).toEqual('localhost.com') - url.href = 'http://foo.com/bar' - expect(url.hostname).toEqual('foo.com') - expect(url.protocol).toEqual('http:') - expect(url.host).toEqual('foo.com') + url.href = '/foo' + expect(url.hostname).toEqual('') + expect(url.protocol).toEqual('') + expect(url.host).toEqual('') }) it('allows to update search params', () => { - const url = new NextURL('/example', 'http://localhost.com') + const url = new NextURL('/example') url.searchParams.set('foo', 'bar') expect(url.search).toEqual('?foo=bar') - expect(url.toString()).toEqual('http://localhost.com/example?foo=bar') + expect(url.toString()).toEqual('/example?foo=bar') }) it('parses and formats the basePath', () => { const url = new NextURL('/root/example', { - base: 'http://127.0.0.1', basePath: '/root', }) expect(url.basePath).toEqual('/root') expect(url.pathname).toEqual('/example') - expect(url.toString()).toEqual('http://localhost/root/example') + expect(url.toString()).toEqual('/root/example') const url2 = new NextURL('https://foo.com/root/bar', { basePath: '/root', @@ -84,47 +89,14 @@ it('parses and formats the basePath', () => { expect(url3.basePath).toEqual('') - url3.href = 'http://localhost.com/root/example' - expect(url3.basePath).toEqual('/root') - expect(url3.pathname).toEqual('/example') - expect(url3.toString()).toEqual('http://localhost.com/root/example') -}) - -it('allows to get empty locale when there is no locale', () => { - const url = new NextURL('https://localhost:3000/foo') - expect(url.locale).toEqual('') -}) - -it('doesnt allow to set an unexisting locale', () => { - const url = new NextURL('https://localhost:3000/foo') - let error: Error | null = null - try { - url.locale = 'foo' - } catch (err) { - error = err - } - - expect(error).toBeInstanceOf(TypeError) - expect(error.message).toEqual( - 'The NextURL configuration includes no locale "foo"' - ) -}) - -it('always get a default locale', () => { - const url = new NextURL('/bar', { - base: 'http://127.0.0.1', - i18n: { - defaultLocale: 'en', - locales: ['en', 'es', 'fr'], - }, - }) - - expect(url.locale).toEqual('en') + url3.href = '/root/example' + expect(url.basePath).toEqual('/root') + expect(url.pathname).toEqual('/example') + expect(url.toString()).toEqual('/root/example') }) it('parses and formats the default locale', () => { const url = new NextURL('/es/bar', { - base: 'http://127.0.0.1', basePath: '/root', i18n: { defaultLocale: 'en', @@ -133,19 +105,19 @@ it('parses and formats the default locale', () => { }) expect(url.locale).toEqual('es') - expect(url.toString()).toEqual('http://localhost/es/bar') + expect(url.toString()).toEqual('/es/bar') url.basePath = '/root' expect(url.locale).toEqual('es') - expect(url.toString()).toEqual('http://localhost/root/es/bar') + expect(url.toString()).toEqual('/root/es/bar') url.locale = 'en' expect(url.locale).toEqual('en') - expect(url.toString()).toEqual('http://localhost/root/bar') + expect(url.toString()).toEqual('/root/bar') url.locale = 'fr' expect(url.locale).toEqual('fr') - expect(url.toString()).toEqual('http://localhost/root/fr/bar') + expect(url.toString()).toEqual('/root/fr/bar') }) it('consider 127.0.0.1 and variations as localhost', () => { @@ -159,13 +131,3 @@ it('consider 127.0.0.1 and variations as localhost', () => { expect(new NextURL('https://127.0.1.0:3000/hello')).toStrictEqual(httpsUrl) expect(new NextURL('https://::1:3000/hello')).toStrictEqual(httpsUrl) }) - -it('allows to change the port', () => { - const url = new NextURL('https://localhost:3000/foo') - url.port = '3001' - expect(url.href).toEqual('https://localhost:3001/foo') - url.port = '80' - expect(url.href).toEqual('https://localhost:80/foo') - url.port = '' - expect(url.href).toEqual('https://localhost/foo') -})