@shuvi/router 1.0.44 → 1.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/history/base.d.ts +6 -0
- package/esm/history/base.js +4 -3
- package/esm/history/browser.d.ts +3 -2
- package/esm/history/browser.js +18 -5
- package/esm/history/hash.d.ts +3 -2
- package/esm/history/hash.js +21 -20
- package/esm/history/index.d.ts +4 -4
- package/esm/history/index.js +4 -4
- package/esm/history/memory.d.ts +3 -3
- package/esm/history/memory.js +4 -11
- package/esm/matchRoutes.js +5 -5
- package/esm/router.d.ts +0 -1
- package/esm/router.js +16 -9
- package/esm/types/history.d.ts +2 -0
- package/esm/types/router.d.ts +1 -0
- package/esm/utils/history.d.ts +2 -1
- package/esm/utils/history.js +10 -3
- package/esm/utils/path.d.ts +9 -1
- package/esm/utils/path.js +23 -13
- package/lib/history/base.d.ts +6 -0
- package/lib/history/base.js +3 -2
- package/lib/history/browser.d.ts +3 -2
- package/lib/history/browser.js +18 -5
- package/lib/history/hash.d.ts +3 -2
- package/lib/history/hash.js +21 -20
- package/lib/history/index.d.ts +4 -4
- package/lib/history/index.js +4 -4
- package/lib/history/memory.d.ts +3 -3
- package/lib/history/memory.js +3 -10
- package/lib/matchRoutes.js +4 -4
- package/lib/router.d.ts +0 -1
- package/lib/router.js +15 -8
- package/lib/types/history.d.ts +2 -0
- package/lib/types/router.d.ts +1 -0
- package/lib/utils/history.d.ts +2 -1
- package/lib/utils/history.js +9 -2
- package/lib/utils/path.d.ts +9 -1
- package/lib/utils/path.js +25 -14
- package/package.json +2 -2
- package/esm/utils/dom.d.ts +0 -1
- package/esm/utils/dom.js +0 -1
- package/lib/utils/dom.d.ts +0 -1
- package/lib/utils/dom.js +0 -4
package/esm/history/base.d.ts
CHANGED
|
@@ -38,13 +38,19 @@ export interface PushOptions {
|
|
|
38
38
|
redirectedFrom?: Path;
|
|
39
39
|
skipGuards?: boolean;
|
|
40
40
|
}
|
|
41
|
+
export declare type BaseHistoryOptions = {
|
|
42
|
+
basename?: string;
|
|
43
|
+
};
|
|
41
44
|
export default abstract class BaseHistory {
|
|
42
45
|
action: Action;
|
|
43
46
|
location: Location;
|
|
47
|
+
basename: string;
|
|
48
|
+
constructor({ basename }?: BaseHistoryOptions);
|
|
44
49
|
doTransition: (to: PathRecord, onComplete: Function, onAbort?: Function, skipGuards?: boolean, isReplace?: boolean, redirectedFrom?: Path) => void;
|
|
45
50
|
protected _index: number;
|
|
46
51
|
protected _blockers: import("../utils").Events<Blocker<State>>;
|
|
47
52
|
protected abstract getIndexAndLocation(): [number, Location];
|
|
53
|
+
/** setup will be called at `onTransition` and `onAbort` */
|
|
48
54
|
abstract setup(): void;
|
|
49
55
|
/**
|
|
50
56
|
* Jump to the specified route
|
package/esm/history/base.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createLocation, createEvents, resolvePath, pathToString } from '../utils';
|
|
1
|
+
import { createLocation, createEvents, resolvePath, pathToString, normalizeBase } from '../utils';
|
|
2
2
|
/**
|
|
3
3
|
* A POP indicates a change to an arbitrary index in the history stack, such
|
|
4
4
|
* as a back or forward navigation. It does not describe the direction of the
|
|
@@ -19,12 +19,13 @@ export const ACTION_PUSH = 'PUSH';
|
|
|
19
19
|
*/
|
|
20
20
|
export const ACTION_REPLACE = 'REPLACE';
|
|
21
21
|
export default class BaseHistory {
|
|
22
|
-
constructor() {
|
|
22
|
+
constructor({ basename = '' } = {}) {
|
|
23
23
|
this.action = ACTION_POP;
|
|
24
24
|
this.location = createLocation('/');
|
|
25
25
|
this.doTransition = () => void 0;
|
|
26
26
|
this._index = 0;
|
|
27
27
|
this._blockers = createEvents();
|
|
28
|
+
this.basename = normalizeBase(basename);
|
|
28
29
|
}
|
|
29
30
|
back() {
|
|
30
31
|
this.go(-1);
|
|
@@ -36,7 +37,7 @@ export default class BaseHistory {
|
|
|
36
37
|
const toPath = resolvePath(to, from);
|
|
37
38
|
return {
|
|
38
39
|
path: toPath,
|
|
39
|
-
href: pathToString(toPath)
|
|
40
|
+
href: pathToString(toPath, this.basename)
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
transitionTo(to, { onTransition, onAbort, action = ACTION_PUSH, state = null, redirectedFrom, skipGuards }) {
|
package/esm/history/browser.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PathRecord, Location, Blocker } from '../types';
|
|
2
|
-
import BaseHistory, { PushOptions } from './base';
|
|
2
|
+
import BaseHistory, { PushOptions, BaseHistoryOptions } from './base';
|
|
3
|
+
export declare type BrowserHistoryOptions = BaseHistoryOptions;
|
|
3
4
|
export default class BrowserHistory extends BaseHistory {
|
|
4
5
|
private _history;
|
|
5
|
-
constructor();
|
|
6
|
+
constructor({ basename }?: BrowserHistoryOptions);
|
|
6
7
|
push(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
7
8
|
replace(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
8
9
|
go(delta: number): void;
|
package/esm/history/browser.js
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { createLocation, pushState, replaceState, addBlocker, warning } from '../utils';
|
|
2
2
|
import BaseHistory, { ACTION_POP, ACTION_REPLACE } from './base';
|
|
3
3
|
export default class BrowserHistory extends BaseHistory {
|
|
4
|
-
constructor() {
|
|
5
|
-
super();
|
|
4
|
+
constructor({ basename } = {}) {
|
|
5
|
+
super({ basename });
|
|
6
6
|
this._history = window.history;
|
|
7
7
|
[this._index, this.location] = this.getIndexAndLocation();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
// redirect immediately if
|
|
9
|
+
// 1. no index
|
|
10
|
+
// 2. we're not on the right url (redirectedFrom means url not match basename)
|
|
11
|
+
const { notMatchBasename } = this.location;
|
|
12
|
+
if (this._index == null || notMatchBasename) {
|
|
13
|
+
this._index = this._index || 0;
|
|
14
|
+
this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '', notMatchBasename ? this.resolve(this.location).href : undefined);
|
|
15
|
+
}
|
|
16
|
+
// recalculate location if not match basename
|
|
17
|
+
if (notMatchBasename) {
|
|
18
|
+
const state = this._history.state || {};
|
|
19
|
+
this.location = createLocation(this.location, {
|
|
20
|
+
state: state.usr || null,
|
|
21
|
+
key: state.key || 'default'
|
|
22
|
+
});
|
|
11
23
|
}
|
|
12
24
|
}
|
|
13
25
|
push(to, { state, redirectedFrom } = {}) {
|
|
@@ -96,6 +108,7 @@ export default class BrowserHistory extends BaseHistory {
|
|
|
96
108
|
search,
|
|
97
109
|
hash
|
|
98
110
|
}, {
|
|
111
|
+
basename: this.basename,
|
|
99
112
|
state: state.usr || null,
|
|
100
113
|
key: state.key || 'default'
|
|
101
114
|
})
|
package/esm/history/hash.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PathRecord, Location, Blocker, ResolvedPath } from '../types';
|
|
2
|
-
import BaseHistory, { PushOptions } from './base';
|
|
2
|
+
import BaseHistory, { PushOptions, BaseHistoryOptions } from './base';
|
|
3
|
+
export declare type HashHistoryOptions = BaseHistoryOptions;
|
|
3
4
|
export default class HashHistory extends BaseHistory {
|
|
4
5
|
private _history;
|
|
5
|
-
constructor();
|
|
6
|
+
constructor({ basename }?: HashHistoryOptions);
|
|
6
7
|
push(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
7
8
|
replace(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
8
9
|
go(delta: number): void;
|
package/esm/history/hash.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { createLocation, pushState, replaceState, addBlocker, resolvePath, pathToString, warning } from '../utils';
|
|
2
2
|
import BaseHistory, { ACTION_POP, ACTION_REPLACE } from './base';
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
let href = '';
|
|
6
|
-
if (base && base.getAttribute('href')) {
|
|
7
|
-
let url = window.location.href;
|
|
8
|
-
let hashIndex = url.indexOf('#');
|
|
9
|
-
href = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
10
|
-
}
|
|
11
|
-
return href;
|
|
12
|
-
}
|
|
13
|
-
function createHref(to) {
|
|
14
|
-
return (getBaseHref() +
|
|
15
|
-
'#' +
|
|
16
|
-
(typeof to === 'string' ? to : pathToString(resolvePath(to))));
|
|
3
|
+
function createHref(to, basename) {
|
|
4
|
+
return '#' + pathToString(resolvePath(to), basename);
|
|
17
5
|
}
|
|
18
6
|
export default class HashHistory extends BaseHistory {
|
|
19
|
-
constructor() {
|
|
20
|
-
super();
|
|
7
|
+
constructor({ basename } = {}) {
|
|
8
|
+
super({ basename });
|
|
21
9
|
this._history = window.history;
|
|
22
10
|
[this._index, this.location] = this.getIndexAndLocation();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
// redirect immediately if
|
|
12
|
+
// 1. no index
|
|
13
|
+
// 2. we're not on the right url (redirectedFrom means url not match basename)
|
|
14
|
+
const { notMatchBasename } = this.location;
|
|
15
|
+
if (this._index == null || notMatchBasename) {
|
|
16
|
+
this._index = this._index || 0;
|
|
17
|
+
this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '', notMatchBasename ? this.resolve(this.location).href : undefined);
|
|
18
|
+
}
|
|
19
|
+
// recalculate location if not match basename
|
|
20
|
+
if (notMatchBasename) {
|
|
21
|
+
const state = this._history.state || {};
|
|
22
|
+
this.location = createLocation(this.location, {
|
|
23
|
+
state: state.usr || null,
|
|
24
|
+
key: state.key || 'default'
|
|
25
|
+
});
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
push(to, { state, redirectedFrom } = {}) {
|
|
@@ -54,7 +54,7 @@ export default class HashHistory extends BaseHistory {
|
|
|
54
54
|
const toPath = resolvePath(to, from);
|
|
55
55
|
return {
|
|
56
56
|
path: toPath,
|
|
57
|
-
href: createHref(toPath)
|
|
57
|
+
href: createHref(toPath, this.basename)
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
setup() {
|
|
@@ -127,6 +127,7 @@ export default class HashHistory extends BaseHistory {
|
|
|
127
127
|
search,
|
|
128
128
|
hash
|
|
129
129
|
}, {
|
|
130
|
+
basename: this.basename,
|
|
130
131
|
state: state.usr || null,
|
|
131
132
|
key: state.key || 'default'
|
|
132
133
|
})
|
package/esm/history/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import MemoryHistory, { MemoryHistoryOptions, InitialEntry } from './memory';
|
|
2
|
-
import BrowserHistory from './browser';
|
|
3
|
-
import HashHistory from './hash';
|
|
2
|
+
import BrowserHistory, { BrowserHistoryOptions } from './browser';
|
|
3
|
+
import HashHistory, { HashHistoryOptions } from './hash';
|
|
4
4
|
export * from './base';
|
|
5
5
|
export { MemoryHistory, MemoryHistoryOptions, InitialEntry };
|
|
6
|
-
export declare function createBrowserHistory(): BrowserHistory;
|
|
7
|
-
export declare function createHashHistory(): HashHistory;
|
|
6
|
+
export declare function createBrowserHistory(options?: BrowserHistoryOptions): BrowserHistory;
|
|
7
|
+
export declare function createHashHistory(options?: HashHistoryOptions): HashHistory;
|
|
8
8
|
export declare function createMemoryHistory(options?: MemoryHistoryOptions): MemoryHistory;
|
package/esm/history/index.js
CHANGED
|
@@ -3,11 +3,11 @@ import BrowserHistory from './browser';
|
|
|
3
3
|
import HashHistory from './hash';
|
|
4
4
|
export * from './base';
|
|
5
5
|
export { MemoryHistory };
|
|
6
|
-
export function createBrowserHistory() {
|
|
7
|
-
return new BrowserHistory();
|
|
6
|
+
export function createBrowserHistory(options = {}) {
|
|
7
|
+
return new BrowserHistory(options);
|
|
8
8
|
}
|
|
9
|
-
export function createHashHistory() {
|
|
10
|
-
return new HashHistory();
|
|
9
|
+
export function createHashHistory(options = {}) {
|
|
10
|
+
return new HashHistory(options);
|
|
11
11
|
}
|
|
12
12
|
export function createMemoryHistory(options = {}) {
|
|
13
13
|
return new MemoryHistory(options);
|
package/esm/history/memory.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PathRecord, Location, Blocker, PartialLocation
|
|
1
|
+
import { PathRecord, Location, Blocker, PartialLocation } from '../types';
|
|
2
2
|
import BaseHistory, { PushOptions } from './base';
|
|
3
3
|
/**
|
|
4
4
|
* A user-supplied object that describes a location. Used when providing
|
|
@@ -8,15 +8,15 @@ export declare type InitialEntry = string | PartialLocation;
|
|
|
8
8
|
export declare type MemoryHistoryOptions = {
|
|
9
9
|
initialEntries?: InitialEntry[];
|
|
10
10
|
initialIndex?: number;
|
|
11
|
+
basename?: string;
|
|
11
12
|
};
|
|
12
13
|
export default class MemoryHistory extends BaseHistory {
|
|
13
14
|
private _entries;
|
|
14
|
-
constructor({ initialEntries, initialIndex }?: MemoryHistoryOptions);
|
|
15
|
+
constructor({ initialEntries, initialIndex, basename }?: MemoryHistoryOptions);
|
|
15
16
|
setup(): void;
|
|
16
17
|
push(to: PathRecord, { state, redirectedFrom, skipGuards }?: PushOptions): void;
|
|
17
18
|
replace(to: PathRecord, { state, redirectedFrom, skipGuards }?: PushOptions): void;
|
|
18
19
|
go(delta: number): void;
|
|
19
20
|
block(blocker: Blocker): () => void;
|
|
20
|
-
resolve(to: PathRecord, from?: string): ResolvedPath;
|
|
21
21
|
protected getIndexAndLocation(): [number, Location];
|
|
22
22
|
}
|
package/esm/history/memory.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { createLocation, resolvePath,
|
|
1
|
+
import { createLocation, resolvePath, warning } from '../utils';
|
|
2
2
|
import BaseHistory, { ACTION_POP, ACTION_REPLACE } from './base';
|
|
3
3
|
function clamp(n, lowerBound, upperBound) {
|
|
4
4
|
return Math.min(Math.max(n, lowerBound), upperBound);
|
|
5
5
|
}
|
|
6
6
|
export default class MemoryHistory extends BaseHistory {
|
|
7
|
-
constructor({ initialEntries = ['/'], initialIndex } = {}) {
|
|
8
|
-
super();
|
|
7
|
+
constructor({ initialEntries = ['/'], initialIndex, basename = '' } = {}) {
|
|
8
|
+
super({ basename });
|
|
9
9
|
this._entries = [];
|
|
10
10
|
this._entries = initialEntries.map(entry => {
|
|
11
|
-
let location = createLocation(Object.assign({ pathname: '/', search: '', hash: '' }, (typeof entry === 'string' ? resolvePath(entry) : entry)));
|
|
11
|
+
let location = createLocation(Object.assign({ pathname: '/', search: '', hash: '' }, (typeof entry === 'string' ? resolvePath(entry) : entry)), { basename: this.basename });
|
|
12
12
|
warning(location.pathname.charAt(0) === '/', `Relative pathnames are not supported in createMemoryHistory({ initialEntries }) (invalid entry: ${JSON.stringify(entry)})`);
|
|
13
13
|
return location;
|
|
14
14
|
});
|
|
@@ -63,13 +63,6 @@ export default class MemoryHistory extends BaseHistory {
|
|
|
63
63
|
block(blocker) {
|
|
64
64
|
return this._blockers.push(blocker);
|
|
65
65
|
}
|
|
66
|
-
resolve(to, from) {
|
|
67
|
-
const toPath = resolvePath(to, from);
|
|
68
|
-
return {
|
|
69
|
-
path: toPath,
|
|
70
|
-
href: pathToString(toPath)
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
66
|
getIndexAndLocation() {
|
|
74
67
|
const index = this._index;
|
|
75
68
|
return [index, this._entries[index]];
|
package/esm/matchRoutes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { matchPathname } from './matchPathname';
|
|
2
|
-
import { joinPaths, resolvePath } from './utils';
|
|
2
|
+
import { joinPaths, normalizeBase, resolvePath, stripBase } from './utils';
|
|
3
3
|
import { tokensToParser, comparePathParserScore } from './pathParserRanker';
|
|
4
4
|
import { tokenizePath } from './pathTokenizer';
|
|
5
5
|
function matchRouteBranch(branch, pathname) {
|
|
@@ -67,12 +67,12 @@ export function matchRoutes(routes, location, basename = '') {
|
|
|
67
67
|
}
|
|
68
68
|
let pathname = location.pathname || '/';
|
|
69
69
|
if (basename) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const normalizedBasename = normalizeBase(basename);
|
|
71
|
+
const pathnameWithoutBase = stripBase(pathname, normalizedBasename);
|
|
72
|
+
if (pathnameWithoutBase) {
|
|
73
|
+
pathname = pathnameWithoutBase;
|
|
73
74
|
}
|
|
74
75
|
else {
|
|
75
|
-
// Pathname does not start with the basename, no match.
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
78
|
}
|
package/esm/router.d.ts
CHANGED
package/esm/router.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createDefer } from '@shuvi/utils/defer';
|
|
2
2
|
import { matchRoutes } from './matchRoutes';
|
|
3
3
|
import { createRoutesFromArray } from './createRoutesFromArray';
|
|
4
|
-
import {
|
|
4
|
+
import { createEvents, resolvePath } from './utils';
|
|
5
5
|
import { isError, isFunction } from './utils/error';
|
|
6
6
|
import { runQueue } from './utils/async';
|
|
7
7
|
import { getRedirectFromRoutes } from './getRedirectFromRoutes';
|
|
@@ -17,7 +17,7 @@ const START = {
|
|
|
17
17
|
redirected: false
|
|
18
18
|
};
|
|
19
19
|
class Router {
|
|
20
|
-
constructor({
|
|
20
|
+
constructor({ history, routes }) {
|
|
21
21
|
this._pending = null;
|
|
22
22
|
this._cancleHandler = null;
|
|
23
23
|
this._ready = false;
|
|
@@ -28,9 +28,14 @@ class Router {
|
|
|
28
28
|
this._afterEachs = createEvents();
|
|
29
29
|
this.init = () => {
|
|
30
30
|
const setup = () => this._history.setup();
|
|
31
|
-
this.
|
|
31
|
+
const current = this._getCurrent();
|
|
32
|
+
this._history.transitionTo(current, {
|
|
32
33
|
onTransition: setup,
|
|
33
|
-
onAbort: setup
|
|
34
|
+
onAbort: setup,
|
|
35
|
+
// current.redirected means the initial url does not match basename and should redirect
|
|
36
|
+
// so we just skip all guards
|
|
37
|
+
// this logic only applies to memory history
|
|
38
|
+
skipGuards: Boolean(current.redirected)
|
|
34
39
|
});
|
|
35
40
|
return this;
|
|
36
41
|
};
|
|
@@ -65,11 +70,11 @@ class Router {
|
|
|
65
70
|
return this._afterEachs.push(listener);
|
|
66
71
|
};
|
|
67
72
|
this.resolve = (to, from) => {
|
|
68
|
-
return this._history.resolve(to, from
|
|
73
|
+
return this._history.resolve(to, from);
|
|
69
74
|
};
|
|
70
75
|
this.match = (to) => {
|
|
71
|
-
const { _routes: routes
|
|
72
|
-
const matches = matchRoutes(routes, to
|
|
76
|
+
const { _routes: routes } = this;
|
|
77
|
+
const matches = matchRoutes(routes, to);
|
|
73
78
|
return matches || [];
|
|
74
79
|
};
|
|
75
80
|
this.replaceRoutes = (routes) => {
|
|
@@ -94,7 +99,6 @@ class Router {
|
|
|
94
99
|
onAbort: setup
|
|
95
100
|
});
|
|
96
101
|
};
|
|
97
|
-
this._basename = normalizeBase(basename);
|
|
98
102
|
this._history = history;
|
|
99
103
|
this._routes = createRoutesFromArray(routes);
|
|
100
104
|
this._current = START;
|
|
@@ -112,6 +116,9 @@ class Router {
|
|
|
112
116
|
get action() {
|
|
113
117
|
return this._history.action;
|
|
114
118
|
}
|
|
119
|
+
get basename() {
|
|
120
|
+
return this._history.basename;
|
|
121
|
+
}
|
|
115
122
|
/*
|
|
116
123
|
The Full Navigation Resolution Flow for shuvi/router
|
|
117
124
|
1. Navigation triggered.
|
|
@@ -255,7 +262,7 @@ class Router {
|
|
|
255
262
|
hash: location.hash,
|
|
256
263
|
query: location.query,
|
|
257
264
|
state: location.state,
|
|
258
|
-
redirected:
|
|
265
|
+
redirected: Boolean(location.redirectedFrom) || location.notMatchBasename,
|
|
259
266
|
key: location.key
|
|
260
267
|
};
|
|
261
268
|
}
|
package/esm/types/history.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ export interface PartialPath {
|
|
|
96
96
|
*/
|
|
97
97
|
export interface Location<S extends State = State> extends Path {
|
|
98
98
|
redirectedFrom?: Path;
|
|
99
|
+
notMatchBasename?: boolean;
|
|
99
100
|
/**
|
|
100
101
|
* An object of arbitrary data associated with this location.
|
|
101
102
|
*
|
|
@@ -114,6 +115,7 @@ export interface Location<S extends State = State> extends Path {
|
|
|
114
115
|
* A partial Location object that may be missing some properties.
|
|
115
116
|
*/
|
|
116
117
|
export interface PartialLocation<S extends State = State> extends PartialPath {
|
|
118
|
+
redirectedFrom?: Path;
|
|
117
119
|
/**
|
|
118
120
|
* An object of arbitrary data associated with this location.
|
|
119
121
|
*
|
package/esm/types/router.d.ts
CHANGED
package/esm/utils/history.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HistoryState, Location, PathRecord, State, Key, Blocker, Path } from '../types';
|
|
2
2
|
import { Events } from './misc';
|
|
3
|
-
export declare function createLocation(to: PathRecord, { state, key, redirectedFrom }?: {
|
|
3
|
+
export declare function createLocation(to: PathRecord, { basename, state, key, redirectedFrom }?: {
|
|
4
|
+
basename?: string;
|
|
4
5
|
state?: State;
|
|
5
6
|
key?: Key;
|
|
6
7
|
redirectedFrom?: Path;
|
package/esm/utils/history.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readOnly } from './misc';
|
|
2
|
-
import { resolvePath } from './path';
|
|
2
|
+
import { resolvePath, stripBase } from './path';
|
|
3
3
|
const BeforeUnloadEventType = 'beforeunload';
|
|
4
4
|
function promptBeforeUnload(event) {
|
|
5
5
|
// Cancel the event.
|
|
@@ -10,8 +10,15 @@ function promptBeforeUnload(event) {
|
|
|
10
10
|
function createKey() {
|
|
11
11
|
return Math.random().toString(36).substr(2, 8);
|
|
12
12
|
}
|
|
13
|
-
export function createLocation(to, { state = null, key, redirectedFrom } = {}) {
|
|
14
|
-
|
|
13
|
+
export function createLocation(to, { basename, state = null, key, redirectedFrom } = {}) {
|
|
14
|
+
const resolved = resolvePath(to);
|
|
15
|
+
const pathnameWithoutBase = stripBase(resolved.pathname, basename || '/');
|
|
16
|
+
if (pathnameWithoutBase) {
|
|
17
|
+
resolved.pathname = pathnameWithoutBase;
|
|
18
|
+
}
|
|
19
|
+
const notMatchBasename = Boolean(basename) && !pathnameWithoutBase;
|
|
20
|
+
return readOnly(Object.assign(Object.assign({}, resolved), { redirectedFrom,
|
|
21
|
+
notMatchBasename,
|
|
15
22
|
state, key: key || createKey() }));
|
|
16
23
|
}
|
|
17
24
|
export function pushState(state, url, { replace = false } = {}) {
|
package/esm/utils/path.d.ts
CHANGED
|
@@ -6,8 +6,16 @@ export declare const joinPaths: (paths: string[]) => string;
|
|
|
6
6
|
export declare const splitPath: (path: string) => string[];
|
|
7
7
|
export declare function normalizeBase(base: string): string;
|
|
8
8
|
export declare function parseQuery(queryStr: string): qs.ParsedQuery<string>;
|
|
9
|
-
export declare function pathToString({ pathname, search, hash, query }: PartialPath): string;
|
|
9
|
+
export declare function pathToString({ pathname, search, hash, query }: PartialPath, basename?: string): string;
|
|
10
10
|
/**
|
|
11
11
|
* Parses a string URL path into its separate pathname, search, and hash components.
|
|
12
12
|
*/
|
|
13
13
|
export declare function resolvePath(to: PathRecord, fromPathname?: string): Path;
|
|
14
|
+
/**
|
|
15
|
+
* Strips off the base from the beginning of a location.pathname in a non-case-sensitive way.
|
|
16
|
+
* If the base does not match at the beginning, null will be returned.
|
|
17
|
+
*
|
|
18
|
+
* @param pathname - location.pathname
|
|
19
|
+
* @param base - base to strip off
|
|
20
|
+
*/
|
|
21
|
+
export declare function stripBase(pathname: string, base: string): string | null;
|
package/esm/utils/path.js
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import * as qs from 'query-string';
|
|
2
|
-
import { inBrowser } from './dom';
|
|
3
2
|
export const trimTrailingSlashes = (path) => path.replace(/\/+$/, '');
|
|
4
3
|
export const normalizeSlashes = (path) => path.replace(/\/\/+/g, '/');
|
|
5
4
|
export const joinPaths = (paths) => normalizeSlashes(paths.join('/'));
|
|
6
5
|
export const splitPath = (path) => normalizeSlashes(path).split('/');
|
|
7
6
|
export function normalizeBase(base) {
|
|
8
7
|
if (!base) {
|
|
9
|
-
|
|
10
|
-
// respect <base> tag
|
|
11
|
-
const baseEl = document.querySelector('base');
|
|
12
|
-
base = (baseEl && baseEl.getAttribute('href')) || '/';
|
|
13
|
-
// strip full URL origin
|
|
14
|
-
base = base.replace(/^https?:\/\/[^\/]+/, '');
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
base = '/';
|
|
18
|
-
}
|
|
8
|
+
base = '/';
|
|
19
9
|
}
|
|
20
10
|
// make sure there's the starting slash
|
|
21
11
|
if (base.charAt(0) !== '/') {
|
|
@@ -27,12 +17,16 @@ export function normalizeBase(base) {
|
|
|
27
17
|
export function parseQuery(queryStr) {
|
|
28
18
|
return qs.parse(queryStr);
|
|
29
19
|
}
|
|
30
|
-
export function pathToString({ pathname = '/', search = '', hash = '', query = {} }) {
|
|
20
|
+
export function pathToString({ pathname = '/', search = '', hash = '', query = {} }, basename) {
|
|
31
21
|
if (!search) {
|
|
32
22
|
const queryString = qs.stringify(query);
|
|
33
23
|
search = queryString ? `?${queryString}` : '';
|
|
34
24
|
}
|
|
35
|
-
|
|
25
|
+
const pathString = pathname + search + hash;
|
|
26
|
+
if (basename) {
|
|
27
|
+
return joinPaths([basename, pathString]);
|
|
28
|
+
}
|
|
29
|
+
return pathString;
|
|
36
30
|
}
|
|
37
31
|
function resolvePathname(toPathname, fromPathname) {
|
|
38
32
|
let segments = splitPath(trimTrailingSlashes(fromPathname));
|
|
@@ -99,3 +93,19 @@ export function resolvePath(to, fromPathname = '/') {
|
|
|
99
93
|
: fromPathname;
|
|
100
94
|
return parsedPath;
|
|
101
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Strips off the base from the beginning of a location.pathname in a non-case-sensitive way.
|
|
98
|
+
* If the base does not match at the beginning, null will be returned.
|
|
99
|
+
*
|
|
100
|
+
* @param pathname - location.pathname
|
|
101
|
+
* @param base - base to strip off
|
|
102
|
+
*/
|
|
103
|
+
export function stripBase(pathname, base) {
|
|
104
|
+
if (!base || base === '/')
|
|
105
|
+
return pathname;
|
|
106
|
+
// no base or base is not found at the beginning
|
|
107
|
+
if (!pathname.toLowerCase().startsWith(base.toLowerCase())) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
return pathname.slice(base.length) || '/';
|
|
111
|
+
}
|
package/lib/history/base.d.ts
CHANGED
|
@@ -38,13 +38,19 @@ export interface PushOptions {
|
|
|
38
38
|
redirectedFrom?: Path;
|
|
39
39
|
skipGuards?: boolean;
|
|
40
40
|
}
|
|
41
|
+
export declare type BaseHistoryOptions = {
|
|
42
|
+
basename?: string;
|
|
43
|
+
};
|
|
41
44
|
export default abstract class BaseHistory {
|
|
42
45
|
action: Action;
|
|
43
46
|
location: Location;
|
|
47
|
+
basename: string;
|
|
48
|
+
constructor({ basename }?: BaseHistoryOptions);
|
|
44
49
|
doTransition: (to: PathRecord, onComplete: Function, onAbort?: Function, skipGuards?: boolean, isReplace?: boolean, redirectedFrom?: Path) => void;
|
|
45
50
|
protected _index: number;
|
|
46
51
|
protected _blockers: import("../utils").Events<Blocker<State>>;
|
|
47
52
|
protected abstract getIndexAndLocation(): [number, Location];
|
|
53
|
+
/** setup will be called at `onTransition` and `onAbort` */
|
|
48
54
|
abstract setup(): void;
|
|
49
55
|
/**
|
|
50
56
|
* Jump to the specified route
|
package/lib/history/base.js
CHANGED
|
@@ -22,12 +22,13 @@ exports.ACTION_PUSH = 'PUSH';
|
|
|
22
22
|
*/
|
|
23
23
|
exports.ACTION_REPLACE = 'REPLACE';
|
|
24
24
|
class BaseHistory {
|
|
25
|
-
constructor() {
|
|
25
|
+
constructor({ basename = '' } = {}) {
|
|
26
26
|
this.action = exports.ACTION_POP;
|
|
27
27
|
this.location = (0, utils_1.createLocation)('/');
|
|
28
28
|
this.doTransition = () => void 0;
|
|
29
29
|
this._index = 0;
|
|
30
30
|
this._blockers = (0, utils_1.createEvents)();
|
|
31
|
+
this.basename = (0, utils_1.normalizeBase)(basename);
|
|
31
32
|
}
|
|
32
33
|
back() {
|
|
33
34
|
this.go(-1);
|
|
@@ -39,7 +40,7 @@ class BaseHistory {
|
|
|
39
40
|
const toPath = (0, utils_1.resolvePath)(to, from);
|
|
40
41
|
return {
|
|
41
42
|
path: toPath,
|
|
42
|
-
href: (0, utils_1.pathToString)(toPath)
|
|
43
|
+
href: (0, utils_1.pathToString)(toPath, this.basename)
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
transitionTo(to, { onTransition, onAbort, action = exports.ACTION_PUSH, state = null, redirectedFrom, skipGuards }) {
|
package/lib/history/browser.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PathRecord, Location, Blocker } from '../types';
|
|
2
|
-
import BaseHistory, { PushOptions } from './base';
|
|
2
|
+
import BaseHistory, { PushOptions, BaseHistoryOptions } from './base';
|
|
3
|
+
export declare type BrowserHistoryOptions = BaseHistoryOptions;
|
|
3
4
|
export default class BrowserHistory extends BaseHistory {
|
|
4
5
|
private _history;
|
|
5
|
-
constructor();
|
|
6
|
+
constructor({ basename }?: BrowserHistoryOptions);
|
|
6
7
|
push(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
7
8
|
replace(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
8
9
|
go(delta: number): void;
|
package/lib/history/browser.js
CHANGED
|
@@ -3,13 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const utils_1 = require("../utils");
|
|
4
4
|
const base_1 = require("./base");
|
|
5
5
|
class BrowserHistory extends base_1.default {
|
|
6
|
-
constructor() {
|
|
7
|
-
super();
|
|
6
|
+
constructor({ basename } = {}) {
|
|
7
|
+
super({ basename });
|
|
8
8
|
this._history = window.history;
|
|
9
9
|
[this._index, this.location] = this.getIndexAndLocation();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
// redirect immediately if
|
|
11
|
+
// 1. no index
|
|
12
|
+
// 2. we're not on the right url (redirectedFrom means url not match basename)
|
|
13
|
+
const { notMatchBasename } = this.location;
|
|
14
|
+
if (this._index == null || notMatchBasename) {
|
|
15
|
+
this._index = this._index || 0;
|
|
16
|
+
this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '', notMatchBasename ? this.resolve(this.location).href : undefined);
|
|
17
|
+
}
|
|
18
|
+
// recalculate location if not match basename
|
|
19
|
+
if (notMatchBasename) {
|
|
20
|
+
const state = this._history.state || {};
|
|
21
|
+
this.location = (0, utils_1.createLocation)(this.location, {
|
|
22
|
+
state: state.usr || null,
|
|
23
|
+
key: state.key || 'default'
|
|
24
|
+
});
|
|
13
25
|
}
|
|
14
26
|
}
|
|
15
27
|
push(to, { state, redirectedFrom } = {}) {
|
|
@@ -98,6 +110,7 @@ class BrowserHistory extends base_1.default {
|
|
|
98
110
|
search,
|
|
99
111
|
hash
|
|
100
112
|
}, {
|
|
113
|
+
basename: this.basename,
|
|
101
114
|
state: state.usr || null,
|
|
102
115
|
key: state.key || 'default'
|
|
103
116
|
})
|
package/lib/history/hash.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PathRecord, Location, Blocker, ResolvedPath } from '../types';
|
|
2
|
-
import BaseHistory, { PushOptions } from './base';
|
|
2
|
+
import BaseHistory, { PushOptions, BaseHistoryOptions } from './base';
|
|
3
|
+
export declare type HashHistoryOptions = BaseHistoryOptions;
|
|
3
4
|
export default class HashHistory extends BaseHistory {
|
|
4
5
|
private _history;
|
|
5
|
-
constructor();
|
|
6
|
+
constructor({ basename }?: HashHistoryOptions);
|
|
6
7
|
push(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
7
8
|
replace(to: PathRecord, { state, redirectedFrom }?: PushOptions): void;
|
|
8
9
|
go(delta: number): void;
|
package/lib/history/hash.js
CHANGED
|
@@ -2,29 +2,29 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const utils_1 = require("../utils");
|
|
4
4
|
const base_1 = require("./base");
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
let href = '';
|
|
8
|
-
if (base && base.getAttribute('href')) {
|
|
9
|
-
let url = window.location.href;
|
|
10
|
-
let hashIndex = url.indexOf('#');
|
|
11
|
-
href = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
12
|
-
}
|
|
13
|
-
return href;
|
|
14
|
-
}
|
|
15
|
-
function createHref(to) {
|
|
16
|
-
return (getBaseHref() +
|
|
17
|
-
'#' +
|
|
18
|
-
(typeof to === 'string' ? to : (0, utils_1.pathToString)((0, utils_1.resolvePath)(to))));
|
|
5
|
+
function createHref(to, basename) {
|
|
6
|
+
return '#' + (0, utils_1.pathToString)((0, utils_1.resolvePath)(to), basename);
|
|
19
7
|
}
|
|
20
8
|
class HashHistory extends base_1.default {
|
|
21
|
-
constructor() {
|
|
22
|
-
super();
|
|
9
|
+
constructor({ basename } = {}) {
|
|
10
|
+
super({ basename });
|
|
23
11
|
this._history = window.history;
|
|
24
12
|
[this._index, this.location] = this.getIndexAndLocation();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
// redirect immediately if
|
|
14
|
+
// 1. no index
|
|
15
|
+
// 2. we're not on the right url (redirectedFrom means url not match basename)
|
|
16
|
+
const { notMatchBasename } = this.location;
|
|
17
|
+
if (this._index == null || notMatchBasename) {
|
|
18
|
+
this._index = this._index || 0;
|
|
19
|
+
this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '', notMatchBasename ? this.resolve(this.location).href : undefined);
|
|
20
|
+
}
|
|
21
|
+
// recalculate location if not match basename
|
|
22
|
+
if (notMatchBasename) {
|
|
23
|
+
const state = this._history.state || {};
|
|
24
|
+
this.location = (0, utils_1.createLocation)(this.location, {
|
|
25
|
+
state: state.usr || null,
|
|
26
|
+
key: state.key || 'default'
|
|
27
|
+
});
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
push(to, { state, redirectedFrom } = {}) {
|
|
@@ -56,7 +56,7 @@ class HashHistory extends base_1.default {
|
|
|
56
56
|
const toPath = (0, utils_1.resolvePath)(to, from);
|
|
57
57
|
return {
|
|
58
58
|
path: toPath,
|
|
59
|
-
href: createHref(toPath)
|
|
59
|
+
href: createHref(toPath, this.basename)
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
setup() {
|
|
@@ -129,6 +129,7 @@ class HashHistory extends base_1.default {
|
|
|
129
129
|
search,
|
|
130
130
|
hash
|
|
131
131
|
}, {
|
|
132
|
+
basename: this.basename,
|
|
132
133
|
state: state.usr || null,
|
|
133
134
|
key: state.key || 'default'
|
|
134
135
|
})
|
package/lib/history/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import MemoryHistory, { MemoryHistoryOptions, InitialEntry } from './memory';
|
|
2
|
-
import BrowserHistory from './browser';
|
|
3
|
-
import HashHistory from './hash';
|
|
2
|
+
import BrowserHistory, { BrowserHistoryOptions } from './browser';
|
|
3
|
+
import HashHistory, { HashHistoryOptions } from './hash';
|
|
4
4
|
export * from './base';
|
|
5
5
|
export { MemoryHistory, MemoryHistoryOptions, InitialEntry };
|
|
6
|
-
export declare function createBrowserHistory(): BrowserHistory;
|
|
7
|
-
export declare function createHashHistory(): HashHistory;
|
|
6
|
+
export declare function createBrowserHistory(options?: BrowserHistoryOptions): BrowserHistory;
|
|
7
|
+
export declare function createHashHistory(options?: HashHistoryOptions): HashHistory;
|
|
8
8
|
export declare function createMemoryHistory(options?: MemoryHistoryOptions): MemoryHistory;
|
package/lib/history/index.js
CHANGED
|
@@ -20,12 +20,12 @@ exports.MemoryHistory = memory_1.default;
|
|
|
20
20
|
const browser_1 = require("./browser");
|
|
21
21
|
const hash_1 = require("./hash");
|
|
22
22
|
__exportStar(require("./base"), exports);
|
|
23
|
-
function createBrowserHistory() {
|
|
24
|
-
return new browser_1.default();
|
|
23
|
+
function createBrowserHistory(options = {}) {
|
|
24
|
+
return new browser_1.default(options);
|
|
25
25
|
}
|
|
26
26
|
exports.createBrowserHistory = createBrowserHistory;
|
|
27
|
-
function createHashHistory() {
|
|
28
|
-
return new hash_1.default();
|
|
27
|
+
function createHashHistory(options = {}) {
|
|
28
|
+
return new hash_1.default(options);
|
|
29
29
|
}
|
|
30
30
|
exports.createHashHistory = createHashHistory;
|
|
31
31
|
function createMemoryHistory(options = {}) {
|
package/lib/history/memory.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PathRecord, Location, Blocker, PartialLocation
|
|
1
|
+
import { PathRecord, Location, Blocker, PartialLocation } from '../types';
|
|
2
2
|
import BaseHistory, { PushOptions } from './base';
|
|
3
3
|
/**
|
|
4
4
|
* A user-supplied object that describes a location. Used when providing
|
|
@@ -8,15 +8,15 @@ export declare type InitialEntry = string | PartialLocation;
|
|
|
8
8
|
export declare type MemoryHistoryOptions = {
|
|
9
9
|
initialEntries?: InitialEntry[];
|
|
10
10
|
initialIndex?: number;
|
|
11
|
+
basename?: string;
|
|
11
12
|
};
|
|
12
13
|
export default class MemoryHistory extends BaseHistory {
|
|
13
14
|
private _entries;
|
|
14
|
-
constructor({ initialEntries, initialIndex }?: MemoryHistoryOptions);
|
|
15
|
+
constructor({ initialEntries, initialIndex, basename }?: MemoryHistoryOptions);
|
|
15
16
|
setup(): void;
|
|
16
17
|
push(to: PathRecord, { state, redirectedFrom, skipGuards }?: PushOptions): void;
|
|
17
18
|
replace(to: PathRecord, { state, redirectedFrom, skipGuards }?: PushOptions): void;
|
|
18
19
|
go(delta: number): void;
|
|
19
20
|
block(blocker: Blocker): () => void;
|
|
20
|
-
resolve(to: PathRecord, from?: string): ResolvedPath;
|
|
21
21
|
protected getIndexAndLocation(): [number, Location];
|
|
22
22
|
}
|
package/lib/history/memory.js
CHANGED
|
@@ -6,11 +6,11 @@ function clamp(n, lowerBound, upperBound) {
|
|
|
6
6
|
return Math.min(Math.max(n, lowerBound), upperBound);
|
|
7
7
|
}
|
|
8
8
|
class MemoryHistory extends base_1.default {
|
|
9
|
-
constructor({ initialEntries = ['/'], initialIndex } = {}) {
|
|
10
|
-
super();
|
|
9
|
+
constructor({ initialEntries = ['/'], initialIndex, basename = '' } = {}) {
|
|
10
|
+
super({ basename });
|
|
11
11
|
this._entries = [];
|
|
12
12
|
this._entries = initialEntries.map(entry => {
|
|
13
|
-
let location = (0, utils_1.createLocation)(Object.assign({ pathname: '/', search: '', hash: '' }, (typeof entry === 'string' ? (0, utils_1.resolvePath)(entry) : entry)));
|
|
13
|
+
let location = (0, utils_1.createLocation)(Object.assign({ pathname: '/', search: '', hash: '' }, (typeof entry === 'string' ? (0, utils_1.resolvePath)(entry) : entry)), { basename: this.basename });
|
|
14
14
|
(0, utils_1.warning)(location.pathname.charAt(0) === '/', `Relative pathnames are not supported in createMemoryHistory({ initialEntries }) (invalid entry: ${JSON.stringify(entry)})`);
|
|
15
15
|
return location;
|
|
16
16
|
});
|
|
@@ -65,13 +65,6 @@ class MemoryHistory extends base_1.default {
|
|
|
65
65
|
block(blocker) {
|
|
66
66
|
return this._blockers.push(blocker);
|
|
67
67
|
}
|
|
68
|
-
resolve(to, from) {
|
|
69
|
-
const toPath = (0, utils_1.resolvePath)(to, from);
|
|
70
|
-
return {
|
|
71
|
-
path: toPath,
|
|
72
|
-
href: (0, utils_1.pathToString)(toPath)
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
68
|
getIndexAndLocation() {
|
|
76
69
|
const index = this._index;
|
|
77
70
|
return [index, this._entries[index]];
|
package/lib/matchRoutes.js
CHANGED
|
@@ -71,12 +71,12 @@ function matchRoutes(routes, location, basename = '') {
|
|
|
71
71
|
}
|
|
72
72
|
let pathname = location.pathname || '/';
|
|
73
73
|
if (basename) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
const normalizedBasename = (0, utils_1.normalizeBase)(basename);
|
|
75
|
+
const pathnameWithoutBase = (0, utils_1.stripBase)(pathname, normalizedBasename);
|
|
76
|
+
if (pathnameWithoutBase) {
|
|
77
|
+
pathname = pathnameWithoutBase;
|
|
77
78
|
}
|
|
78
79
|
else {
|
|
79
|
-
// Pathname does not start with the basename, no match.
|
|
80
80
|
return null;
|
|
81
81
|
}
|
|
82
82
|
}
|
package/lib/router.d.ts
CHANGED
package/lib/router.js
CHANGED
|
@@ -20,7 +20,7 @@ const START = {
|
|
|
20
20
|
redirected: false
|
|
21
21
|
};
|
|
22
22
|
class Router {
|
|
23
|
-
constructor({
|
|
23
|
+
constructor({ history, routes }) {
|
|
24
24
|
this._pending = null;
|
|
25
25
|
this._cancleHandler = null;
|
|
26
26
|
this._ready = false;
|
|
@@ -31,9 +31,14 @@ class Router {
|
|
|
31
31
|
this._afterEachs = (0, utils_1.createEvents)();
|
|
32
32
|
this.init = () => {
|
|
33
33
|
const setup = () => this._history.setup();
|
|
34
|
-
this.
|
|
34
|
+
const current = this._getCurrent();
|
|
35
|
+
this._history.transitionTo(current, {
|
|
35
36
|
onTransition: setup,
|
|
36
|
-
onAbort: setup
|
|
37
|
+
onAbort: setup,
|
|
38
|
+
// current.redirected means the initial url does not match basename and should redirect
|
|
39
|
+
// so we just skip all guards
|
|
40
|
+
// this logic only applies to memory history
|
|
41
|
+
skipGuards: Boolean(current.redirected)
|
|
37
42
|
});
|
|
38
43
|
return this;
|
|
39
44
|
};
|
|
@@ -68,11 +73,11 @@ class Router {
|
|
|
68
73
|
return this._afterEachs.push(listener);
|
|
69
74
|
};
|
|
70
75
|
this.resolve = (to, from) => {
|
|
71
|
-
return this._history.resolve(to, from
|
|
76
|
+
return this._history.resolve(to, from);
|
|
72
77
|
};
|
|
73
78
|
this.match = (to) => {
|
|
74
|
-
const { _routes: routes
|
|
75
|
-
const matches = (0, matchRoutes_1.matchRoutes)(routes, to
|
|
79
|
+
const { _routes: routes } = this;
|
|
80
|
+
const matches = (0, matchRoutes_1.matchRoutes)(routes, to);
|
|
76
81
|
return matches || [];
|
|
77
82
|
};
|
|
78
83
|
this.replaceRoutes = (routes) => {
|
|
@@ -97,7 +102,6 @@ class Router {
|
|
|
97
102
|
onAbort: setup
|
|
98
103
|
});
|
|
99
104
|
};
|
|
100
|
-
this._basename = (0, utils_1.normalizeBase)(basename);
|
|
101
105
|
this._history = history;
|
|
102
106
|
this._routes = (0, createRoutesFromArray_1.createRoutesFromArray)(routes);
|
|
103
107
|
this._current = START;
|
|
@@ -115,6 +119,9 @@ class Router {
|
|
|
115
119
|
get action() {
|
|
116
120
|
return this._history.action;
|
|
117
121
|
}
|
|
122
|
+
get basename() {
|
|
123
|
+
return this._history.basename;
|
|
124
|
+
}
|
|
118
125
|
/*
|
|
119
126
|
The Full Navigation Resolution Flow for shuvi/router
|
|
120
127
|
1. Navigation triggered.
|
|
@@ -258,7 +265,7 @@ class Router {
|
|
|
258
265
|
hash: location.hash,
|
|
259
266
|
query: location.query,
|
|
260
267
|
state: location.state,
|
|
261
|
-
redirected:
|
|
268
|
+
redirected: Boolean(location.redirectedFrom) || location.notMatchBasename,
|
|
262
269
|
key: location.key
|
|
263
270
|
};
|
|
264
271
|
}
|
package/lib/types/history.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ export interface PartialPath {
|
|
|
96
96
|
*/
|
|
97
97
|
export interface Location<S extends State = State> extends Path {
|
|
98
98
|
redirectedFrom?: Path;
|
|
99
|
+
notMatchBasename?: boolean;
|
|
99
100
|
/**
|
|
100
101
|
* An object of arbitrary data associated with this location.
|
|
101
102
|
*
|
|
@@ -114,6 +115,7 @@ export interface Location<S extends State = State> extends Path {
|
|
|
114
115
|
* A partial Location object that may be missing some properties.
|
|
115
116
|
*/
|
|
116
117
|
export interface PartialLocation<S extends State = State> extends PartialPath {
|
|
118
|
+
redirectedFrom?: Path;
|
|
117
119
|
/**
|
|
118
120
|
* An object of arbitrary data associated with this location.
|
|
119
121
|
*
|
package/lib/types/router.d.ts
CHANGED
package/lib/utils/history.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HistoryState, Location, PathRecord, State, Key, Blocker, Path } from '../types';
|
|
2
2
|
import { Events } from './misc';
|
|
3
|
-
export declare function createLocation(to: PathRecord, { state, key, redirectedFrom }?: {
|
|
3
|
+
export declare function createLocation(to: PathRecord, { basename, state, key, redirectedFrom }?: {
|
|
4
|
+
basename?: string;
|
|
4
5
|
state?: State;
|
|
5
6
|
key?: Key;
|
|
6
7
|
redirectedFrom?: Path;
|
package/lib/utils/history.js
CHANGED
|
@@ -13,8 +13,15 @@ function promptBeforeUnload(event) {
|
|
|
13
13
|
function createKey() {
|
|
14
14
|
return Math.random().toString(36).substr(2, 8);
|
|
15
15
|
}
|
|
16
|
-
function createLocation(to, { state = null, key, redirectedFrom } = {}) {
|
|
17
|
-
|
|
16
|
+
function createLocation(to, { basename, state = null, key, redirectedFrom } = {}) {
|
|
17
|
+
const resolved = (0, path_1.resolvePath)(to);
|
|
18
|
+
const pathnameWithoutBase = (0, path_1.stripBase)(resolved.pathname, basename || '/');
|
|
19
|
+
if (pathnameWithoutBase) {
|
|
20
|
+
resolved.pathname = pathnameWithoutBase;
|
|
21
|
+
}
|
|
22
|
+
const notMatchBasename = Boolean(basename) && !pathnameWithoutBase;
|
|
23
|
+
return (0, misc_1.readOnly)(Object.assign(Object.assign({}, resolved), { redirectedFrom,
|
|
24
|
+
notMatchBasename,
|
|
18
25
|
state, key: key || createKey() }));
|
|
19
26
|
}
|
|
20
27
|
exports.createLocation = createLocation;
|
package/lib/utils/path.d.ts
CHANGED
|
@@ -6,8 +6,16 @@ export declare const joinPaths: (paths: string[]) => string;
|
|
|
6
6
|
export declare const splitPath: (path: string) => string[];
|
|
7
7
|
export declare function normalizeBase(base: string): string;
|
|
8
8
|
export declare function parseQuery(queryStr: string): qs.ParsedQuery<string>;
|
|
9
|
-
export declare function pathToString({ pathname, search, hash, query }: PartialPath): string;
|
|
9
|
+
export declare function pathToString({ pathname, search, hash, query }: PartialPath, basename?: string): string;
|
|
10
10
|
/**
|
|
11
11
|
* Parses a string URL path into its separate pathname, search, and hash components.
|
|
12
12
|
*/
|
|
13
13
|
export declare function resolvePath(to: PathRecord, fromPathname?: string): Path;
|
|
14
|
+
/**
|
|
15
|
+
* Strips off the base from the beginning of a location.pathname in a non-case-sensitive way.
|
|
16
|
+
* If the base does not match at the beginning, null will be returned.
|
|
17
|
+
*
|
|
18
|
+
* @param pathname - location.pathname
|
|
19
|
+
* @param base - base to strip off
|
|
20
|
+
*/
|
|
21
|
+
export declare function stripBase(pathname: string, base: string): string | null;
|
package/lib/utils/path.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolvePath = exports.pathToString = exports.parseQuery = exports.normalizeBase = exports.splitPath = exports.joinPaths = exports.normalizeSlashes = exports.trimTrailingSlashes = void 0;
|
|
3
|
+
exports.stripBase = exports.resolvePath = exports.pathToString = exports.parseQuery = exports.normalizeBase = exports.splitPath = exports.joinPaths = exports.normalizeSlashes = exports.trimTrailingSlashes = void 0;
|
|
4
4
|
const qs = require("query-string");
|
|
5
|
-
const dom_1 = require("./dom");
|
|
6
5
|
const trimTrailingSlashes = (path) => path.replace(/\/+$/, '');
|
|
7
6
|
exports.trimTrailingSlashes = trimTrailingSlashes;
|
|
8
7
|
const normalizeSlashes = (path) => path.replace(/\/\/+/g, '/');
|
|
@@ -13,16 +12,7 @@ const splitPath = (path) => (0, exports.normalizeSlashes)(path).split('/');
|
|
|
13
12
|
exports.splitPath = splitPath;
|
|
14
13
|
function normalizeBase(base) {
|
|
15
14
|
if (!base) {
|
|
16
|
-
|
|
17
|
-
// respect <base> tag
|
|
18
|
-
const baseEl = document.querySelector('base');
|
|
19
|
-
base = (baseEl && baseEl.getAttribute('href')) || '/';
|
|
20
|
-
// strip full URL origin
|
|
21
|
-
base = base.replace(/^https?:\/\/[^\/]+/, '');
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
base = '/';
|
|
25
|
-
}
|
|
15
|
+
base = '/';
|
|
26
16
|
}
|
|
27
17
|
// make sure there's the starting slash
|
|
28
18
|
if (base.charAt(0) !== '/') {
|
|
@@ -36,12 +26,16 @@ function parseQuery(queryStr) {
|
|
|
36
26
|
return qs.parse(queryStr);
|
|
37
27
|
}
|
|
38
28
|
exports.parseQuery = parseQuery;
|
|
39
|
-
function pathToString({ pathname = '/', search = '', hash = '', query = {} }) {
|
|
29
|
+
function pathToString({ pathname = '/', search = '', hash = '', query = {} }, basename) {
|
|
40
30
|
if (!search) {
|
|
41
31
|
const queryString = qs.stringify(query);
|
|
42
32
|
search = queryString ? `?${queryString}` : '';
|
|
43
33
|
}
|
|
44
|
-
|
|
34
|
+
const pathString = pathname + search + hash;
|
|
35
|
+
if (basename) {
|
|
36
|
+
return (0, exports.joinPaths)([basename, pathString]);
|
|
37
|
+
}
|
|
38
|
+
return pathString;
|
|
45
39
|
}
|
|
46
40
|
exports.pathToString = pathToString;
|
|
47
41
|
function resolvePathname(toPathname, fromPathname) {
|
|
@@ -110,3 +104,20 @@ function resolvePath(to, fromPathname = '/') {
|
|
|
110
104
|
return parsedPath;
|
|
111
105
|
}
|
|
112
106
|
exports.resolvePath = resolvePath;
|
|
107
|
+
/**
|
|
108
|
+
* Strips off the base from the beginning of a location.pathname in a non-case-sensitive way.
|
|
109
|
+
* If the base does not match at the beginning, null will be returned.
|
|
110
|
+
*
|
|
111
|
+
* @param pathname - location.pathname
|
|
112
|
+
* @param base - base to strip off
|
|
113
|
+
*/
|
|
114
|
+
function stripBase(pathname, base) {
|
|
115
|
+
if (!base || base === '/')
|
|
116
|
+
return pathname;
|
|
117
|
+
// no base or base is not found at the beginning
|
|
118
|
+
if (!pathname.toLowerCase().startsWith(base.toLowerCase())) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return pathname.slice(base.length) || '/';
|
|
122
|
+
}
|
|
123
|
+
exports.stripBase = stripBase;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shuvi/router",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.45",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/shuvijs/shuvi.git",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">= 16.0.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@shuvi/utils": "1.0.
|
|
31
|
+
"@shuvi/utils": "1.0.45",
|
|
32
32
|
"query-string": "6.13.8"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/esm/utils/dom.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const inBrowser: boolean;
|
package/esm/utils/dom.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const inBrowser = typeof window !== 'undefined';
|
package/lib/utils/dom.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const inBrowser: boolean;
|
package/lib/utils/dom.js
DELETED