@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.
Files changed (43) hide show
  1. package/esm/history/base.d.ts +6 -0
  2. package/esm/history/base.js +4 -3
  3. package/esm/history/browser.d.ts +3 -2
  4. package/esm/history/browser.js +18 -5
  5. package/esm/history/hash.d.ts +3 -2
  6. package/esm/history/hash.js +21 -20
  7. package/esm/history/index.d.ts +4 -4
  8. package/esm/history/index.js +4 -4
  9. package/esm/history/memory.d.ts +3 -3
  10. package/esm/history/memory.js +4 -11
  11. package/esm/matchRoutes.js +5 -5
  12. package/esm/router.d.ts +0 -1
  13. package/esm/router.js +16 -9
  14. package/esm/types/history.d.ts +2 -0
  15. package/esm/types/router.d.ts +1 -0
  16. package/esm/utils/history.d.ts +2 -1
  17. package/esm/utils/history.js +10 -3
  18. package/esm/utils/path.d.ts +9 -1
  19. package/esm/utils/path.js +23 -13
  20. package/lib/history/base.d.ts +6 -0
  21. package/lib/history/base.js +3 -2
  22. package/lib/history/browser.d.ts +3 -2
  23. package/lib/history/browser.js +18 -5
  24. package/lib/history/hash.d.ts +3 -2
  25. package/lib/history/hash.js +21 -20
  26. package/lib/history/index.d.ts +4 -4
  27. package/lib/history/index.js +4 -4
  28. package/lib/history/memory.d.ts +3 -3
  29. package/lib/history/memory.js +3 -10
  30. package/lib/matchRoutes.js +4 -4
  31. package/lib/router.d.ts +0 -1
  32. package/lib/router.js +15 -8
  33. package/lib/types/history.d.ts +2 -0
  34. package/lib/types/router.d.ts +1 -0
  35. package/lib/utils/history.d.ts +2 -1
  36. package/lib/utils/history.js +9 -2
  37. package/lib/utils/path.d.ts +9 -1
  38. package/lib/utils/path.js +25 -14
  39. package/package.json +2 -2
  40. package/esm/utils/dom.d.ts +0 -1
  41. package/esm/utils/dom.js +0 -1
  42. package/lib/utils/dom.d.ts +0 -1
  43. package/lib/utils/dom.js +0 -4
@@ -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
@@ -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 }) {
@@ -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;
@@ -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
- if (this._index == null) {
9
- this._index = 0;
10
- this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '');
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
  })
@@ -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;
@@ -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 getBaseHref() {
4
- let base = document.querySelector('base');
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
- if (this._index == null) {
24
- this._index = 0;
25
- this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '');
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
  })
@@ -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;
@@ -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);
@@ -1,4 +1,4 @@
1
- import { PathRecord, Location, Blocker, PartialLocation, ResolvedPath } from '../types';
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
  }
@@ -1,14 +1,14 @@
1
- import { createLocation, resolvePath, pathToString, warning } from '../utils';
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]];
@@ -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
- let base = basename.replace(/^\/*/, '/').replace(/\/+$/, '');
71
- if (pathname.startsWith(base)) {
72
- pathname = pathname === base ? '/' : pathname.slice(base.length);
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
@@ -4,7 +4,6 @@ interface IRouterOptions<RouteRecord extends IPartialRouteRecord> {
4
4
  history: History;
5
5
  routes: RouteRecord[];
6
6
  caseSensitive?: boolean;
7
- basename?: string;
8
7
  }
9
8
  export declare const createRouter: <RouteRecord extends {
10
9
  path: string;
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 { normalizeBase, joinPaths, createEvents, resolvePath } from './utils';
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({ basename = '', history, routes }) {
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._history.transitionTo(this._getCurrent(), {
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 ? joinPaths([this._basename, from]) : this._basename);
73
+ return this._history.resolve(to, from);
69
74
  };
70
75
  this.match = (to) => {
71
- const { _routes: routes, _basename: basename } = this;
72
- const matches = matchRoutes(routes, to, basename);
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: !!location.redirectedFrom,
265
+ redirected: Boolean(location.redirectedFrom) || location.notMatchBasename,
259
266
  key: location.key
260
267
  };
261
268
  }
@@ -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
  *
@@ -63,6 +63,7 @@ export interface IRouter<RouteRecord extends {
63
63
  } = any> {
64
64
  current: IRoute<RouteRecord>;
65
65
  action: History['action'];
66
+ basename: string;
66
67
  push(to: PathRecord, state?: any): void;
67
68
  replace(to: PathRecord, state?: any): void;
68
69
  go: History['go'];
@@ -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;
@@ -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
- return readOnly(Object.assign(Object.assign({}, resolvePath(to)), { redirectedFrom,
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 } = {}) {
@@ -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
- if (inBrowser) {
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
- return pathname + search + hash;
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
+ }
@@ -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
@@ -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 }) {
@@ -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;
@@ -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
- if (this._index == null) {
11
- this._index = 0;
12
- this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '');
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
  })
@@ -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;
@@ -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 getBaseHref() {
6
- let base = document.querySelector('base');
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
- if (this._index == null) {
26
- this._index = 0;
27
- this._history.replaceState(Object.assign(Object.assign({}, this._history.state), { idx: this._index }), '');
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
  })
@@ -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;
@@ -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 = {}) {
@@ -1,4 +1,4 @@
1
- import { PathRecord, Location, Blocker, PartialLocation, ResolvedPath } from '../types';
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
  }
@@ -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]];
@@ -71,12 +71,12 @@ function matchRoutes(routes, location, basename = '') {
71
71
  }
72
72
  let pathname = location.pathname || '/';
73
73
  if (basename) {
74
- let base = basename.replace(/^\/*/, '/').replace(/\/+$/, '');
75
- if (pathname.startsWith(base)) {
76
- pathname = pathname === base ? '/' : pathname.slice(base.length);
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
@@ -4,7 +4,6 @@ interface IRouterOptions<RouteRecord extends IPartialRouteRecord> {
4
4
  history: History;
5
5
  routes: RouteRecord[];
6
6
  caseSensitive?: boolean;
7
- basename?: string;
8
7
  }
9
8
  export declare const createRouter: <RouteRecord extends {
10
9
  path: string;
package/lib/router.js CHANGED
@@ -20,7 +20,7 @@ const START = {
20
20
  redirected: false
21
21
  };
22
22
  class Router {
23
- constructor({ basename = '', history, routes }) {
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._history.transitionTo(this._getCurrent(), {
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 ? (0, utils_1.joinPaths)([this._basename, from]) : this._basename);
76
+ return this._history.resolve(to, from);
72
77
  };
73
78
  this.match = (to) => {
74
- const { _routes: routes, _basename: basename } = this;
75
- const matches = (0, matchRoutes_1.matchRoutes)(routes, to, basename);
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: !!location.redirectedFrom,
268
+ redirected: Boolean(location.redirectedFrom) || location.notMatchBasename,
262
269
  key: location.key
263
270
  };
264
271
  }
@@ -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
  *
@@ -63,6 +63,7 @@ export interface IRouter<RouteRecord extends {
63
63
  } = any> {
64
64
  current: IRoute<RouteRecord>;
65
65
  action: History['action'];
66
+ basename: string;
66
67
  push(to: PathRecord, state?: any): void;
67
68
  replace(to: PathRecord, state?: any): void;
68
69
  go: History['go'];
@@ -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;
@@ -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
- return (0, misc_1.readOnly)(Object.assign(Object.assign({}, (0, path_1.resolvePath)(to)), { redirectedFrom,
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;
@@ -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
- if (dom_1.inBrowser) {
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
- return pathname + search + hash;
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.44",
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.44",
31
+ "@shuvi/utils": "1.0.45",
32
32
  "query-string": "6.13.8"
33
33
  }
34
34
  }
@@ -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';
@@ -1 +0,0 @@
1
- export declare const inBrowser: boolean;
package/lib/utils/dom.js DELETED
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.inBrowser = void 0;
4
- exports.inBrowser = typeof window !== 'undefined';