2566 lines
85 KiB
JavaScript
2566 lines
85 KiB
JavaScript
import {
|
|
setupDevtoolsPlugin
|
|
} from "./chunk-YFT6OQ5R.js";
|
|
import {
|
|
computed,
|
|
defineComponent,
|
|
getCurrentInstance,
|
|
h,
|
|
inject,
|
|
nextTick,
|
|
onActivated,
|
|
onDeactivated,
|
|
onUnmounted,
|
|
provide,
|
|
reactive,
|
|
ref,
|
|
shallowRef,
|
|
unref,
|
|
watch,
|
|
watchEffect
|
|
} from "./chunk-TF6X5W6F.js";
|
|
import "./chunk-G3PMV62Z.js";
|
|
|
|
// node_modules/vue-router/dist/vue-router.esm-bundler.js
|
|
var hasSymbol = typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol";
|
|
var PolySymbol = (name) => (
|
|
// vr = vue router
|
|
hasSymbol ? Symbol(true ? "[vue-router]: " + name : name) : (true ? "[vue-router]: " : "_vr_") + name
|
|
);
|
|
var matchedRouteKey = PolySymbol(true ? "router view location matched" : "rvlm");
|
|
var viewDepthKey = PolySymbol(true ? "router view depth" : "rvd");
|
|
var routerKey = PolySymbol(true ? "router" : "r");
|
|
var routeLocationKey = PolySymbol(true ? "route location" : "rl");
|
|
var routerViewLocationKey = PolySymbol(true ? "router view location" : "rvl");
|
|
var isBrowser = typeof window !== "undefined";
|
|
function isESModule(obj) {
|
|
return obj.__esModule || hasSymbol && obj[Symbol.toStringTag] === "Module";
|
|
}
|
|
var assign = Object.assign;
|
|
function applyToParams(fn, params) {
|
|
const newParams = {};
|
|
for (const key in params) {
|
|
const value = params[key];
|
|
newParams[key] = Array.isArray(value) ? value.map(fn) : fn(value);
|
|
}
|
|
return newParams;
|
|
}
|
|
var noop = () => {
|
|
};
|
|
function warn(msg) {
|
|
const args = Array.from(arguments).slice(1);
|
|
console.warn.apply(console, ["[Vue Router warn]: " + msg].concat(args));
|
|
}
|
|
var TRAILING_SLASH_RE = /\/$/;
|
|
var removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, "");
|
|
function parseURL(parseQuery2, location2, currentLocation = "/") {
|
|
let path, query = {}, searchString = "", hash = "";
|
|
const searchPos = location2.indexOf("?");
|
|
const hashPos = location2.indexOf("#", searchPos > -1 ? searchPos : 0);
|
|
if (searchPos > -1) {
|
|
path = location2.slice(0, searchPos);
|
|
searchString = location2.slice(searchPos + 1, hashPos > -1 ? hashPos : location2.length);
|
|
query = parseQuery2(searchString);
|
|
}
|
|
if (hashPos > -1) {
|
|
path = path || location2.slice(0, hashPos);
|
|
hash = location2.slice(hashPos, location2.length);
|
|
}
|
|
path = resolveRelativePath(path != null ? path : location2, currentLocation);
|
|
return {
|
|
fullPath: path + (searchString && "?") + searchString + hash,
|
|
path,
|
|
query,
|
|
hash
|
|
};
|
|
}
|
|
function stringifyURL(stringifyQuery2, location2) {
|
|
const query = location2.query ? stringifyQuery2(location2.query) : "";
|
|
return location2.path + (query && "?") + query + (location2.hash || "");
|
|
}
|
|
function stripBase(pathname, base) {
|
|
if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase()))
|
|
return pathname;
|
|
return pathname.slice(base.length) || "/";
|
|
}
|
|
function isSameRouteLocation(stringifyQuery2, a, b) {
|
|
const aLastIndex = a.matched.length - 1;
|
|
const bLastIndex = b.matched.length - 1;
|
|
return aLastIndex > -1 && aLastIndex === bLastIndex && isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) && isSameRouteLocationParams(a.params, b.params) && stringifyQuery2(a.query) === stringifyQuery2(b.query) && a.hash === b.hash;
|
|
}
|
|
function isSameRouteRecord(a, b) {
|
|
return (a.aliasOf || a) === (b.aliasOf || b);
|
|
}
|
|
function isSameRouteLocationParams(a, b) {
|
|
if (Object.keys(a).length !== Object.keys(b).length)
|
|
return false;
|
|
for (const key in a) {
|
|
if (!isSameRouteLocationParamsValue(a[key], b[key]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function isSameRouteLocationParamsValue(a, b) {
|
|
return Array.isArray(a) ? isEquivalentArray(a, b) : Array.isArray(b) ? isEquivalentArray(b, a) : a === b;
|
|
}
|
|
function isEquivalentArray(a, b) {
|
|
return Array.isArray(b) ? a.length === b.length && a.every((value, i) => value === b[i]) : a.length === 1 && a[0] === b;
|
|
}
|
|
function resolveRelativePath(to, from) {
|
|
if (to.startsWith("/"))
|
|
return to;
|
|
if (!from.startsWith("/")) {
|
|
warn(`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`);
|
|
return to;
|
|
}
|
|
if (!to)
|
|
return from;
|
|
const fromSegments = from.split("/");
|
|
const toSegments = to.split("/");
|
|
let position = fromSegments.length - 1;
|
|
let toPosition;
|
|
let segment;
|
|
for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
|
|
segment = toSegments[toPosition];
|
|
if (position === 1 || segment === ".")
|
|
continue;
|
|
if (segment === "..")
|
|
position--;
|
|
else
|
|
break;
|
|
}
|
|
return fromSegments.slice(0, position).join("/") + "/" + toSegments.slice(toPosition - (toPosition === toSegments.length ? 1 : 0)).join("/");
|
|
}
|
|
var NavigationType;
|
|
(function(NavigationType2) {
|
|
NavigationType2["pop"] = "pop";
|
|
NavigationType2["push"] = "push";
|
|
})(NavigationType || (NavigationType = {}));
|
|
var NavigationDirection;
|
|
(function(NavigationDirection2) {
|
|
NavigationDirection2["back"] = "back";
|
|
NavigationDirection2["forward"] = "forward";
|
|
NavigationDirection2["unknown"] = "";
|
|
})(NavigationDirection || (NavigationDirection = {}));
|
|
var START = "";
|
|
function normalizeBase(base) {
|
|
if (!base) {
|
|
if (isBrowser) {
|
|
const baseEl = document.querySelector("base");
|
|
base = baseEl && baseEl.getAttribute("href") || "/";
|
|
base = base.replace(/^\w+:\/\/[^\/]+/, "");
|
|
} else {
|
|
base = "/";
|
|
}
|
|
}
|
|
if (base[0] !== "/" && base[0] !== "#")
|
|
base = "/" + base;
|
|
return removeTrailingSlash(base);
|
|
}
|
|
var BEFORE_HASH_RE = /^[^#]+#/;
|
|
function createHref(base, location2) {
|
|
return base.replace(BEFORE_HASH_RE, "#") + location2;
|
|
}
|
|
function getElementPosition(el, offset) {
|
|
const docRect = document.documentElement.getBoundingClientRect();
|
|
const elRect = el.getBoundingClientRect();
|
|
return {
|
|
behavior: offset.behavior,
|
|
left: elRect.left - docRect.left - (offset.left || 0),
|
|
top: elRect.top - docRect.top - (offset.top || 0)
|
|
};
|
|
}
|
|
var computeScrollPosition = () => ({
|
|
left: window.pageXOffset,
|
|
top: window.pageYOffset
|
|
});
|
|
function scrollToPosition(position) {
|
|
let scrollToOptions;
|
|
if ("el" in position) {
|
|
const positionEl = position.el;
|
|
const isIdSelector = typeof positionEl === "string" && positionEl.startsWith("#");
|
|
if (typeof position.el === "string") {
|
|
if (!isIdSelector || !document.getElementById(position.el.slice(1))) {
|
|
try {
|
|
const foundEl = document.querySelector(position.el);
|
|
if (isIdSelector && foundEl) {
|
|
warn(`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`);
|
|
return;
|
|
}
|
|
} catch (err) {
|
|
warn(`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
const el = typeof positionEl === "string" ? isIdSelector ? document.getElementById(positionEl.slice(1)) : document.querySelector(positionEl) : positionEl;
|
|
if (!el) {
|
|
warn(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`);
|
|
return;
|
|
}
|
|
scrollToOptions = getElementPosition(el, position);
|
|
} else {
|
|
scrollToOptions = position;
|
|
}
|
|
if ("scrollBehavior" in document.documentElement.style)
|
|
window.scrollTo(scrollToOptions);
|
|
else {
|
|
window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.pageXOffset, scrollToOptions.top != null ? scrollToOptions.top : window.pageYOffset);
|
|
}
|
|
}
|
|
function getScrollKey(path, delta) {
|
|
const position = history.state ? history.state.position - delta : -1;
|
|
return position + path;
|
|
}
|
|
var scrollPositions = /* @__PURE__ */ new Map();
|
|
function saveScrollPosition(key, scrollPosition) {
|
|
scrollPositions.set(key, scrollPosition);
|
|
}
|
|
function getSavedScrollPosition(key) {
|
|
const scroll = scrollPositions.get(key);
|
|
scrollPositions.delete(key);
|
|
return scroll;
|
|
}
|
|
var createBaseLocation = () => location.protocol + "//" + location.host;
|
|
function createCurrentLocation(base, location2) {
|
|
const { pathname, search, hash } = location2;
|
|
const hashPos = base.indexOf("#");
|
|
if (hashPos > -1) {
|
|
let slicePos = hash.includes(base.slice(hashPos)) ? base.slice(hashPos).length : 1;
|
|
let pathFromHash = hash.slice(slicePos);
|
|
if (pathFromHash[0] !== "/")
|
|
pathFromHash = "/" + pathFromHash;
|
|
return stripBase(pathFromHash, "");
|
|
}
|
|
const path = stripBase(pathname, base);
|
|
return path + search + hash;
|
|
}
|
|
function useHistoryListeners(base, historyState, currentLocation, replace) {
|
|
let listeners = [];
|
|
let teardowns = [];
|
|
let pauseState = null;
|
|
const popStateHandler = ({ state }) => {
|
|
const to = createCurrentLocation(base, location);
|
|
const from = currentLocation.value;
|
|
const fromState = historyState.value;
|
|
let delta = 0;
|
|
if (state) {
|
|
currentLocation.value = to;
|
|
historyState.value = state;
|
|
if (pauseState && pauseState === from) {
|
|
pauseState = null;
|
|
return;
|
|
}
|
|
delta = fromState ? state.position - fromState.position : 0;
|
|
} else {
|
|
replace(to);
|
|
}
|
|
listeners.forEach((listener) => {
|
|
listener(currentLocation.value, from, {
|
|
delta,
|
|
type: NavigationType.pop,
|
|
direction: delta ? delta > 0 ? NavigationDirection.forward : NavigationDirection.back : NavigationDirection.unknown
|
|
});
|
|
});
|
|
};
|
|
function pauseListeners() {
|
|
pauseState = currentLocation.value;
|
|
}
|
|
function listen(callback) {
|
|
listeners.push(callback);
|
|
const teardown = () => {
|
|
const index = listeners.indexOf(callback);
|
|
if (index > -1)
|
|
listeners.splice(index, 1);
|
|
};
|
|
teardowns.push(teardown);
|
|
return teardown;
|
|
}
|
|
function beforeUnloadListener() {
|
|
const { history: history2 } = window;
|
|
if (!history2.state)
|
|
return;
|
|
history2.replaceState(assign({}, history2.state, { scroll: computeScrollPosition() }), "");
|
|
}
|
|
function destroy() {
|
|
for (const teardown of teardowns)
|
|
teardown();
|
|
teardowns = [];
|
|
window.removeEventListener("popstate", popStateHandler);
|
|
window.removeEventListener("beforeunload", beforeUnloadListener);
|
|
}
|
|
window.addEventListener("popstate", popStateHandler);
|
|
window.addEventListener("beforeunload", beforeUnloadListener);
|
|
return {
|
|
pauseListeners,
|
|
listen,
|
|
destroy
|
|
};
|
|
}
|
|
function buildState(back, current, forward, replaced = false, computeScroll = false) {
|
|
return {
|
|
back,
|
|
current,
|
|
forward,
|
|
replaced,
|
|
position: window.history.length,
|
|
scroll: computeScroll ? computeScrollPosition() : null
|
|
};
|
|
}
|
|
function useHistoryStateNavigation(base) {
|
|
const { history: history2, location: location2 } = window;
|
|
const currentLocation = {
|
|
value: createCurrentLocation(base, location2)
|
|
};
|
|
const historyState = { value: history2.state };
|
|
if (!historyState.value) {
|
|
changeLocation(currentLocation.value, {
|
|
back: null,
|
|
current: currentLocation.value,
|
|
forward: null,
|
|
// the length is off by one, we need to decrease it
|
|
position: history2.length - 1,
|
|
replaced: true,
|
|
// don't add a scroll as the user may have an anchor and we want
|
|
// scrollBehavior to be triggered without a saved position
|
|
scroll: null
|
|
}, true);
|
|
}
|
|
function changeLocation(to, state, replace2) {
|
|
const hashIndex = base.indexOf("#");
|
|
const url = hashIndex > -1 ? (location2.host && document.querySelector("base") ? base : base.slice(hashIndex)) + to : createBaseLocation() + base + to;
|
|
try {
|
|
history2[replace2 ? "replaceState" : "pushState"](state, "", url);
|
|
historyState.value = state;
|
|
} catch (err) {
|
|
if (true) {
|
|
warn("Error with push/replace State", err);
|
|
} else {
|
|
console.error(err);
|
|
}
|
|
location2[replace2 ? "replace" : "assign"](url);
|
|
}
|
|
}
|
|
function replace(to, data) {
|
|
const state = assign({}, history2.state, buildState(
|
|
historyState.value.back,
|
|
// keep back and forward entries but override current position
|
|
to,
|
|
historyState.value.forward,
|
|
true
|
|
), data, { position: historyState.value.position });
|
|
changeLocation(to, state, true);
|
|
currentLocation.value = to;
|
|
}
|
|
function push(to, data) {
|
|
const currentState = assign(
|
|
{},
|
|
// use current history state to gracefully handle a wrong call to
|
|
// history.replaceState
|
|
// https://github.com/vuejs/router/issues/366
|
|
historyState.value,
|
|
history2.state,
|
|
{
|
|
forward: to,
|
|
scroll: computeScrollPosition()
|
|
}
|
|
);
|
|
if (!history2.state) {
|
|
warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:
|
|
|
|
history.replaceState(history.state, '', url)
|
|
|
|
You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`);
|
|
}
|
|
changeLocation(currentState.current, currentState, true);
|
|
const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data);
|
|
changeLocation(to, state, false);
|
|
currentLocation.value = to;
|
|
}
|
|
return {
|
|
location: currentLocation,
|
|
state: historyState,
|
|
push,
|
|
replace
|
|
};
|
|
}
|
|
function createWebHistory(base) {
|
|
base = normalizeBase(base);
|
|
const historyNavigation = useHistoryStateNavigation(base);
|
|
const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace);
|
|
function go(delta, triggerListeners = true) {
|
|
if (!triggerListeners)
|
|
historyListeners.pauseListeners();
|
|
history.go(delta);
|
|
}
|
|
const routerHistory = assign({
|
|
// it's overridden right after
|
|
location: "",
|
|
base,
|
|
go,
|
|
createHref: createHref.bind(null, base)
|
|
}, historyNavigation, historyListeners);
|
|
Object.defineProperty(routerHistory, "location", {
|
|
enumerable: true,
|
|
get: () => historyNavigation.location.value
|
|
});
|
|
Object.defineProperty(routerHistory, "state", {
|
|
enumerable: true,
|
|
get: () => historyNavigation.state.value
|
|
});
|
|
return routerHistory;
|
|
}
|
|
function createMemoryHistory(base = "") {
|
|
let listeners = [];
|
|
let queue = [START];
|
|
let position = 0;
|
|
base = normalizeBase(base);
|
|
function setLocation(location2) {
|
|
position++;
|
|
if (position === queue.length) {
|
|
queue.push(location2);
|
|
} else {
|
|
queue.splice(position);
|
|
queue.push(location2);
|
|
}
|
|
}
|
|
function triggerListeners(to, from, { direction, delta }) {
|
|
const info = {
|
|
direction,
|
|
delta,
|
|
type: NavigationType.pop
|
|
};
|
|
for (const callback of listeners) {
|
|
callback(to, from, info);
|
|
}
|
|
}
|
|
const routerHistory = {
|
|
// rewritten by Object.defineProperty
|
|
location: START,
|
|
// TODO: should be kept in queue
|
|
state: {},
|
|
base,
|
|
createHref: createHref.bind(null, base),
|
|
replace(to) {
|
|
queue.splice(position--, 1);
|
|
setLocation(to);
|
|
},
|
|
push(to, data) {
|
|
setLocation(to);
|
|
},
|
|
listen(callback) {
|
|
listeners.push(callback);
|
|
return () => {
|
|
const index = listeners.indexOf(callback);
|
|
if (index > -1)
|
|
listeners.splice(index, 1);
|
|
};
|
|
},
|
|
destroy() {
|
|
listeners = [];
|
|
queue = [START];
|
|
position = 0;
|
|
},
|
|
go(delta, shouldTrigger = true) {
|
|
const from = this.location;
|
|
const direction = (
|
|
// we are considering delta === 0 going forward, but in abstract mode
|
|
// using 0 for the delta doesn't make sense like it does in html5 where
|
|
// it reloads the page
|
|
delta < 0 ? NavigationDirection.back : NavigationDirection.forward
|
|
);
|
|
position = Math.max(0, Math.min(position + delta, queue.length - 1));
|
|
if (shouldTrigger) {
|
|
triggerListeners(this.location, from, {
|
|
direction,
|
|
delta
|
|
});
|
|
}
|
|
}
|
|
};
|
|
Object.defineProperty(routerHistory, "location", {
|
|
enumerable: true,
|
|
get: () => queue[position]
|
|
});
|
|
return routerHistory;
|
|
}
|
|
function createWebHashHistory(base) {
|
|
base = location.host ? base || location.pathname + location.search : "";
|
|
if (!base.includes("#"))
|
|
base += "#";
|
|
if (!base.endsWith("#/") && !base.endsWith("#")) {
|
|
warn(`A hash base must end with a "#":
|
|
"${base}" should be "${base.replace(/#.*$/, "#")}".`);
|
|
}
|
|
return createWebHistory(base);
|
|
}
|
|
function isRouteLocation(route) {
|
|
return typeof route === "string" || route && typeof route === "object";
|
|
}
|
|
function isRouteName(name) {
|
|
return typeof name === "string" || typeof name === "symbol";
|
|
}
|
|
var START_LOCATION_NORMALIZED = {
|
|
path: "/",
|
|
name: void 0,
|
|
params: {},
|
|
query: {},
|
|
hash: "",
|
|
fullPath: "/",
|
|
matched: [],
|
|
meta: {},
|
|
redirectedFrom: void 0
|
|
};
|
|
var NavigationFailureSymbol = PolySymbol(true ? "navigation failure" : "nf");
|
|
var NavigationFailureType;
|
|
(function(NavigationFailureType2) {
|
|
NavigationFailureType2[NavigationFailureType2["aborted"] = 4] = "aborted";
|
|
NavigationFailureType2[NavigationFailureType2["cancelled"] = 8] = "cancelled";
|
|
NavigationFailureType2[NavigationFailureType2["duplicated"] = 16] = "duplicated";
|
|
})(NavigationFailureType || (NavigationFailureType = {}));
|
|
var ErrorTypeMessages = {
|
|
[
|
|
1
|
|
/* MATCHER_NOT_FOUND */
|
|
]({ location: location2, currentLocation }) {
|
|
return `No match for
|
|
${JSON.stringify(location2)}${currentLocation ? "\nwhile being at\n" + JSON.stringify(currentLocation) : ""}`;
|
|
},
|
|
[
|
|
2
|
|
/* NAVIGATION_GUARD_REDIRECT */
|
|
]({ from, to }) {
|
|
return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
|
|
},
|
|
[
|
|
4
|
|
/* NAVIGATION_ABORTED */
|
|
]({ from, to }) {
|
|
return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
|
|
},
|
|
[
|
|
8
|
|
/* NAVIGATION_CANCELLED */
|
|
]({ from, to }) {
|
|
return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
|
|
},
|
|
[
|
|
16
|
|
/* NAVIGATION_DUPLICATED */
|
|
]({ from, to }) {
|
|
return `Avoided redundant navigation to current location: "${from.fullPath}".`;
|
|
}
|
|
};
|
|
function createRouterError(type, params) {
|
|
if (true) {
|
|
return assign(new Error(ErrorTypeMessages[type](params)), {
|
|
type,
|
|
[NavigationFailureSymbol]: true
|
|
}, params);
|
|
} else {
|
|
return assign(new Error(), {
|
|
type,
|
|
[NavigationFailureSymbol]: true
|
|
}, params);
|
|
}
|
|
}
|
|
function isNavigationFailure(error, type) {
|
|
return error instanceof Error && NavigationFailureSymbol in error && (type == null || !!(error.type & type));
|
|
}
|
|
var propertiesToLog = ["params", "query", "hash"];
|
|
function stringifyRoute(to) {
|
|
if (typeof to === "string")
|
|
return to;
|
|
if ("path" in to)
|
|
return to.path;
|
|
const location2 = {};
|
|
for (const key of propertiesToLog) {
|
|
if (key in to)
|
|
location2[key] = to[key];
|
|
}
|
|
return JSON.stringify(location2, null, 2);
|
|
}
|
|
var BASE_PARAM_PATTERN = "[^/]+?";
|
|
var BASE_PATH_PARSER_OPTIONS = {
|
|
sensitive: false,
|
|
strict: false,
|
|
start: true,
|
|
end: true
|
|
};
|
|
var REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g;
|
|
function tokensToParser(segments, extraOptions) {
|
|
const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions);
|
|
const score = [];
|
|
let pattern = options.start ? "^" : "";
|
|
const keys = [];
|
|
for (const segment of segments) {
|
|
const segmentScores = segment.length ? [] : [
|
|
90
|
|
/* Root */
|
|
];
|
|
if (options.strict && !segment.length)
|
|
pattern += "/";
|
|
for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
|
|
const token = segment[tokenIndex];
|
|
let subSegmentScore = 40 + (options.sensitive ? 0.25 : 0);
|
|
if (token.type === 0) {
|
|
if (!tokenIndex)
|
|
pattern += "/";
|
|
pattern += token.value.replace(REGEX_CHARS_RE, "\\$&");
|
|
subSegmentScore += 40;
|
|
} else if (token.type === 1) {
|
|
const { value, repeatable, optional, regexp } = token;
|
|
keys.push({
|
|
name: value,
|
|
repeatable,
|
|
optional
|
|
});
|
|
const re2 = regexp ? regexp : BASE_PARAM_PATTERN;
|
|
if (re2 !== BASE_PARAM_PATTERN) {
|
|
subSegmentScore += 10;
|
|
try {
|
|
new RegExp(`(${re2})`);
|
|
} catch (err) {
|
|
throw new Error(`Invalid custom RegExp for param "${value}" (${re2}): ` + err.message);
|
|
}
|
|
}
|
|
let subPattern = repeatable ? `((?:${re2})(?:/(?:${re2}))*)` : `(${re2})`;
|
|
if (!tokenIndex)
|
|
subPattern = // avoid an optional / if there are more segments e.g. /:p?-static
|
|
// or /:p?-:p2
|
|
optional && segment.length < 2 ? `(?:/${subPattern})` : "/" + subPattern;
|
|
if (optional)
|
|
subPattern += "?";
|
|
pattern += subPattern;
|
|
subSegmentScore += 20;
|
|
if (optional)
|
|
subSegmentScore += -8;
|
|
if (repeatable)
|
|
subSegmentScore += -20;
|
|
if (re2 === ".*")
|
|
subSegmentScore += -50;
|
|
}
|
|
segmentScores.push(subSegmentScore);
|
|
}
|
|
score.push(segmentScores);
|
|
}
|
|
if (options.strict && options.end) {
|
|
const i = score.length - 1;
|
|
score[i][score[i].length - 1] += 0.7000000000000001;
|
|
}
|
|
if (!options.strict)
|
|
pattern += "/?";
|
|
if (options.end)
|
|
pattern += "$";
|
|
else if (options.strict)
|
|
pattern += "(?:/|$)";
|
|
const re = new RegExp(pattern, options.sensitive ? "" : "i");
|
|
function parse(path) {
|
|
const match = path.match(re);
|
|
const params = {};
|
|
if (!match)
|
|
return null;
|
|
for (let i = 1; i < match.length; i++) {
|
|
const value = match[i] || "";
|
|
const key = keys[i - 1];
|
|
params[key.name] = value && key.repeatable ? value.split("/") : value;
|
|
}
|
|
return params;
|
|
}
|
|
function stringify(params) {
|
|
let path = "";
|
|
let avoidDuplicatedSlash = false;
|
|
for (const segment of segments) {
|
|
if (!avoidDuplicatedSlash || !path.endsWith("/"))
|
|
path += "/";
|
|
avoidDuplicatedSlash = false;
|
|
for (const token of segment) {
|
|
if (token.type === 0) {
|
|
path += token.value;
|
|
} else if (token.type === 1) {
|
|
const { value, repeatable, optional } = token;
|
|
const param = value in params ? params[value] : "";
|
|
if (Array.isArray(param) && !repeatable)
|
|
throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
|
|
const text = Array.isArray(param) ? param.join("/") : param;
|
|
if (!text) {
|
|
if (optional) {
|
|
if (segment.length < 2) {
|
|
if (path.endsWith("/"))
|
|
path = path.slice(0, -1);
|
|
else
|
|
avoidDuplicatedSlash = true;
|
|
}
|
|
} else
|
|
throw new Error(`Missing required param "${value}"`);
|
|
}
|
|
path += text;
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
return {
|
|
re,
|
|
score,
|
|
keys,
|
|
parse,
|
|
stringify
|
|
};
|
|
}
|
|
function compareScoreArray(a, b) {
|
|
let i = 0;
|
|
while (i < a.length && i < b.length) {
|
|
const diff = b[i] - a[i];
|
|
if (diff)
|
|
return diff;
|
|
i++;
|
|
}
|
|
if (a.length < b.length) {
|
|
return a.length === 1 && a[0] === 40 + 40 ? -1 : 1;
|
|
} else if (a.length > b.length) {
|
|
return b.length === 1 && b[0] === 40 + 40 ? 1 : -1;
|
|
}
|
|
return 0;
|
|
}
|
|
function comparePathParserScore(a, b) {
|
|
let i = 0;
|
|
const aScore = a.score;
|
|
const bScore = b.score;
|
|
while (i < aScore.length && i < bScore.length) {
|
|
const comp = compareScoreArray(aScore[i], bScore[i]);
|
|
if (comp)
|
|
return comp;
|
|
i++;
|
|
}
|
|
return bScore.length - aScore.length;
|
|
}
|
|
var ROOT_TOKEN = {
|
|
type: 0,
|
|
value: ""
|
|
};
|
|
var VALID_PARAM_RE = /[a-zA-Z0-9_]/;
|
|
function tokenizePath(path) {
|
|
if (!path)
|
|
return [[]];
|
|
if (path === "/")
|
|
return [[ROOT_TOKEN]];
|
|
if (!path.startsWith("/")) {
|
|
throw new Error(true ? `Route paths should start with a "/": "${path}" should be "/${path}".` : `Invalid path "${path}"`);
|
|
}
|
|
function crash(message) {
|
|
throw new Error(`ERR (${state})/"${buffer}": ${message}`);
|
|
}
|
|
let state = 0;
|
|
let previousState = state;
|
|
const tokens = [];
|
|
let segment;
|
|
function finalizeSegment() {
|
|
if (segment)
|
|
tokens.push(segment);
|
|
segment = [];
|
|
}
|
|
let i = 0;
|
|
let char;
|
|
let buffer = "";
|
|
let customRe = "";
|
|
function consumeBuffer() {
|
|
if (!buffer)
|
|
return;
|
|
if (state === 0) {
|
|
segment.push({
|
|
type: 0,
|
|
value: buffer
|
|
});
|
|
} else if (state === 1 || state === 2 || state === 3) {
|
|
if (segment.length > 1 && (char === "*" || char === "+"))
|
|
crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
|
|
segment.push({
|
|
type: 1,
|
|
value: buffer,
|
|
regexp: customRe,
|
|
repeatable: char === "*" || char === "+",
|
|
optional: char === "*" || char === "?"
|
|
});
|
|
} else {
|
|
crash("Invalid state to consume buffer");
|
|
}
|
|
buffer = "";
|
|
}
|
|
function addCharToBuffer() {
|
|
buffer += char;
|
|
}
|
|
while (i < path.length) {
|
|
char = path[i++];
|
|
if (char === "\\" && state !== 2) {
|
|
previousState = state;
|
|
state = 4;
|
|
continue;
|
|
}
|
|
switch (state) {
|
|
case 0:
|
|
if (char === "/") {
|
|
if (buffer) {
|
|
consumeBuffer();
|
|
}
|
|
finalizeSegment();
|
|
} else if (char === ":") {
|
|
consumeBuffer();
|
|
state = 1;
|
|
} else {
|
|
addCharToBuffer();
|
|
}
|
|
break;
|
|
case 4:
|
|
addCharToBuffer();
|
|
state = previousState;
|
|
break;
|
|
case 1:
|
|
if (char === "(") {
|
|
state = 2;
|
|
} else if (VALID_PARAM_RE.test(char)) {
|
|
addCharToBuffer();
|
|
} else {
|
|
consumeBuffer();
|
|
state = 0;
|
|
if (char !== "*" && char !== "?" && char !== "+")
|
|
i--;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (char === ")") {
|
|
if (customRe[customRe.length - 1] == "\\")
|
|
customRe = customRe.slice(0, -1) + char;
|
|
else
|
|
state = 3;
|
|
} else {
|
|
customRe += char;
|
|
}
|
|
break;
|
|
case 3:
|
|
consumeBuffer();
|
|
state = 0;
|
|
if (char !== "*" && char !== "?" && char !== "+")
|
|
i--;
|
|
customRe = "";
|
|
break;
|
|
default:
|
|
crash("Unknown state");
|
|
break;
|
|
}
|
|
}
|
|
if (state === 2)
|
|
crash(`Unfinished custom RegExp for param "${buffer}"`);
|
|
consumeBuffer();
|
|
finalizeSegment();
|
|
return tokens;
|
|
}
|
|
function createRouteRecordMatcher(record, parent, options) {
|
|
const parser = tokensToParser(tokenizePath(record.path), options);
|
|
if (true) {
|
|
const existingKeys = /* @__PURE__ */ new Set();
|
|
for (const key of parser.keys) {
|
|
if (existingKeys.has(key.name))
|
|
warn(`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`);
|
|
existingKeys.add(key.name);
|
|
}
|
|
}
|
|
const matcher = assign(parser, {
|
|
record,
|
|
parent,
|
|
// these needs to be populated by the parent
|
|
children: [],
|
|
alias: []
|
|
});
|
|
if (parent) {
|
|
if (!matcher.record.aliasOf === !parent.record.aliasOf)
|
|
parent.children.push(matcher);
|
|
}
|
|
return matcher;
|
|
}
|
|
function createRouterMatcher(routes, globalOptions) {
|
|
const matchers = [];
|
|
const matcherMap = /* @__PURE__ */ new Map();
|
|
globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions);
|
|
function getRecordMatcher(name) {
|
|
return matcherMap.get(name);
|
|
}
|
|
function addRoute(record, parent, originalRecord) {
|
|
const isRootAdd = !originalRecord;
|
|
const mainNormalizedRecord = normalizeRouteRecord(record);
|
|
mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
|
|
const options = mergeOptions(globalOptions, record);
|
|
const normalizedRecords = [
|
|
mainNormalizedRecord
|
|
];
|
|
if ("alias" in record) {
|
|
const aliases = typeof record.alias === "string" ? [record.alias] : record.alias;
|
|
for (const alias of aliases) {
|
|
normalizedRecords.push(assign({}, mainNormalizedRecord, {
|
|
// this allows us to hold a copy of the `components` option
|
|
// so that async components cache is hold on the original record
|
|
components: originalRecord ? originalRecord.record.components : mainNormalizedRecord.components,
|
|
path: alias,
|
|
// we might be the child of an alias
|
|
aliasOf: originalRecord ? originalRecord.record : mainNormalizedRecord
|
|
// the aliases are always of the same kind as the original since they
|
|
// are defined on the same record
|
|
}));
|
|
}
|
|
}
|
|
let matcher;
|
|
let originalMatcher;
|
|
for (const normalizedRecord of normalizedRecords) {
|
|
const { path } = normalizedRecord;
|
|
if (parent && path[0] !== "/") {
|
|
const parentPath = parent.record.path;
|
|
const connectingSlash = parentPath[parentPath.length - 1] === "/" ? "" : "/";
|
|
normalizedRecord.path = parent.record.path + (path && connectingSlash + path);
|
|
}
|
|
if (normalizedRecord.path === "*") {
|
|
throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\nSee more at https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes.');
|
|
}
|
|
matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
|
|
if (parent && path[0] === "/")
|
|
checkMissingParamsInAbsolutePath(matcher, parent);
|
|
if (originalRecord) {
|
|
originalRecord.alias.push(matcher);
|
|
if (true) {
|
|
checkSameParams(originalRecord, matcher);
|
|
}
|
|
} else {
|
|
originalMatcher = originalMatcher || matcher;
|
|
if (originalMatcher !== matcher)
|
|
originalMatcher.alias.push(matcher);
|
|
if (isRootAdd && record.name && !isAliasRecord(matcher))
|
|
removeRoute(record.name);
|
|
}
|
|
if ("children" in mainNormalizedRecord) {
|
|
const children = mainNormalizedRecord.children;
|
|
for (let i = 0; i < children.length; i++) {
|
|
addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
|
|
}
|
|
}
|
|
originalRecord = originalRecord || matcher;
|
|
insertMatcher(matcher);
|
|
}
|
|
return originalMatcher ? () => {
|
|
removeRoute(originalMatcher);
|
|
} : noop;
|
|
}
|
|
function removeRoute(matcherRef) {
|
|
if (isRouteName(matcherRef)) {
|
|
const matcher = matcherMap.get(matcherRef);
|
|
if (matcher) {
|
|
matcherMap.delete(matcherRef);
|
|
matchers.splice(matchers.indexOf(matcher), 1);
|
|
matcher.children.forEach(removeRoute);
|
|
matcher.alias.forEach(removeRoute);
|
|
}
|
|
} else {
|
|
const index = matchers.indexOf(matcherRef);
|
|
if (index > -1) {
|
|
matchers.splice(index, 1);
|
|
if (matcherRef.record.name)
|
|
matcherMap.delete(matcherRef.record.name);
|
|
matcherRef.children.forEach(removeRoute);
|
|
matcherRef.alias.forEach(removeRoute);
|
|
}
|
|
}
|
|
}
|
|
function getRoutes() {
|
|
return matchers;
|
|
}
|
|
function insertMatcher(matcher) {
|
|
let i = 0;
|
|
while (i < matchers.length && comparePathParserScore(matcher, matchers[i]) >= 0 && // Adding children with empty path should still appear before the parent
|
|
// https://github.com/vuejs/router/issues/1124
|
|
(matcher.record.path !== matchers[i].record.path || !isRecordChildOf(matcher, matchers[i])))
|
|
i++;
|
|
matchers.splice(i, 0, matcher);
|
|
if (matcher.record.name && !isAliasRecord(matcher))
|
|
matcherMap.set(matcher.record.name, matcher);
|
|
}
|
|
function resolve(location2, currentLocation) {
|
|
let matcher;
|
|
let params = {};
|
|
let path;
|
|
let name;
|
|
if ("name" in location2 && location2.name) {
|
|
matcher = matcherMap.get(location2.name);
|
|
if (!matcher)
|
|
throw createRouterError(1, {
|
|
location: location2
|
|
});
|
|
name = matcher.record.name;
|
|
params = assign(
|
|
// paramsFromLocation is a new object
|
|
paramsFromLocation(
|
|
currentLocation.params,
|
|
// only keep params that exist in the resolved location
|
|
// TODO: only keep optional params coming from a parent record
|
|
matcher.keys.filter((k) => !k.optional).map((k) => k.name)
|
|
),
|
|
location2.params
|
|
);
|
|
path = matcher.stringify(params);
|
|
} else if ("path" in location2) {
|
|
path = location2.path;
|
|
if (!path.startsWith("/")) {
|
|
warn(`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/router.`);
|
|
}
|
|
matcher = matchers.find((m) => m.re.test(path));
|
|
if (matcher) {
|
|
params = matcher.parse(path);
|
|
name = matcher.record.name;
|
|
}
|
|
} else {
|
|
matcher = currentLocation.name ? matcherMap.get(currentLocation.name) : matchers.find((m) => m.re.test(currentLocation.path));
|
|
if (!matcher)
|
|
throw createRouterError(1, {
|
|
location: location2,
|
|
currentLocation
|
|
});
|
|
name = matcher.record.name;
|
|
params = assign({}, currentLocation.params, location2.params);
|
|
path = matcher.stringify(params);
|
|
}
|
|
const matched = [];
|
|
let parentMatcher = matcher;
|
|
while (parentMatcher) {
|
|
matched.unshift(parentMatcher.record);
|
|
parentMatcher = parentMatcher.parent;
|
|
}
|
|
return {
|
|
name,
|
|
path,
|
|
params,
|
|
matched,
|
|
meta: mergeMetaFields(matched)
|
|
};
|
|
}
|
|
routes.forEach((route) => addRoute(route));
|
|
return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher };
|
|
}
|
|
function paramsFromLocation(params, keys) {
|
|
const newParams = {};
|
|
for (const key of keys) {
|
|
if (key in params)
|
|
newParams[key] = params[key];
|
|
}
|
|
return newParams;
|
|
}
|
|
function normalizeRouteRecord(record) {
|
|
return {
|
|
path: record.path,
|
|
redirect: record.redirect,
|
|
name: record.name,
|
|
meta: record.meta || {},
|
|
aliasOf: void 0,
|
|
beforeEnter: record.beforeEnter,
|
|
props: normalizeRecordProps(record),
|
|
children: record.children || [],
|
|
instances: {},
|
|
leaveGuards: /* @__PURE__ */ new Set(),
|
|
updateGuards: /* @__PURE__ */ new Set(),
|
|
enterCallbacks: {},
|
|
components: "components" in record ? record.components || {} : { default: record.component }
|
|
};
|
|
}
|
|
function normalizeRecordProps(record) {
|
|
const propsObject = {};
|
|
const props = record.props || false;
|
|
if ("component" in record) {
|
|
propsObject.default = props;
|
|
} else {
|
|
for (const name in record.components)
|
|
propsObject[name] = typeof props === "boolean" ? props : props[name];
|
|
}
|
|
return propsObject;
|
|
}
|
|
function isAliasRecord(record) {
|
|
while (record) {
|
|
if (record.record.aliasOf)
|
|
return true;
|
|
record = record.parent;
|
|
}
|
|
return false;
|
|
}
|
|
function mergeMetaFields(matched) {
|
|
return matched.reduce((meta, record) => assign(meta, record.meta), {});
|
|
}
|
|
function mergeOptions(defaults, partialOptions) {
|
|
const options = {};
|
|
for (const key in defaults) {
|
|
options[key] = key in partialOptions ? partialOptions[key] : defaults[key];
|
|
}
|
|
return options;
|
|
}
|
|
function isSameParam(a, b) {
|
|
return a.name === b.name && a.optional === b.optional && a.repeatable === b.repeatable;
|
|
}
|
|
function checkSameParams(a, b) {
|
|
for (const key of a.keys) {
|
|
if (!key.optional && !b.keys.find(isSameParam.bind(null, key)))
|
|
return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
|
|
}
|
|
for (const key of b.keys) {
|
|
if (!key.optional && !a.keys.find(isSameParam.bind(null, key)))
|
|
return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
|
|
}
|
|
}
|
|
function checkMissingParamsInAbsolutePath(record, parent) {
|
|
for (const key of parent.keys) {
|
|
if (!record.keys.find(isSameParam.bind(null, key)))
|
|
return warn(`Absolute path "${record.record.path}" should have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
|
|
}
|
|
}
|
|
function isRecordChildOf(record, parent) {
|
|
return parent.children.some((child) => child === record || isRecordChildOf(record, child));
|
|
}
|
|
var HASH_RE = /#/g;
|
|
var AMPERSAND_RE = /&/g;
|
|
var SLASH_RE = /\//g;
|
|
var EQUAL_RE = /=/g;
|
|
var IM_RE = /\?/g;
|
|
var PLUS_RE = /\+/g;
|
|
var ENC_BRACKET_OPEN_RE = /%5B/g;
|
|
var ENC_BRACKET_CLOSE_RE = /%5D/g;
|
|
var ENC_CARET_RE = /%5E/g;
|
|
var ENC_BACKTICK_RE = /%60/g;
|
|
var ENC_CURLY_OPEN_RE = /%7B/g;
|
|
var ENC_PIPE_RE = /%7C/g;
|
|
var ENC_CURLY_CLOSE_RE = /%7D/g;
|
|
var ENC_SPACE_RE = /%20/g;
|
|
function commonEncode(text) {
|
|
return encodeURI("" + text).replace(ENC_PIPE_RE, "|").replace(ENC_BRACKET_OPEN_RE, "[").replace(ENC_BRACKET_CLOSE_RE, "]");
|
|
}
|
|
function encodeHash(text) {
|
|
return commonEncode(text).replace(ENC_CURLY_OPEN_RE, "{").replace(ENC_CURLY_CLOSE_RE, "}").replace(ENC_CARET_RE, "^");
|
|
}
|
|
function encodeQueryValue(text) {
|
|
return commonEncode(text).replace(PLUS_RE, "%2B").replace(ENC_SPACE_RE, "+").replace(HASH_RE, "%23").replace(AMPERSAND_RE, "%26").replace(ENC_BACKTICK_RE, "`").replace(ENC_CURLY_OPEN_RE, "{").replace(ENC_CURLY_CLOSE_RE, "}").replace(ENC_CARET_RE, "^");
|
|
}
|
|
function encodeQueryKey(text) {
|
|
return encodeQueryValue(text).replace(EQUAL_RE, "%3D");
|
|
}
|
|
function encodePath(text) {
|
|
return commonEncode(text).replace(HASH_RE, "%23").replace(IM_RE, "%3F");
|
|
}
|
|
function encodeParam(text) {
|
|
return text == null ? "" : encodePath(text).replace(SLASH_RE, "%2F");
|
|
}
|
|
function decode(text) {
|
|
try {
|
|
return decodeURIComponent("" + text);
|
|
} catch (err) {
|
|
warn(`Error decoding "${text}". Using original value`);
|
|
}
|
|
return "" + text;
|
|
}
|
|
function parseQuery(search) {
|
|
const query = {};
|
|
if (search === "" || search === "?")
|
|
return query;
|
|
const hasLeadingIM = search[0] === "?";
|
|
const searchParams = (hasLeadingIM ? search.slice(1) : search).split("&");
|
|
for (let i = 0; i < searchParams.length; ++i) {
|
|
const searchParam = searchParams[i].replace(PLUS_RE, " ");
|
|
const eqPos = searchParam.indexOf("=");
|
|
const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
|
|
const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
|
|
if (key in query) {
|
|
let currentValue = query[key];
|
|
if (!Array.isArray(currentValue)) {
|
|
currentValue = query[key] = [currentValue];
|
|
}
|
|
currentValue.push(value);
|
|
} else {
|
|
query[key] = value;
|
|
}
|
|
}
|
|
return query;
|
|
}
|
|
function stringifyQuery(query) {
|
|
let search = "";
|
|
for (let key in query) {
|
|
const value = query[key];
|
|
key = encodeQueryKey(key);
|
|
if (value == null) {
|
|
if (value !== void 0) {
|
|
search += (search.length ? "&" : "") + key;
|
|
}
|
|
continue;
|
|
}
|
|
const values = Array.isArray(value) ? value.map((v) => v && encodeQueryValue(v)) : [value && encodeQueryValue(value)];
|
|
values.forEach((value2) => {
|
|
if (value2 !== void 0) {
|
|
search += (search.length ? "&" : "") + key;
|
|
if (value2 != null)
|
|
search += "=" + value2;
|
|
}
|
|
});
|
|
}
|
|
return search;
|
|
}
|
|
function normalizeQuery(query) {
|
|
const normalizedQuery = {};
|
|
for (const key in query) {
|
|
const value = query[key];
|
|
if (value !== void 0) {
|
|
normalizedQuery[key] = Array.isArray(value) ? value.map((v) => v == null ? null : "" + v) : value == null ? value : "" + value;
|
|
}
|
|
}
|
|
return normalizedQuery;
|
|
}
|
|
function useCallbacks() {
|
|
let handlers = [];
|
|
function add(handler) {
|
|
handlers.push(handler);
|
|
return () => {
|
|
const i = handlers.indexOf(handler);
|
|
if (i > -1)
|
|
handlers.splice(i, 1);
|
|
};
|
|
}
|
|
function reset() {
|
|
handlers = [];
|
|
}
|
|
return {
|
|
add,
|
|
list: () => handlers,
|
|
reset
|
|
};
|
|
}
|
|
function registerGuard(record, name, guard) {
|
|
const removeFromList = () => {
|
|
record[name].delete(guard);
|
|
};
|
|
onUnmounted(removeFromList);
|
|
onDeactivated(removeFromList);
|
|
onActivated(() => {
|
|
record[name].add(guard);
|
|
});
|
|
record[name].add(guard);
|
|
}
|
|
function onBeforeRouteLeave(leaveGuard) {
|
|
if (!getCurrentInstance()) {
|
|
warn("getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function");
|
|
return;
|
|
}
|
|
const activeRecord = inject(
|
|
matchedRouteKey,
|
|
// to avoid warning
|
|
{}
|
|
).value;
|
|
if (!activeRecord) {
|
|
warn("No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?");
|
|
return;
|
|
}
|
|
registerGuard(activeRecord, "leaveGuards", leaveGuard);
|
|
}
|
|
function onBeforeRouteUpdate(updateGuard) {
|
|
if (!getCurrentInstance()) {
|
|
warn("getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function");
|
|
return;
|
|
}
|
|
const activeRecord = inject(
|
|
matchedRouteKey,
|
|
// to avoid warning
|
|
{}
|
|
).value;
|
|
if (!activeRecord) {
|
|
warn("No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?");
|
|
return;
|
|
}
|
|
registerGuard(activeRecord, "updateGuards", updateGuard);
|
|
}
|
|
function guardToPromiseFn(guard, to, from, record, name) {
|
|
const enterCallbackArray = record && // name is defined if record is because of the function overload
|
|
(record.enterCallbacks[name] = record.enterCallbacks[name] || []);
|
|
return () => new Promise((resolve, reject) => {
|
|
const next = (valid) => {
|
|
if (valid === false)
|
|
reject(createRouterError(4, {
|
|
from,
|
|
to
|
|
}));
|
|
else if (valid instanceof Error) {
|
|
reject(valid);
|
|
} else if (isRouteLocation(valid)) {
|
|
reject(createRouterError(2, {
|
|
from: to,
|
|
to: valid
|
|
}));
|
|
} else {
|
|
if (enterCallbackArray && // since enterCallbackArray is truthy, both record and name also are
|
|
record.enterCallbacks[name] === enterCallbackArray && typeof valid === "function")
|
|
enterCallbackArray.push(valid);
|
|
resolve();
|
|
}
|
|
};
|
|
const guardReturn = guard.call(record && record.instances[name], to, from, true ? canOnlyBeCalledOnce(next, to, from) : next);
|
|
let guardCall = Promise.resolve(guardReturn);
|
|
if (guard.length < 3)
|
|
guardCall = guardCall.then(next);
|
|
if (guard.length > 2) {
|
|
const message = `The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ""}:
|
|
${guard.toString()}
|
|
. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
|
|
if (typeof guardReturn === "object" && "then" in guardReturn) {
|
|
guardCall = guardCall.then((resolvedValue) => {
|
|
if (!next._called) {
|
|
warn(message);
|
|
return Promise.reject(new Error("Invalid navigation guard"));
|
|
}
|
|
return resolvedValue;
|
|
});
|
|
} else if (guardReturn !== void 0) {
|
|
if (!next._called) {
|
|
warn(message);
|
|
reject(new Error("Invalid navigation guard"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
guardCall.catch((err) => reject(err));
|
|
});
|
|
}
|
|
function canOnlyBeCalledOnce(next, to, from) {
|
|
let called = 0;
|
|
return function() {
|
|
if (called++ === 1)
|
|
warn(`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`);
|
|
next._called = true;
|
|
if (called === 1)
|
|
next.apply(null, arguments);
|
|
};
|
|
}
|
|
function extractComponentsGuards(matched, guardType, to, from) {
|
|
const guards = [];
|
|
for (const record of matched) {
|
|
for (const name in record.components) {
|
|
let rawComponent = record.components[name];
|
|
if (true) {
|
|
if (!rawComponent || typeof rawComponent !== "object" && typeof rawComponent !== "function") {
|
|
warn(`Component "${name}" in record with path "${record.path}" is not a valid component. Received "${String(rawComponent)}".`);
|
|
throw new Error("Invalid route component");
|
|
} else if ("then" in rawComponent) {
|
|
warn(`Component "${name}" in record with path "${record.path}" is a Promise instead of a function that returns a Promise. Did you write "import('./MyPage.vue')" instead of "() => import('./MyPage.vue')" ? This will break in production if not fixed.`);
|
|
const promise = rawComponent;
|
|
rawComponent = () => promise;
|
|
} else if (rawComponent.__asyncLoader && // warn only once per component
|
|
!rawComponent.__warnedDefineAsync) {
|
|
rawComponent.__warnedDefineAsync = true;
|
|
warn(`Component "${name}" in record with path "${record.path}" is defined using "defineAsyncComponent()". Write "() => import('./MyPage.vue')" instead of "defineAsyncComponent(() => import('./MyPage.vue'))".`);
|
|
}
|
|
}
|
|
if (guardType !== "beforeRouteEnter" && !record.instances[name])
|
|
continue;
|
|
if (isRouteComponent(rawComponent)) {
|
|
const options = rawComponent.__vccOpts || rawComponent;
|
|
const guard = options[guardType];
|
|
guard && guards.push(guardToPromiseFn(guard, to, from, record, name));
|
|
} else {
|
|
let componentPromise = rawComponent();
|
|
if (!("catch" in componentPromise)) {
|
|
warn(`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`);
|
|
componentPromise = Promise.resolve(componentPromise);
|
|
}
|
|
guards.push(() => componentPromise.then((resolved) => {
|
|
if (!resolved)
|
|
return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}"`));
|
|
const resolvedComponent = isESModule(resolved) ? resolved.default : resolved;
|
|
record.components[name] = resolvedComponent;
|
|
const options = resolvedComponent.__vccOpts || resolvedComponent;
|
|
const guard = options[guardType];
|
|
return guard && guardToPromiseFn(guard, to, from, record, name)();
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
return guards;
|
|
}
|
|
function isRouteComponent(component) {
|
|
return typeof component === "object" || "displayName" in component || "props" in component || "__vccOpts" in component;
|
|
}
|
|
function useLink(props) {
|
|
const router = inject(routerKey);
|
|
const currentRoute = inject(routeLocationKey);
|
|
const route = computed(() => router.resolve(unref(props.to)));
|
|
const activeRecordIndex = computed(() => {
|
|
const { matched } = route.value;
|
|
const { length } = matched;
|
|
const routeMatched = matched[length - 1];
|
|
const currentMatched = currentRoute.matched;
|
|
if (!routeMatched || !currentMatched.length)
|
|
return -1;
|
|
const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched));
|
|
if (index > -1)
|
|
return index;
|
|
const parentRecordPath = getOriginalPath(matched[length - 2]);
|
|
return (
|
|
// we are dealing with nested routes
|
|
length > 1 && // if the parent and matched route have the same path, this link is
|
|
// referring to the empty child. Or we currently are on a different
|
|
// child of the same parent
|
|
getOriginalPath(routeMatched) === parentRecordPath && // avoid comparing the child with its parent
|
|
currentMatched[currentMatched.length - 1].path !== parentRecordPath ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2])) : index
|
|
);
|
|
});
|
|
const isActive = computed(() => activeRecordIndex.value > -1 && includesParams(currentRoute.params, route.value.params));
|
|
const isExactActive = computed(() => activeRecordIndex.value > -1 && activeRecordIndex.value === currentRoute.matched.length - 1 && isSameRouteLocationParams(currentRoute.params, route.value.params));
|
|
function navigate(e = {}) {
|
|
if (guardEvent(e)) {
|
|
return router[unref(props.replace) ? "replace" : "push"](
|
|
unref(props.to)
|
|
// avoid uncaught errors are they are logged anyway
|
|
).catch(noop);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
if (isBrowser) {
|
|
const instance = getCurrentInstance();
|
|
if (instance) {
|
|
const linkContextDevtools = {
|
|
route: route.value,
|
|
isActive: isActive.value,
|
|
isExactActive: isExactActive.value
|
|
};
|
|
instance.__vrl_devtools = instance.__vrl_devtools || [];
|
|
instance.__vrl_devtools.push(linkContextDevtools);
|
|
watchEffect(() => {
|
|
linkContextDevtools.route = route.value;
|
|
linkContextDevtools.isActive = isActive.value;
|
|
linkContextDevtools.isExactActive = isExactActive.value;
|
|
}, { flush: "post" });
|
|
}
|
|
}
|
|
return {
|
|
route,
|
|
href: computed(() => route.value.href),
|
|
isActive,
|
|
isExactActive,
|
|
navigate
|
|
};
|
|
}
|
|
var RouterLinkImpl = defineComponent({
|
|
name: "RouterLink",
|
|
props: {
|
|
to: {
|
|
type: [String, Object],
|
|
required: true
|
|
},
|
|
replace: Boolean,
|
|
activeClass: String,
|
|
// inactiveClass: String,
|
|
exactActiveClass: String,
|
|
custom: Boolean,
|
|
ariaCurrentValue: {
|
|
type: String,
|
|
default: "page"
|
|
}
|
|
},
|
|
useLink,
|
|
setup(props, { slots }) {
|
|
const link = reactive(useLink(props));
|
|
const { options } = inject(routerKey);
|
|
const elClass = computed(() => ({
|
|
[getLinkClass(props.activeClass, options.linkActiveClass, "router-link-active")]: link.isActive,
|
|
// [getLinkClass(
|
|
// props.inactiveClass,
|
|
// options.linkInactiveClass,
|
|
// 'router-link-inactive'
|
|
// )]: !link.isExactActive,
|
|
[getLinkClass(props.exactActiveClass, options.linkExactActiveClass, "router-link-exact-active")]: link.isExactActive
|
|
}));
|
|
return () => {
|
|
const children = slots.default && slots.default(link);
|
|
return props.custom ? children : h("a", {
|
|
"aria-current": link.isExactActive ? props.ariaCurrentValue : null,
|
|
href: link.href,
|
|
// this would override user added attrs but Vue will still add
|
|
// the listener so we end up triggering both
|
|
onClick: link.navigate,
|
|
class: elClass.value
|
|
}, children);
|
|
};
|
|
}
|
|
});
|
|
var RouterLink = RouterLinkImpl;
|
|
function guardEvent(e) {
|
|
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
return;
|
|
if (e.defaultPrevented)
|
|
return;
|
|
if (e.button !== void 0 && e.button !== 0)
|
|
return;
|
|
if (e.currentTarget && e.currentTarget.getAttribute) {
|
|
const target = e.currentTarget.getAttribute("target");
|
|
if (/\b_blank\b/i.test(target))
|
|
return;
|
|
}
|
|
if (e.preventDefault)
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
function includesParams(outer, inner) {
|
|
for (const key in inner) {
|
|
const innerValue = inner[key];
|
|
const outerValue = outer[key];
|
|
if (typeof innerValue === "string") {
|
|
if (innerValue !== outerValue)
|
|
return false;
|
|
} else {
|
|
if (!Array.isArray(outerValue) || outerValue.length !== innerValue.length || innerValue.some((value, i) => value !== outerValue[i]))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function getOriginalPath(record) {
|
|
return record ? record.aliasOf ? record.aliasOf.path : record.path : "";
|
|
}
|
|
var getLinkClass = (propClass, globalClass, defaultClass) => propClass != null ? propClass : globalClass != null ? globalClass : defaultClass;
|
|
var RouterViewImpl = defineComponent({
|
|
name: "RouterView",
|
|
// #674 we manually inherit them
|
|
inheritAttrs: false,
|
|
props: {
|
|
name: {
|
|
type: String,
|
|
default: "default"
|
|
},
|
|
route: Object
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
warnDeprecatedUsage();
|
|
const injectedRoute = inject(routerViewLocationKey);
|
|
const routeToDisplay = computed(() => props.route || injectedRoute.value);
|
|
const depth = inject(viewDepthKey, 0);
|
|
const matchedRouteRef = computed(() => routeToDisplay.value.matched[depth]);
|
|
provide(viewDepthKey, depth + 1);
|
|
provide(matchedRouteKey, matchedRouteRef);
|
|
provide(routerViewLocationKey, routeToDisplay);
|
|
const viewRef = ref();
|
|
watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => {
|
|
if (to) {
|
|
to.instances[name] = instance;
|
|
if (from && from !== to && instance && instance === oldInstance) {
|
|
if (!to.leaveGuards.size) {
|
|
to.leaveGuards = from.leaveGuards;
|
|
}
|
|
if (!to.updateGuards.size) {
|
|
to.updateGuards = from.updateGuards;
|
|
}
|
|
}
|
|
}
|
|
if (instance && to && // if there is no instance but to and from are the same this might be
|
|
// the first visit
|
|
(!from || !isSameRouteRecord(to, from) || !oldInstance)) {
|
|
(to.enterCallbacks[name] || []).forEach((callback) => callback(instance));
|
|
}
|
|
}, { flush: "post" });
|
|
return () => {
|
|
const route = routeToDisplay.value;
|
|
const matchedRoute = matchedRouteRef.value;
|
|
const ViewComponent = matchedRoute && matchedRoute.components[props.name];
|
|
const currentName = props.name;
|
|
if (!ViewComponent) {
|
|
return normalizeSlot(slots.default, { Component: ViewComponent, route });
|
|
}
|
|
const routePropsOption = matchedRoute.props[props.name];
|
|
const routeProps = routePropsOption ? routePropsOption === true ? route.params : typeof routePropsOption === "function" ? routePropsOption(route) : routePropsOption : null;
|
|
const onVnodeUnmounted = (vnode) => {
|
|
if (vnode.component.isUnmounted) {
|
|
matchedRoute.instances[currentName] = null;
|
|
}
|
|
};
|
|
const component = h(ViewComponent, assign({}, routeProps, attrs, {
|
|
onVnodeUnmounted,
|
|
ref: viewRef
|
|
}));
|
|
if (isBrowser && component.ref) {
|
|
const info = {
|
|
depth,
|
|
name: matchedRoute.name,
|
|
path: matchedRoute.path,
|
|
meta: matchedRoute.meta
|
|
};
|
|
const internalInstances = Array.isArray(component.ref) ? component.ref.map((r) => r.i) : [component.ref.i];
|
|
internalInstances.forEach((instance) => {
|
|
instance.__vrv_devtools = info;
|
|
});
|
|
}
|
|
return (
|
|
// pass the vnode to the slot as a prop.
|
|
// h and <component :is="..."> both accept vnodes
|
|
normalizeSlot(slots.default, { Component: component, route }) || component
|
|
);
|
|
};
|
|
}
|
|
});
|
|
function normalizeSlot(slot, data) {
|
|
if (!slot)
|
|
return null;
|
|
const slotContent = slot(data);
|
|
return slotContent.length === 1 ? slotContent[0] : slotContent;
|
|
}
|
|
var RouterView = RouterViewImpl;
|
|
function warnDeprecatedUsage() {
|
|
const instance = getCurrentInstance();
|
|
const parentName = instance.parent && instance.parent.type.name;
|
|
if (parentName && (parentName === "KeepAlive" || parentName.includes("Transition"))) {
|
|
const comp = parentName === "KeepAlive" ? "keep-alive" : "transition";
|
|
warn(`<router-view> can no longer be used directly inside <transition> or <keep-alive>.
|
|
Use slot props instead:
|
|
|
|
<router-view v-slot="{ Component }">
|
|
<${comp}>
|
|
<component :is="Component" />
|
|
</${comp}>
|
|
</router-view>`);
|
|
}
|
|
}
|
|
function formatRouteLocation(routeLocation, tooltip) {
|
|
const copy = assign({}, routeLocation, {
|
|
// remove variables that can contain vue instances
|
|
matched: routeLocation.matched.map((matched) => omit(matched, ["instances", "children", "aliasOf"]))
|
|
});
|
|
return {
|
|
_custom: {
|
|
type: null,
|
|
readOnly: true,
|
|
display: routeLocation.fullPath,
|
|
tooltip,
|
|
value: copy
|
|
}
|
|
};
|
|
}
|
|
function formatDisplay(display) {
|
|
return {
|
|
_custom: {
|
|
display
|
|
}
|
|
};
|
|
}
|
|
var routerId = 0;
|
|
function addDevtools(app, router, matcher) {
|
|
if (router.__hasDevtools)
|
|
return;
|
|
router.__hasDevtools = true;
|
|
const id = routerId++;
|
|
setupDevtoolsPlugin({
|
|
id: "org.vuejs.router" + (id ? "." + id : ""),
|
|
label: "Vue Router",
|
|
packageName: "vue-router",
|
|
homepage: "https://router.vuejs.org",
|
|
logo: "https://router.vuejs.org/logo.png",
|
|
componentStateTypes: ["Routing"],
|
|
app
|
|
}, (api) => {
|
|
api.on.inspectComponent((payload, ctx) => {
|
|
if (payload.instanceData) {
|
|
payload.instanceData.state.push({
|
|
type: "Routing",
|
|
key: "$route",
|
|
editable: false,
|
|
value: formatRouteLocation(router.currentRoute.value, "Current Route")
|
|
});
|
|
}
|
|
});
|
|
api.on.visitComponentTree(({ treeNode: node, componentInstance }) => {
|
|
if (componentInstance.__vrv_devtools) {
|
|
const info = componentInstance.__vrv_devtools;
|
|
node.tags.push({
|
|
label: (info.name ? `${info.name.toString()}: ` : "") + info.path,
|
|
textColor: 0,
|
|
tooltip: "This component is rendered by <router-view>",
|
|
backgroundColor: PINK_500
|
|
});
|
|
}
|
|
if (Array.isArray(componentInstance.__vrl_devtools)) {
|
|
componentInstance.__devtoolsApi = api;
|
|
componentInstance.__vrl_devtools.forEach((devtoolsData) => {
|
|
let backgroundColor = ORANGE_400;
|
|
let tooltip = "";
|
|
if (devtoolsData.isExactActive) {
|
|
backgroundColor = LIME_500;
|
|
tooltip = "This is exactly active";
|
|
} else if (devtoolsData.isActive) {
|
|
backgroundColor = BLUE_600;
|
|
tooltip = "This link is active";
|
|
}
|
|
node.tags.push({
|
|
label: devtoolsData.route.path,
|
|
textColor: 0,
|
|
tooltip,
|
|
backgroundColor
|
|
});
|
|
});
|
|
}
|
|
});
|
|
watch(router.currentRoute, () => {
|
|
refreshRoutesView();
|
|
api.notifyComponentUpdate();
|
|
api.sendInspectorTree(routerInspectorId);
|
|
api.sendInspectorState(routerInspectorId);
|
|
});
|
|
const navigationsLayerId = "router:navigations:" + id;
|
|
api.addTimelineLayer({
|
|
id: navigationsLayerId,
|
|
label: `Router${id ? " " + id : ""} Navigations`,
|
|
color: 4237508
|
|
});
|
|
router.onError((error, to) => {
|
|
api.addTimelineEvent({
|
|
layerId: navigationsLayerId,
|
|
event: {
|
|
title: "Error during Navigation",
|
|
subtitle: to.fullPath,
|
|
logType: "error",
|
|
time: Date.now(),
|
|
data: { error },
|
|
groupId: to.meta.__navigationId
|
|
}
|
|
});
|
|
});
|
|
let navigationId = 0;
|
|
router.beforeEach((to, from) => {
|
|
const data = {
|
|
guard: formatDisplay("beforeEach"),
|
|
from: formatRouteLocation(from, "Current Location during this navigation"),
|
|
to: formatRouteLocation(to, "Target location")
|
|
};
|
|
Object.defineProperty(to.meta, "__navigationId", {
|
|
value: navigationId++
|
|
});
|
|
api.addTimelineEvent({
|
|
layerId: navigationsLayerId,
|
|
event: {
|
|
time: Date.now(),
|
|
title: "Start of navigation",
|
|
subtitle: to.fullPath,
|
|
data,
|
|
groupId: to.meta.__navigationId
|
|
}
|
|
});
|
|
});
|
|
router.afterEach((to, from, failure) => {
|
|
const data = {
|
|
guard: formatDisplay("afterEach")
|
|
};
|
|
if (failure) {
|
|
data.failure = {
|
|
_custom: {
|
|
type: Error,
|
|
readOnly: true,
|
|
display: failure ? failure.message : "",
|
|
tooltip: "Navigation Failure",
|
|
value: failure
|
|
}
|
|
};
|
|
data.status = formatDisplay("❌");
|
|
} else {
|
|
data.status = formatDisplay("✅");
|
|
}
|
|
data.from = formatRouteLocation(from, "Current Location during this navigation");
|
|
data.to = formatRouteLocation(to, "Target location");
|
|
api.addTimelineEvent({
|
|
layerId: navigationsLayerId,
|
|
event: {
|
|
title: "End of navigation",
|
|
subtitle: to.fullPath,
|
|
time: Date.now(),
|
|
data,
|
|
logType: failure ? "warning" : "default",
|
|
groupId: to.meta.__navigationId
|
|
}
|
|
});
|
|
});
|
|
const routerInspectorId = "router-inspector:" + id;
|
|
api.addInspector({
|
|
id: routerInspectorId,
|
|
label: "Routes" + (id ? " " + id : ""),
|
|
icon: "book",
|
|
treeFilterPlaceholder: "Search routes"
|
|
});
|
|
function refreshRoutesView() {
|
|
if (!activeRoutesPayload)
|
|
return;
|
|
const payload = activeRoutesPayload;
|
|
let routes = matcher.getRoutes().filter((route) => !route.parent);
|
|
routes.forEach(resetMatchStateOnRouteRecord);
|
|
if (payload.filter) {
|
|
routes = routes.filter((route) => (
|
|
// save matches state based on the payload
|
|
isRouteMatching(route, payload.filter.toLowerCase())
|
|
));
|
|
}
|
|
routes.forEach((route) => markRouteRecordActive(route, router.currentRoute.value));
|
|
payload.rootNodes = routes.map(formatRouteRecordForInspector);
|
|
}
|
|
let activeRoutesPayload;
|
|
api.on.getInspectorTree((payload) => {
|
|
activeRoutesPayload = payload;
|
|
if (payload.app === app && payload.inspectorId === routerInspectorId) {
|
|
refreshRoutesView();
|
|
}
|
|
});
|
|
api.on.getInspectorState((payload) => {
|
|
if (payload.app === app && payload.inspectorId === routerInspectorId) {
|
|
const routes = matcher.getRoutes();
|
|
const route = routes.find((route2) => route2.record.__vd_id === payload.nodeId);
|
|
if (route) {
|
|
payload.state = {
|
|
options: formatRouteRecordMatcherForStateInspector(route)
|
|
};
|
|
}
|
|
}
|
|
});
|
|
api.sendInspectorTree(routerInspectorId);
|
|
api.sendInspectorState(routerInspectorId);
|
|
});
|
|
}
|
|
function modifierForKey(key) {
|
|
if (key.optional) {
|
|
return key.repeatable ? "*" : "?";
|
|
} else {
|
|
return key.repeatable ? "+" : "";
|
|
}
|
|
}
|
|
function formatRouteRecordMatcherForStateInspector(route) {
|
|
const { record } = route;
|
|
const fields = [
|
|
{ editable: false, key: "path", value: record.path }
|
|
];
|
|
if (record.name != null) {
|
|
fields.push({
|
|
editable: false,
|
|
key: "name",
|
|
value: record.name
|
|
});
|
|
}
|
|
fields.push({ editable: false, key: "regexp", value: route.re });
|
|
if (route.keys.length) {
|
|
fields.push({
|
|
editable: false,
|
|
key: "keys",
|
|
value: {
|
|
_custom: {
|
|
type: null,
|
|
readOnly: true,
|
|
display: route.keys.map((key) => `${key.name}${modifierForKey(key)}`).join(" "),
|
|
tooltip: "Param keys",
|
|
value: route.keys
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (record.redirect != null) {
|
|
fields.push({
|
|
editable: false,
|
|
key: "redirect",
|
|
value: record.redirect
|
|
});
|
|
}
|
|
if (route.alias.length) {
|
|
fields.push({
|
|
editable: false,
|
|
key: "aliases",
|
|
value: route.alias.map((alias) => alias.record.path)
|
|
});
|
|
}
|
|
fields.push({
|
|
key: "score",
|
|
editable: false,
|
|
value: {
|
|
_custom: {
|
|
type: null,
|
|
readOnly: true,
|
|
display: route.score.map((score) => score.join(", ")).join(" | "),
|
|
tooltip: "Score used to sort routes",
|
|
value: route.score
|
|
}
|
|
}
|
|
});
|
|
return fields;
|
|
}
|
|
var PINK_500 = 15485081;
|
|
var BLUE_600 = 2450411;
|
|
var LIME_500 = 8702998;
|
|
var CYAN_400 = 2282478;
|
|
var ORANGE_400 = 16486972;
|
|
var DARK = 6710886;
|
|
function formatRouteRecordForInspector(route) {
|
|
const tags = [];
|
|
const { record } = route;
|
|
if (record.name != null) {
|
|
tags.push({
|
|
label: String(record.name),
|
|
textColor: 0,
|
|
backgroundColor: CYAN_400
|
|
});
|
|
}
|
|
if (record.aliasOf) {
|
|
tags.push({
|
|
label: "alias",
|
|
textColor: 0,
|
|
backgroundColor: ORANGE_400
|
|
});
|
|
}
|
|
if (route.__vd_match) {
|
|
tags.push({
|
|
label: "matches",
|
|
textColor: 0,
|
|
backgroundColor: PINK_500
|
|
});
|
|
}
|
|
if (route.__vd_exactActive) {
|
|
tags.push({
|
|
label: "exact",
|
|
textColor: 0,
|
|
backgroundColor: LIME_500
|
|
});
|
|
}
|
|
if (route.__vd_active) {
|
|
tags.push({
|
|
label: "active",
|
|
textColor: 0,
|
|
backgroundColor: BLUE_600
|
|
});
|
|
}
|
|
if (record.redirect) {
|
|
tags.push({
|
|
label: "redirect: " + (typeof record.redirect === "string" ? record.redirect : "Object"),
|
|
textColor: 16777215,
|
|
backgroundColor: DARK
|
|
});
|
|
}
|
|
let id = record.__vd_id;
|
|
if (id == null) {
|
|
id = String(routeRecordId++);
|
|
record.__vd_id = id;
|
|
}
|
|
return {
|
|
id,
|
|
label: record.path,
|
|
tags,
|
|
children: route.children.map(formatRouteRecordForInspector)
|
|
};
|
|
}
|
|
var routeRecordId = 0;
|
|
var EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/;
|
|
function markRouteRecordActive(route, currentRoute) {
|
|
const isExactActive = currentRoute.matched.length && isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
|
|
route.__vd_exactActive = route.__vd_active = isExactActive;
|
|
if (!isExactActive) {
|
|
route.__vd_active = currentRoute.matched.some((match) => isSameRouteRecord(match, route.record));
|
|
}
|
|
route.children.forEach((childRoute) => markRouteRecordActive(childRoute, currentRoute));
|
|
}
|
|
function resetMatchStateOnRouteRecord(route) {
|
|
route.__vd_match = false;
|
|
route.children.forEach(resetMatchStateOnRouteRecord);
|
|
}
|
|
function isRouteMatching(route, filter) {
|
|
const found = String(route.re).match(EXTRACT_REGEXP_RE);
|
|
route.__vd_match = false;
|
|
if (!found || found.length < 3) {
|
|
return false;
|
|
}
|
|
const nonEndingRE = new RegExp(found[1].replace(/\$$/, ""), found[2]);
|
|
if (nonEndingRE.test(filter)) {
|
|
route.children.forEach((child) => isRouteMatching(child, filter));
|
|
if (route.record.path !== "/" || filter === "/") {
|
|
route.__vd_match = route.re.test(filter);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
const path = route.record.path.toLowerCase();
|
|
const decodedPath = decode(path);
|
|
if (!filter.startsWith("/") && (decodedPath.includes(filter) || path.includes(filter)))
|
|
return true;
|
|
if (decodedPath.startsWith(filter) || path.startsWith(filter))
|
|
return true;
|
|
if (route.record.name && String(route.record.name).includes(filter))
|
|
return true;
|
|
return route.children.some((child) => isRouteMatching(child, filter));
|
|
}
|
|
function omit(obj, keys) {
|
|
const ret = {};
|
|
for (const key in obj) {
|
|
if (!keys.includes(key)) {
|
|
ret[key] = obj[key];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
function createRouter(options) {
|
|
const matcher = createRouterMatcher(options.routes, options);
|
|
const parseQuery$1 = options.parseQuery || parseQuery;
|
|
const stringifyQuery$1 = options.stringifyQuery || stringifyQuery;
|
|
const routerHistory = options.history;
|
|
if (!routerHistory)
|
|
throw new Error('Provide the "history" option when calling "createRouter()": https://next.router.vuejs.org/api/#history.');
|
|
const beforeGuards = useCallbacks();
|
|
const beforeResolveGuards = useCallbacks();
|
|
const afterGuards = useCallbacks();
|
|
const currentRoute = shallowRef(START_LOCATION_NORMALIZED);
|
|
let pendingLocation = START_LOCATION_NORMALIZED;
|
|
if (isBrowser && options.scrollBehavior && "scrollRestoration" in history) {
|
|
history.scrollRestoration = "manual";
|
|
}
|
|
const normalizeParams = applyToParams.bind(null, (paramValue) => "" + paramValue);
|
|
const encodeParams = applyToParams.bind(null, encodeParam);
|
|
const decodeParams = (
|
|
// @ts-expect-error: intentionally avoid the type check
|
|
applyToParams.bind(null, decode)
|
|
);
|
|
function addRoute(parentOrRoute, route) {
|
|
let parent;
|
|
let record;
|
|
if (isRouteName(parentOrRoute)) {
|
|
parent = matcher.getRecordMatcher(parentOrRoute);
|
|
record = route;
|
|
} else {
|
|
record = parentOrRoute;
|
|
}
|
|
return matcher.addRoute(record, parent);
|
|
}
|
|
function removeRoute(name) {
|
|
const recordMatcher = matcher.getRecordMatcher(name);
|
|
if (recordMatcher) {
|
|
matcher.removeRoute(recordMatcher);
|
|
} else if (true) {
|
|
warn(`Cannot remove non-existent route "${String(name)}"`);
|
|
}
|
|
}
|
|
function getRoutes() {
|
|
return matcher.getRoutes().map((routeMatcher) => routeMatcher.record);
|
|
}
|
|
function hasRoute(name) {
|
|
return !!matcher.getRecordMatcher(name);
|
|
}
|
|
function resolve(rawLocation, currentLocation) {
|
|
currentLocation = assign({}, currentLocation || currentRoute.value);
|
|
if (typeof rawLocation === "string") {
|
|
const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path);
|
|
const matchedRoute2 = matcher.resolve({ path: locationNormalized.path }, currentLocation);
|
|
const href2 = routerHistory.createHref(locationNormalized.fullPath);
|
|
if (true) {
|
|
if (href2.startsWith("//"))
|
|
warn(`Location "${rawLocation}" resolved to "${href2}". A resolved location cannot start with multiple slashes.`);
|
|
else if (!matchedRoute2.matched.length) {
|
|
warn(`No match found for location with path "${rawLocation}"`);
|
|
}
|
|
}
|
|
return assign(locationNormalized, matchedRoute2, {
|
|
params: decodeParams(matchedRoute2.params),
|
|
hash: decode(locationNormalized.hash),
|
|
redirectedFrom: void 0,
|
|
href: href2
|
|
});
|
|
}
|
|
let matcherLocation;
|
|
if ("path" in rawLocation) {
|
|
if ("params" in rawLocation && !("name" in rawLocation) && // @ts-expect-error: the type is never
|
|
Object.keys(rawLocation.params).length) {
|
|
warn(`Path "${// @ts-expect-error: the type is never
|
|
rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`);
|
|
}
|
|
matcherLocation = assign({}, rawLocation, {
|
|
path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path
|
|
});
|
|
} else {
|
|
const targetParams = assign({}, rawLocation.params);
|
|
for (const key in targetParams) {
|
|
if (targetParams[key] == null) {
|
|
delete targetParams[key];
|
|
}
|
|
}
|
|
matcherLocation = assign({}, rawLocation, {
|
|
params: encodeParams(rawLocation.params)
|
|
});
|
|
currentLocation.params = encodeParams(currentLocation.params);
|
|
}
|
|
const matchedRoute = matcher.resolve(matcherLocation, currentLocation);
|
|
const hash = rawLocation.hash || "";
|
|
if (hash && !hash.startsWith("#")) {
|
|
warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
|
|
}
|
|
matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
|
|
const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
|
|
hash: encodeHash(hash),
|
|
path: matchedRoute.path
|
|
}));
|
|
const href = routerHistory.createHref(fullPath);
|
|
if (true) {
|
|
if (href.startsWith("//")) {
|
|
warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
|
|
} else if (!matchedRoute.matched.length) {
|
|
warn(`No match found for location with path "${"path" in rawLocation ? rawLocation.path : rawLocation}"`);
|
|
}
|
|
}
|
|
return assign({
|
|
fullPath,
|
|
// keep the hash encoded so fullPath is effectively path + encodedQuery +
|
|
// hash
|
|
hash,
|
|
query: (
|
|
// if the user is using a custom query lib like qs, we might have
|
|
// nested objects, so we keep the query as is, meaning it can contain
|
|
// numbers at `$route.query`, but at the point, the user will have to
|
|
// use their own type anyway.
|
|
// https://github.com/vuejs/router/issues/328#issuecomment-649481567
|
|
stringifyQuery$1 === stringifyQuery ? normalizeQuery(rawLocation.query) : rawLocation.query || {}
|
|
)
|
|
}, matchedRoute, {
|
|
redirectedFrom: void 0,
|
|
href
|
|
});
|
|
}
|
|
function locationAsObject(to) {
|
|
return typeof to === "string" ? parseURL(parseQuery$1, to, currentRoute.value.path) : assign({}, to);
|
|
}
|
|
function checkCanceledNavigation(to, from) {
|
|
if (pendingLocation !== to) {
|
|
return createRouterError(8, {
|
|
from,
|
|
to
|
|
});
|
|
}
|
|
}
|
|
function push(to) {
|
|
return pushWithRedirect(to);
|
|
}
|
|
function replace(to) {
|
|
return push(assign(locationAsObject(to), { replace: true }));
|
|
}
|
|
function handleRedirectRecord(to) {
|
|
const lastMatched = to.matched[to.matched.length - 1];
|
|
if (lastMatched && lastMatched.redirect) {
|
|
const { redirect } = lastMatched;
|
|
let newTargetLocation = typeof redirect === "function" ? redirect(to) : redirect;
|
|
if (typeof newTargetLocation === "string") {
|
|
newTargetLocation = newTargetLocation.includes("?") || newTargetLocation.includes("#") ? newTargetLocation = locationAsObject(newTargetLocation) : (
|
|
// force empty params
|
|
{ path: newTargetLocation }
|
|
);
|
|
newTargetLocation.params = {};
|
|
}
|
|
if (!("path" in newTargetLocation) && !("name" in newTargetLocation)) {
|
|
warn(`Invalid redirect found:
|
|
${JSON.stringify(newTargetLocation, null, 2)}
|
|
when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`);
|
|
throw new Error("Invalid redirect");
|
|
}
|
|
return assign({
|
|
query: to.query,
|
|
hash: to.hash,
|
|
params: to.params
|
|
}, newTargetLocation);
|
|
}
|
|
}
|
|
function pushWithRedirect(to, redirectedFrom) {
|
|
const targetLocation = pendingLocation = resolve(to);
|
|
const from = currentRoute.value;
|
|
const data = to.state;
|
|
const force = to.force;
|
|
const replace2 = to.replace === true;
|
|
const shouldRedirect = handleRedirectRecord(targetLocation);
|
|
if (shouldRedirect)
|
|
return pushWithRedirect(
|
|
assign(locationAsObject(shouldRedirect), {
|
|
state: data,
|
|
force,
|
|
replace: replace2
|
|
}),
|
|
// keep original redirectedFrom if it exists
|
|
redirectedFrom || targetLocation
|
|
);
|
|
const toLocation = targetLocation;
|
|
toLocation.redirectedFrom = redirectedFrom;
|
|
let failure;
|
|
if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
|
|
failure = createRouterError(16, { to: toLocation, from });
|
|
handleScroll(
|
|
from,
|
|
from,
|
|
// this is a push, the only way for it to be triggered from a
|
|
// history.listen is with a redirect, which makes it become a push
|
|
true,
|
|
// This cannot be the first navigation because the initial location
|
|
// cannot be manually navigated to
|
|
false
|
|
);
|
|
}
|
|
return (failure ? Promise.resolve(failure) : navigate(toLocation, from)).catch((error) => isNavigationFailure(error) ? (
|
|
// navigation redirects still mark the router as ready
|
|
isNavigationFailure(
|
|
error,
|
|
2
|
|
/* NAVIGATION_GUARD_REDIRECT */
|
|
) ? error : markAsReady(error)
|
|
) : (
|
|
// reject any unknown error
|
|
triggerError(error, toLocation, from)
|
|
)).then((failure2) => {
|
|
if (failure2) {
|
|
if (isNavigationFailure(
|
|
failure2,
|
|
2
|
|
/* NAVIGATION_GUARD_REDIRECT */
|
|
)) {
|
|
if (// we are redirecting to the same location we were already at
|
|
isSameRouteLocation(stringifyQuery$1, resolve(failure2.to), toLocation) && // and we have done it a couple of times
|
|
redirectedFrom && // @ts-expect-error: added only in dev
|
|
(redirectedFrom._count = redirectedFrom._count ? (
|
|
// @ts-expect-error
|
|
redirectedFrom._count + 1
|
|
) : 1) > 10) {
|
|
warn(`Detected an infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow. This will break in production if not fixed.`);
|
|
return Promise.reject(new Error("Infinite redirect in navigation guard"));
|
|
}
|
|
return pushWithRedirect(
|
|
// keep options
|
|
assign(locationAsObject(failure2.to), {
|
|
state: data,
|
|
force,
|
|
replace: replace2
|
|
}),
|
|
// preserve the original redirectedFrom if any
|
|
redirectedFrom || toLocation
|
|
);
|
|
}
|
|
} else {
|
|
failure2 = finalizeNavigation(toLocation, from, true, replace2, data);
|
|
}
|
|
triggerAfterEach(toLocation, from, failure2);
|
|
return failure2;
|
|
});
|
|
}
|
|
function checkCanceledNavigationAndReject(to, from) {
|
|
const error = checkCanceledNavigation(to, from);
|
|
return error ? Promise.reject(error) : Promise.resolve();
|
|
}
|
|
function navigate(to, from) {
|
|
let guards;
|
|
const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from);
|
|
guards = extractComponentsGuards(leavingRecords.reverse(), "beforeRouteLeave", to, from);
|
|
for (const record of leavingRecords) {
|
|
record.leaveGuards.forEach((guard) => {
|
|
guards.push(guardToPromiseFn(guard, to, from));
|
|
});
|
|
}
|
|
const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from);
|
|
guards.push(canceledNavigationCheck);
|
|
return runGuardQueue(guards).then(() => {
|
|
guards = [];
|
|
for (const guard of beforeGuards.list()) {
|
|
guards.push(guardToPromiseFn(guard, to, from));
|
|
}
|
|
guards.push(canceledNavigationCheck);
|
|
return runGuardQueue(guards);
|
|
}).then(() => {
|
|
guards = extractComponentsGuards(updatingRecords, "beforeRouteUpdate", to, from);
|
|
for (const record of updatingRecords) {
|
|
record.updateGuards.forEach((guard) => {
|
|
guards.push(guardToPromiseFn(guard, to, from));
|
|
});
|
|
}
|
|
guards.push(canceledNavigationCheck);
|
|
return runGuardQueue(guards);
|
|
}).then(() => {
|
|
guards = [];
|
|
for (const record of to.matched) {
|
|
if (record.beforeEnter && !from.matched.includes(record)) {
|
|
if (Array.isArray(record.beforeEnter)) {
|
|
for (const beforeEnter of record.beforeEnter)
|
|
guards.push(guardToPromiseFn(beforeEnter, to, from));
|
|
} else {
|
|
guards.push(guardToPromiseFn(record.beforeEnter, to, from));
|
|
}
|
|
}
|
|
}
|
|
guards.push(canceledNavigationCheck);
|
|
return runGuardQueue(guards);
|
|
}).then(() => {
|
|
to.matched.forEach((record) => record.enterCallbacks = {});
|
|
guards = extractComponentsGuards(enteringRecords, "beforeRouteEnter", to, from);
|
|
guards.push(canceledNavigationCheck);
|
|
return runGuardQueue(guards);
|
|
}).then(() => {
|
|
guards = [];
|
|
for (const guard of beforeResolveGuards.list()) {
|
|
guards.push(guardToPromiseFn(guard, to, from));
|
|
}
|
|
guards.push(canceledNavigationCheck);
|
|
return runGuardQueue(guards);
|
|
}).catch((err) => isNavigationFailure(
|
|
err,
|
|
8
|
|
/* NAVIGATION_CANCELLED */
|
|
) ? err : Promise.reject(err));
|
|
}
|
|
function triggerAfterEach(to, from, failure) {
|
|
for (const guard of afterGuards.list())
|
|
guard(to, from, failure);
|
|
}
|
|
function finalizeNavigation(toLocation, from, isPush, replace2, data) {
|
|
const error = checkCanceledNavigation(toLocation, from);
|
|
if (error)
|
|
return error;
|
|
const isFirstNavigation = from === START_LOCATION_NORMALIZED;
|
|
const state = !isBrowser ? {} : history.state;
|
|
if (isPush) {
|
|
if (replace2 || isFirstNavigation)
|
|
routerHistory.replace(toLocation.fullPath, assign({
|
|
scroll: isFirstNavigation && state && state.scroll
|
|
}, data));
|
|
else
|
|
routerHistory.push(toLocation.fullPath, data);
|
|
}
|
|
currentRoute.value = toLocation;
|
|
handleScroll(toLocation, from, isPush, isFirstNavigation);
|
|
markAsReady();
|
|
}
|
|
let removeHistoryListener;
|
|
function setupListeners() {
|
|
removeHistoryListener = routerHistory.listen((to, _from, info) => {
|
|
const toLocation = resolve(to);
|
|
const shouldRedirect = handleRedirectRecord(toLocation);
|
|
if (shouldRedirect) {
|
|
pushWithRedirect(assign(shouldRedirect, { replace: true }), toLocation).catch(noop);
|
|
return;
|
|
}
|
|
pendingLocation = toLocation;
|
|
const from = currentRoute.value;
|
|
if (isBrowser) {
|
|
saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition());
|
|
}
|
|
navigate(toLocation, from).catch((error) => {
|
|
if (isNavigationFailure(
|
|
error,
|
|
4 | 8
|
|
/* NAVIGATION_CANCELLED */
|
|
)) {
|
|
return error;
|
|
}
|
|
if (isNavigationFailure(
|
|
error,
|
|
2
|
|
/* NAVIGATION_GUARD_REDIRECT */
|
|
)) {
|
|
pushWithRedirect(
|
|
error.to,
|
|
toLocation
|
|
// avoid an uncaught rejection, let push call triggerError
|
|
).then((failure) => {
|
|
if (isNavigationFailure(
|
|
failure,
|
|
4 | 16
|
|
/* NAVIGATION_DUPLICATED */
|
|
) && !info.delta && info.type === NavigationType.pop) {
|
|
routerHistory.go(-1, false);
|
|
}
|
|
}).catch(noop);
|
|
return Promise.reject();
|
|
}
|
|
if (info.delta)
|
|
routerHistory.go(-info.delta, false);
|
|
return triggerError(error, toLocation, from);
|
|
}).then((failure) => {
|
|
failure = failure || finalizeNavigation(
|
|
// after navigation, all matched components are resolved
|
|
toLocation,
|
|
from,
|
|
false
|
|
);
|
|
if (failure) {
|
|
if (info.delta) {
|
|
routerHistory.go(-info.delta, false);
|
|
} else if (info.type === NavigationType.pop && isNavigationFailure(
|
|
failure,
|
|
4 | 16
|
|
/* NAVIGATION_DUPLICATED */
|
|
)) {
|
|
routerHistory.go(-1, false);
|
|
}
|
|
}
|
|
triggerAfterEach(toLocation, from, failure);
|
|
}).catch(noop);
|
|
});
|
|
}
|
|
let readyHandlers = useCallbacks();
|
|
let errorHandlers = useCallbacks();
|
|
let ready;
|
|
function triggerError(error, to, from) {
|
|
markAsReady(error);
|
|
const list = errorHandlers.list();
|
|
if (list.length) {
|
|
list.forEach((handler) => handler(error, to, from));
|
|
} else {
|
|
if (true) {
|
|
warn("uncaught error during route navigation:");
|
|
}
|
|
console.error(error);
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
function isReady() {
|
|
if (ready && currentRoute.value !== START_LOCATION_NORMALIZED)
|
|
return Promise.resolve();
|
|
return new Promise((resolve2, reject) => {
|
|
readyHandlers.add([resolve2, reject]);
|
|
});
|
|
}
|
|
function markAsReady(err) {
|
|
if (!ready) {
|
|
ready = !err;
|
|
setupListeners();
|
|
readyHandlers.list().forEach(([resolve2, reject]) => err ? reject(err) : resolve2());
|
|
readyHandlers.reset();
|
|
}
|
|
return err;
|
|
}
|
|
function handleScroll(to, from, isPush, isFirstNavigation) {
|
|
const { scrollBehavior } = options;
|
|
if (!isBrowser || !scrollBehavior)
|
|
return Promise.resolve();
|
|
const scrollPosition = !isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0)) || (isFirstNavigation || !isPush) && history.state && history.state.scroll || null;
|
|
return nextTick().then(() => scrollBehavior(to, from, scrollPosition)).then((position) => position && scrollToPosition(position)).catch((err) => triggerError(err, to, from));
|
|
}
|
|
const go = (delta) => routerHistory.go(delta);
|
|
let started;
|
|
const installedApps = /* @__PURE__ */ new Set();
|
|
const router = {
|
|
currentRoute,
|
|
addRoute,
|
|
removeRoute,
|
|
hasRoute,
|
|
getRoutes,
|
|
resolve,
|
|
options,
|
|
push,
|
|
replace,
|
|
go,
|
|
back: () => go(-1),
|
|
forward: () => go(1),
|
|
beforeEach: beforeGuards.add,
|
|
beforeResolve: beforeResolveGuards.add,
|
|
afterEach: afterGuards.add,
|
|
onError: errorHandlers.add,
|
|
isReady,
|
|
install(app) {
|
|
const router2 = this;
|
|
app.component("RouterLink", RouterLink);
|
|
app.component("RouterView", RouterView);
|
|
app.config.globalProperties.$router = router2;
|
|
Object.defineProperty(app.config.globalProperties, "$route", {
|
|
enumerable: true,
|
|
get: () => unref(currentRoute)
|
|
});
|
|
if (isBrowser && // used for the initial navigation client side to avoid pushing
|
|
// multiple times when the router is used in multiple apps
|
|
!started && currentRoute.value === START_LOCATION_NORMALIZED) {
|
|
started = true;
|
|
push(routerHistory.location).catch((err) => {
|
|
if (true)
|
|
warn("Unexpected error when starting the router:", err);
|
|
});
|
|
}
|
|
const reactiveRoute = {};
|
|
for (const key in START_LOCATION_NORMALIZED) {
|
|
reactiveRoute[key] = computed(() => currentRoute.value[key]);
|
|
}
|
|
app.provide(routerKey, router2);
|
|
app.provide(routeLocationKey, reactive(reactiveRoute));
|
|
app.provide(routerViewLocationKey, currentRoute);
|
|
const unmountApp = app.unmount;
|
|
installedApps.add(app);
|
|
app.unmount = function() {
|
|
installedApps.delete(app);
|
|
if (installedApps.size < 1) {
|
|
pendingLocation = START_LOCATION_NORMALIZED;
|
|
removeHistoryListener && removeHistoryListener();
|
|
currentRoute.value = START_LOCATION_NORMALIZED;
|
|
started = false;
|
|
ready = false;
|
|
}
|
|
unmountApp();
|
|
};
|
|
if (isBrowser) {
|
|
addDevtools(app, router2, matcher);
|
|
}
|
|
}
|
|
};
|
|
return router;
|
|
}
|
|
function runGuardQueue(guards) {
|
|
return guards.reduce((promise, guard) => promise.then(() => guard()), Promise.resolve());
|
|
}
|
|
function extractChangingRecords(to, from) {
|
|
const leavingRecords = [];
|
|
const updatingRecords = [];
|
|
const enteringRecords = [];
|
|
const len = Math.max(from.matched.length, to.matched.length);
|
|
for (let i = 0; i < len; i++) {
|
|
const recordFrom = from.matched[i];
|
|
if (recordFrom) {
|
|
if (to.matched.find((record) => isSameRouteRecord(record, recordFrom)))
|
|
updatingRecords.push(recordFrom);
|
|
else
|
|
leavingRecords.push(recordFrom);
|
|
}
|
|
const recordTo = to.matched[i];
|
|
if (recordTo) {
|
|
if (!from.matched.find((record) => isSameRouteRecord(record, recordTo))) {
|
|
enteringRecords.push(recordTo);
|
|
}
|
|
}
|
|
}
|
|
return [leavingRecords, updatingRecords, enteringRecords];
|
|
}
|
|
function useRouter() {
|
|
return inject(routerKey);
|
|
}
|
|
function useRoute() {
|
|
return inject(routeLocationKey);
|
|
}
|
|
export {
|
|
NavigationFailureType,
|
|
RouterLink,
|
|
RouterView,
|
|
START_LOCATION_NORMALIZED as START_LOCATION,
|
|
createMemoryHistory,
|
|
createRouter,
|
|
createRouterMatcher,
|
|
createWebHashHistory,
|
|
createWebHistory,
|
|
isNavigationFailure,
|
|
matchedRouteKey,
|
|
onBeforeRouteLeave,
|
|
onBeforeRouteUpdate,
|
|
parseQuery,
|
|
routeLocationKey,
|
|
routerKey,
|
|
routerViewLocationKey,
|
|
stringifyQuery,
|
|
useLink,
|
|
useRoute,
|
|
useRouter,
|
|
viewDepthKey
|
|
};
|
|
/*! Bundled license information:
|
|
|
|
vue-router/dist/vue-router.esm-bundler.js:
|
|
(*!
|
|
* vue-router v4.0.13
|
|
* (c) 2022 Eduardo San Martin Morote
|
|
* @license MIT
|
|
*)
|
|
*/
|
|
//# sourceMappingURL=vue-router.js.map
|