(opts: {
getLocation: () => HistoryLocation
getLength: () => number
pushState: (path: string, state: any) => void
replaceState: (path: string, state: any) => void
go: (n: number) => void
back: (ignoreBlocker: boolean) => void
forward: (ignoreBlocker: boolean) => void
createHref: (path: string) => string
flush?: () => void
destroy?: () => void
onBlocked?: () => void
getBlockers?: () => Array<NavigationBlocker>
setBlockers?: (blockers: Array<NavigationBlocker>) => void
// Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event
notifyOnIndexChange?: boolean
})
| 97 | const beforeUnloadEvent = 'beforeunload' |
| 98 | |
| 99 | export function createHistory(opts: { |
| 100 | getLocation: () => HistoryLocation |
| 101 | getLength: () => number |
| 102 | pushState: (path: string, state: any) => void |
| 103 | replaceState: (path: string, state: any) => void |
| 104 | go: (n: number) => void |
| 105 | back: (ignoreBlocker: boolean) => void |
| 106 | forward: (ignoreBlocker: boolean) => void |
| 107 | createHref: (path: string) => string |
| 108 | flush?: () => void |
| 109 | destroy?: () => void |
| 110 | onBlocked?: () => void |
| 111 | getBlockers?: () => Array<NavigationBlocker> |
| 112 | setBlockers?: (blockers: Array<NavigationBlocker>) => void |
| 113 | // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event |
| 114 | notifyOnIndexChange?: boolean |
| 115 | }): RouterHistory { |
| 116 | let location = opts.getLocation() |
| 117 | const subscribers = new Set<(opts: SubscriberArgs) => void>() |
| 118 | |
| 119 | const notify = (action: SubscriberHistoryAction) => { |
| 120 | location = opts.getLocation() |
| 121 | subscribers.forEach((subscriber) => subscriber({ location, action })) |
| 122 | } |
| 123 | |
| 124 | const handleIndexChange = (action: SubscriberHistoryAction) => { |
| 125 | if (opts.notifyOnIndexChange ?? true) notify(action) |
| 126 | else location = opts.getLocation() |
| 127 | } |
| 128 | |
| 129 | const tryNavigation = async ({ |
| 130 | task, |
| 131 | navigateOpts, |
| 132 | ...actionInfo |
| 133 | }: TryNavigateArgs) => { |
| 134 | const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false |
| 135 | if (ignoreBlocker) { |
| 136 | task() |
| 137 | return |
| 138 | } |
| 139 | |
| 140 | const blockers = opts.getBlockers?.() ?? [] |
| 141 | const isPushOrReplace = |
| 142 | actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE' |
| 143 | if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) { |
| 144 | for (const blocker of blockers) { |
| 145 | const nextLocation = parseHref(actionInfo.path, actionInfo.state) |
| 146 | const isBlocked = await blocker.blockerFn({ |
| 147 | currentLocation: location, |
| 148 | nextLocation, |
| 149 | action: actionInfo.type, |
| 150 | }) |
| 151 | if (isBlocked) { |
| 152 | opts.onBlocked?.() |
| 153 | return |
| 154 | } |
| 155 | } |
| 156 | } |
no test coverage detected