forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix router not working on some protocol (vercel#16650)
Co-authored-by: Tim Neutkens <[email protected]> Co-authored-by: Joe Haddad <[email protected]> Co-authored-by: JJ Kasper <[email protected]>
- Loading branch information
1 parent
3c33794
commit 28e1287
Showing
11 changed files
with
688 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
29 changes: 10 additions & 19 deletions
29
packages/next/next-server/lib/router/utils/parse-relative-url.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,29 @@ | ||
import { getLocationOrigin } from '../../utils' | ||
import { searchParamsToUrlQuery } from './querystring' | ||
|
||
const DUMMY_BASE = new URL( | ||
typeof window === 'undefined' ? 'http://n' : getLocationOrigin() | ||
) | ||
|
||
/** | ||
* Parses path-relative urls (e.g. `/hello/world?foo=bar`). If url isn't path-relative | ||
* (e.g. `./hello`) then at least base must be. | ||
* Absolute urls are rejected with one exception, in the browser, absolute urls that are on | ||
* the current origin will be parsed as relative | ||
*/ | ||
export function parseRelativeUrl(url: string, base?: string) { | ||
const resolvedBase = base ? new URL(base, DUMMY_BASE) : DUMMY_BASE | ||
const { | ||
pathname, | ||
searchParams, | ||
search, | ||
hash, | ||
href, | ||
origin, | ||
protocol, | ||
} = new URL(url, resolvedBase) | ||
if ( | ||
origin !== DUMMY_BASE.origin || | ||
(protocol !== 'http:' && protocol !== 'https:') | ||
) { | ||
const globalBase = new URL( | ||
typeof window === 'undefined' ? 'http://n' : getLocationOrigin() | ||
) | ||
const resolvedBase = base ? new URL(base, globalBase) : globalBase | ||
const { pathname, searchParams, search, hash, href, origin } = new URL( | ||
url, | ||
resolvedBase | ||
) | ||
if (origin !== globalBase.origin) { | ||
throw new Error('invariant: invalid relative URL') | ||
} | ||
return { | ||
pathname, | ||
query: searchParamsToUrlQuery(searchParams), | ||
search, | ||
hash, | ||
href: href.slice(DUMMY_BASE.origin.length), | ||
href: href.slice(globalBase.origin.length), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
const path = require('path') | ||
const outdir = path.join(__dirname, 'out') | ||
|
||
module.exports = { | ||
basePath: outdir, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import Link from 'next/link' | ||
|
||
export default () => ( | ||
<div id="about-page"> | ||
<p>This is the about page</p> | ||
<Link href="/"> | ||
<a>Go Back</a> | ||
</Link> | ||
</div> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import Link from 'next/link' | ||
import Router from 'next/router' | ||
|
||
function routeToAbout(e) { | ||
e.preventDefault() | ||
Router.push('/about') | ||
} | ||
|
||
export default () => ( | ||
<div id="home-page"> | ||
<p>This is the home page</p> | ||
<Link href="/about"> | ||
<a id="about-via-link">about via Link</a> | ||
</Link> | ||
<a id="about-via-router" onClick={routeToAbout} href="#"> | ||
about via Router | ||
</a> | ||
</div> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
const { join } = require('path') | ||
const { app, BrowserWindow } = require('electron') | ||
const url = require('url') | ||
|
||
function createWindow() { | ||
const mainWindow = new BrowserWindow({ | ||
width: 800, | ||
height: 600, | ||
webPreferences: { | ||
nodeIntegration: false, | ||
}, | ||
}) | ||
mainWindow.loadURL( | ||
url.format({ | ||
pathname: join(__dirname, 'index.html'), | ||
protocol: 'file:', | ||
slashes: true, | ||
}) | ||
) | ||
} | ||
|
||
app.whenReady().then(() => { | ||
createWindow() | ||
app.on('activate', () => { | ||
if (BrowserWindow.getAllWindows().length === 0) createWindow() | ||
}) | ||
}) | ||
|
||
app.on('window-all-closed', () => { | ||
app.quit() | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* eslint-env jest */ | ||
|
||
import { join } from 'path' | ||
import { nextBuild, nextExport } from 'next-test-utils' | ||
import { Application } from 'spectron' | ||
import electron from 'electron' | ||
|
||
jest.setTimeout(1000 * 60 * 5) | ||
|
||
const nextdir = join(__dirname, '../') | ||
const outdir = join(nextdir, 'out') | ||
const appdir = join(outdir, 'main.js') | ||
let app = null | ||
|
||
describe('Parse Relative Url', () => { | ||
describe('File Protocol via Electron', () => { | ||
beforeAll(async () => { | ||
await nextBuild(nextdir) | ||
await nextExport(nextdir, { outdir }) | ||
|
||
app = new Application({ | ||
path: electron, | ||
args: [ | ||
'--no-sandbox', | ||
'--disable-dev-shm-usage', | ||
'--disable-gpu', | ||
appdir, | ||
], | ||
chromeDriverArgs: [ | ||
'--no-sandbox', | ||
'--disable-dev-shm-usage', | ||
'--disable-gpu', | ||
], | ||
}) | ||
await app.start() | ||
}) | ||
|
||
afterAll(async () => { | ||
if (app && app.isRunning()) { | ||
await app.stop() | ||
} | ||
}) | ||
|
||
it('app init', async () => { | ||
const count = await app.client.getWindowCount() | ||
expect(count).toEqual(1) | ||
}) | ||
|
||
it('should render the home page', async () => { | ||
const text = await app.client.$('#home-page p').getText() | ||
expect(text).toBe('This is the home page') | ||
}) | ||
|
||
it('should do navigations via Link', async () => { | ||
await app.client.$('#about-via-link').click() | ||
const text = await app.client.$('#about-page p').getText() | ||
|
||
expect(text).toBe('This is the about page') | ||
}) | ||
|
||
it('should do back to home page via Link', async () => { | ||
await app.client.$('#about-page a').click() | ||
const text = await app.client.$('#home-page p').getText() | ||
|
||
expect(text).toBe('This is the home page') | ||
}) | ||
|
||
it('should do navigations via Router', async () => { | ||
await app.client.$('#about-via-router').click() | ||
const text = await app.client.$('#about-page p').getText() | ||
|
||
expect(text).toBe('This is the about page') | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* eslint-env jest */ | ||
import { parseRelativeUrl } from 'next/dist/next-server/lib/router/utils/parse-relative-url' | ||
|
||
// convenience function so tests can be aligned neatly | ||
// and easy to eyeball | ||
const check = (windowUrl, targetUrl, expected) => { | ||
window.location = new URL(windowUrl) | ||
if (typeof expected === 'string') { | ||
expect(() => parseRelativeUrl(targetUrl)).toThrow(expected) | ||
} else { | ||
const parsedUrl = parseRelativeUrl(targetUrl) | ||
expect(parsedUrl.pathname).toBe(expected.pathname) | ||
expect(parsedUrl.search).toBe(expected.search) | ||
expect(parsedUrl.hash).toBe(expected.hash) | ||
} | ||
} | ||
|
||
describe('parseRelativeUrl', () => { | ||
beforeAll(() => { | ||
global.window = { | ||
location: {}, | ||
} | ||
}) | ||
|
||
afterAll(() => { | ||
delete global.window | ||
}) | ||
|
||
it('should parse relative url', () => { | ||
check( | ||
'http://example.com:3210/someA/pathB?fooC=barD#hashE', | ||
'/someF/pathG?fooH=barI#hashJ', | ||
{ | ||
pathname: '/someF/pathG', | ||
search: '?fooH=barI', | ||
hash: '#hashJ', | ||
} | ||
) | ||
}) | ||
|
||
it('should parse relative url when start with dot', () => { | ||
check( | ||
'http://example.com:3210/someA/pathB?fooC=barD#hashE', | ||
'./someF/pathG?fooH=barI#hashJ', | ||
{ | ||
pathname: '/someF/pathG', | ||
search: '?fooH=barI', | ||
hash: '#hashJ', | ||
} | ||
) | ||
}) | ||
|
||
it('should parse relative url on special protocol', () => { | ||
check( | ||
'ionic://localhost/someA/pathB?fooC=barD#hashE', | ||
'/someF/pathG?fooH=barI#hashJ', | ||
{ | ||
pathname: '/someF/pathG', | ||
search: '?fooH=barI', | ||
hash: '#hashJ', | ||
} | ||
) | ||
check( | ||
'file:///someA/pathB?fooC=barD#hashE', | ||
'/someF/pathG?fooH=barI#hashJ', | ||
{ | ||
pathname: '/someF/pathG', | ||
search: '?fooH=barI', | ||
hash: '#hashJ', | ||
} | ||
) | ||
}) | ||
|
||
it('should parse the full url with current origin', () => { | ||
check( | ||
'http://example.com:3210/someA/pathB?fooC=barD#hashE', | ||
'http://example.com:3210/someF/pathG?fooH=barI#hashJ', | ||
{ | ||
pathname: '/someF/pathG', | ||
search: '?fooH=barI', | ||
hash: '#hashJ', | ||
} | ||
) | ||
}) | ||
|
||
it('should throw when parsing the full url with diff origin', () => { | ||
check( | ||
'http://example.com:3210/someA/pathB?fooC=barD#hashE', | ||
'http://google.com/someF/pathG?fooH=barI#hashJ', | ||
'invariant: invalid relative URL' | ||
) | ||
}) | ||
|
||
it('should throw when parsing the special prefix', () => { | ||
check( | ||
'http://example.com:3210/someA/pathB?fooC=barD#hashE', | ||
'mailto:[email protected]', | ||
'invariant: invalid relative URL' | ||
) | ||
check( | ||
'http://example.com:3210/someA/pathB?fooC=barD#hashE', | ||
'tel:+123456789', | ||
'invariant: invalid relative URL' | ||
) | ||
check( | ||
'http://example.com:3210/someA/pathB?fooC=barD#hashE', | ||
'sms:+123456789', | ||
'invariant: invalid relative URL' | ||
) | ||
}) | ||
}) |
Oops, something went wrong.