@hippy/vue-router
Version:
Official router for hippy-vue
1,580 lines (1,505 loc) • 68.9 kB
JavaScript
/*!
* @hippy/vue-router v3.3.2
* (Using Vue v2.6.14 and Hippy-Vue v3.3.2)
* Build at: Fri Nov 08 2024 20:45:13 GMT+0800 (中国标准时间)
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2024 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* */
Object.freeze({});
/**
* Make a map and return a function for checking if a key
* is in that map.
*/
function makeMap(str, expectsLowerCase) {
const map = Object.create(null);
const list = str.split(',');
for (let i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val];
}
/**
* Check if a tag is a built-in tag.
*/
makeMap('slot,component', true);
/**
* Check if an attribute is a reserved attribute.
*/
makeMap('key,ref,slot,slot-scope,is');
/**
* Ensure a function is called only once.
*/
function once(fn) {
let called = false;
return function () {
if (!called) {
called = true;
fn.apply(this, arguments);
}
};
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Hippy debug address
*/
`http://127.0.0.1:${process.env.PORT}/`;
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let _Vue;
function setVue(Vue) {
_Vue = Vue;
}
function getVue() {
return _Vue;
}
/**
* Better function checking
*/
function isFunction(func) {
return Object.prototype.toString.call(func) === '[object Function]';
}
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var defineProperty = {exports: {}};
var toPropertyKey = {exports: {}};
var _typeof = {exports: {}};
(function (module) {
function _typeof(o) {
"@babel/helpers - typeof";
return (module.exports = _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, module.exports.__esModule = true, module.exports["default"] = module.exports), _typeof(o);
}
module.exports = _typeof, module.exports.__esModule = true, module.exports["default"] = module.exports;
})(_typeof);
var toPrimitive = {exports: {}};
(function (module) {
var _typeof$1 = _typeof.exports["default"];
function toPrimitive(t, r) {
if ("object" != _typeof$1(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof$1(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
module.exports = toPrimitive, module.exports.__esModule = true, module.exports["default"] = module.exports;
})(toPrimitive);
(function (module) {
var _typeof$1 = _typeof.exports["default"];
var toPrimitive$1 = toPrimitive.exports;
function toPropertyKey(t) {
var i = toPrimitive$1(t, "string");
return "symbol" == _typeof$1(i) ? i : i + "";
}
module.exports = toPropertyKey, module.exports.__esModule = true, module.exports["default"] = module.exports;
})(toPropertyKey);
(function (module) {
var toPropertyKey$1 = toPropertyKey.exports;
function _defineProperty(e, r, t) {
return (r = toPropertyKey$1(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
module.exports = _defineProperty, module.exports.__esModule = true, module.exports["default"] = module.exports;
})(defineProperty);
var _defineProperty = /*@__PURE__*/getDefaultExportFromCjs(defineProperty.exports);
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-console */
function assert(condition, message) {
if (!condition) {
throw new Error(`[hippy-vue-router] ${message}`);
}
}
function warn(condition, message) {
if (process.env.NODE_ENV !== 'production' && !condition) {
if (typeof console !== 'undefined') {
console.warn(`[hippy-vue-router] ${message}`);
}
}
}
function isError(err) {
return Object.prototype.toString.call(err).indexOf('Error') > -1;
}
function ownKeys$2(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread$2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$2(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$2(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function resolveProps(route, config) {
switch (typeof config) {
case 'undefined':
return null;
case 'object':
return config;
case 'function':
return config(route);
case 'boolean':
return config ? route.params : undefined;
default:
if (process.env.NODE_ENV !== 'production') {
warn(false, `props in "${route.path}" is a ${typeof config}, ` + 'expecting an object, function or boolean.');
}
return null;
}
}
var View = {
name: 'RouterView',
functional: true,
props: {
name: {
type: String,
default: 'default'
}
},
render(_, {
props,
children,
parent,
data
}) {
// used by devtools to display a router-view badge
data.routerView = true;
// directly use parent context's createElement() function
// so that components rendered by router-view can resolve named slots
const h = parent.$createElement;
const {
name
} = props;
const route = parent.$route;
const cache = parent._routerViewCache || (parent._routerViewCache = {});
// determine current view depth, also check to see if the tree
// has been toggled inactive but kept-alive.
let depth = 0;
let inactive = false;
while (parent && parent._routerRoot !== parent) {
if (parent.$vnode && parent.$vnode.data.routerView) {
depth += 1;
}
if (parent._inactive) {
inactive = true;
}
parent = parent.$parent;
}
data.routerViewDepth = depth;
// render previous view if the tree is inactive and kept-alive
if (inactive) {
return h(cache[name], data, children);
}
const matched = route.matched[depth];
// render empty node if no matched route
if (!matched) {
cache[name] = null;
return h();
}
const component = matched.components[name];
cache[name] = component;
// attach instance registration hook
// this will be called in the instance's injected lifecycle hooks
data.registerRouteInstance = (vm, val) => {
// val could be undefined for unregistration
const current = matched.instances[name];
if (val && current !== vm || !val && current === vm) {
matched.instances[name] = val;
}
};
// also register instance in prepatch hook
// in case the same component instance is reused across different routes
if (!data.hook) {
data.hook = {};
}
data.hook.prepatch = (__, vnode) => {
matched.instances[name] = vnode.componentInstance;
};
// resolve props
let propsToPass = resolveProps(route, matched.props && matched.props[name]);
data.props = propsToPass;
if (propsToPass) {
// clone to prevent mutation
propsToPass = _objectSpread$2({}, propsToPass);
data.props = propsToPass;
// pass non-declared props as attrs
const attrs = data.attrs || {};
Object.keys(propsToPass).forEach(key => {
if (!component.props || !(key in component.props)) {
attrs[key] = propsToPass[key];
delete propsToPass[key];
}
});
}
return h(component, data, children);
}
};
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const encodeReserveRE = /[!'()*]/g;
const encodeReserveReplacer = c => `%${c.charCodeAt(0).toString(16)}`;
const commaRE = /%2C/g;
// fixed encodeURIComponent which is more conformant to RFC3986:
// - escapes [!'()*]
// - preserve commas
const encode = str => encodeURIComponent(str).replace(encodeReserveRE, encodeReserveReplacer).replace(commaRE, ',');
const decode = decodeURIComponent;
function parseQuery(query) {
const res = {};
query = query.trim().replace(/^(\?|#|&)/, '');
if (!query) {
return res;
}
query.split('&').forEach(param => {
const parts = param.replace(/\+/g, ' ').split('=');
const key = decode(parts.shift());
const val = parts.length > 0 ? decode(parts.join('=')) : null;
if (res[key] === undefined) {
res[key] = val;
} else if (Array.isArray(res[key])) {
res[key].push(val);
} else {
res[key] = [res[key], val];
}
});
return res;
}
function resolveQuery(query, extraQuery = {}, _parseQuery) {
const parse = _parseQuery || parseQuery;
let parsedQuery;
try {
parsedQuery = parse(query || '');
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
warn(false, e.message);
}
parsedQuery = {};
}
Object.keys(extraQuery).forEach(key => {
parsedQuery[key] = extraQuery[key];
});
return parsedQuery;
}
function stringifyQuery(obj) {
const res = obj ? Object.keys(obj).map(key => {
const val = obj[key];
if (val === undefined) {
return '';
}
if (val === null) {
return encode(key);
}
if (Array.isArray(val)) {
const result = [];
val.forEach(val2 => {
if (val2 === undefined) {
return;
}
if (val2 === null) {
result.push(encode(key));
} else {
result.push(`${encode(key)}=${encode(val2)}`);
}
});
return result.join('&');
}
return `${encode(key)}=${encode(val)}`;
}).filter(x => x.length > 0).join('&') : null;
return res ? `?${res}` : '';
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const trailingSlashRE = /\/?$/;
function clone(value) {
if (Array.isArray(value)) {
return value.map(clone);
}
if (value && typeof value === 'object') {
const res = {};
Object.keys(value).forEach(key => {
res[key] = clone(value[key]);
});
return res;
}
return value;
}
function formatMatch(record) {
const res = [];
while (record) {
res.unshift(record);
record = record.parent;
}
return res;
}
function getFullPath({
path,
query = {},
hash = ''
}, _stringifyQuery) {
const stringify = _stringifyQuery || stringifyQuery;
return (path || '/') + stringify(query) + hash;
}
function isObjectEqual(a = {}, b = {}) {
// handle null value #1566
if (!a || !b) return a === b;
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) {
return false;
}
return aKeys.every(key => {
const aVal = a[key];
const bVal = b[key];
// check nested equality
if (typeof aVal === 'object' && typeof bVal === 'object') {
return isObjectEqual(aVal, bVal);
}
return String(aVal) === String(bVal);
});
}
function createRoute(record, location, redirectedFrom, router) {
let stringifyQueryStr;
if (router) {
({
stringifyQuery: stringifyQueryStr
} = router.options);
}
let query = location.query || {};
try {
query = clone(query);
} catch (e) {
// pass
}
const route = {
name: location.name || record && record.name,
meta: record && record.meta || {},
path: location.path || '/',
hash: location.hash || '',
query,
params: location.params || {},
fullPath: getFullPath(location, stringifyQueryStr),
matched: record ? formatMatch(record) : []
};
if (redirectedFrom) {
route.redirectedFrom = getFullPath(redirectedFrom, stringifyQueryStr);
}
return Object.freeze(route);
}
function queryIncludes(current, target) {
for (const key in target) {
if (!(key in current)) {
return false;
}
}
return true;
}
// the starting route that represents the initial state
const START = createRoute(null, {
path: '/'
});
function isSameRoute(a, b) {
if (b === START) {
return a === b;
}
if (!b) {
return false;
}
if (a.path && b.path) {
return a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && a.hash === b.hash && isObjectEqual(a.query, b.query);
}
if (a.name && b.name) {
return a.name === b.name && a.hash === b.hash && isObjectEqual(a.query, b.query) && isObjectEqual(a.params, b.params);
}
return false;
}
function isIncludedRoute(current, target) {
return current.path.replace(trailingSlashRE, '/').indexOf(target.path.replace(trailingSlashRE, '/')) === 0 && (!target.hash || current.hash === target.hash) && queryIncludes(current.query, target.query);
}
function ownKeys$1(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread$1(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$1(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$1(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
// work around weird flow bug
const toTypes = [String, Object];
const eventTypes = [String, Array];
function guardEvent(e) {
// don't redirect with control keys
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return false;
// don't redirect when preventDefault called
if (e.defaultPrevented) return false;
// don't redirect on right click
if (e.button !== undefined && e.button !== 0) return false;
// don't redirect if `target="_blank"`
if (e.currentTarget && e.currentTarget.getAttribute) {
const target = e.currentTarget.getAttribute('target');
if (/\b_blank\b/i.test(target)) return false;
}
// this may be a Weex event which doesn't have this method
if (e.preventDefault) {
e.preventDefault();
}
return true;
}
function findAnchor(children) {
if (!children) {
return null;
}
return children.find(child => {
if (child.tag === 'a') {
return true;
}
if (child.children) {
const c = findAnchor(child.children);
return !!c;
}
return false;
});
}
var Link = {
name: 'RouterLink',
props: {
to: {
type: toTypes,
required: true
},
tag: {
type: String,
default: 'a'
},
exact: Boolean,
append: Boolean,
replace: Boolean,
activeClass: String,
exactActiveClass: String,
event: {
type: eventTypes,
default: 'click'
}
},
render(h) {
const router = this.$router;
const current = this.$route;
const {
location,
route,
href
} = router.resolve(this.to, current, this.append);
const classes = {};
const globalActiveClass = router.options.linkActiveClass;
const globalExactActiveClass = router.options.linkExactActiveClass;
// Support global empty active class
// eslint-disable-next-line eqeqeq
const activeClassFallback = globalActiveClass == null ? 'router-link-active' : globalActiveClass;
// eslint-disable-next-line eqeqeq
const exactActiveClassFallback = globalExactActiveClass == null ? 'router-link-exact-active' : globalExactActiveClass;
// eslint-disable-next-line eqeqeq
const activeClass = this.activeClass == null ? activeClassFallback : this.activeClass;
// eslint-disable-next-line eqeqeq
const exactActiveClass = this.exactActiveClass == null ? exactActiveClassFallback : this.exactActiveClass;
const compareTarget = location.path ? createRoute(null, location, null, router) : route;
classes[exactActiveClass] = isSameRoute(current, compareTarget);
classes[activeClass] = this.exact ? classes[exactActiveClass] : isIncludedRoute(current, compareTarget);
const handler = e => {
if (guardEvent(e)) {
if (this.replace) {
router.replace(location);
} else {
router.push(location);
}
}
};
const on = {
click: guardEvent
};
if (Array.isArray(this.event)) {
this.event.forEach(e => {
on[e] = handler;
});
} else {
on[this.event] = handler;
}
const data = {
class: classes
};
if (this.tag === 'a') {
data.on = on;
data.attrs = {
href
};
} else {
// find the first <a> child and apply listener and href
const a = findAnchor(this.$slots.default);
if (a) {
// in case the <a> is a static node
a.isStatic = false;
const aData = _objectSpread$1({}, a.data);
a.data = aData;
aData.on = on;
const aAttrs = _objectSpread$1({}, a.data.attrs);
a.data.attrs = aAttrs;
aAttrs.href = href;
} else {
// doesn't have <a> child, apply listener to self
data.on = on;
}
}
return h(this.tag, data, this.$slots.default);
}
};
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function install(Vue) {
if (install.installed && getVue() === Vue) return;
install.installed = true;
setVue(Vue);
const isDef = v => v !== undefined;
const registerInstance = (vm, callVal) => {
let i = vm.$options._parentVnode;
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal);
}
};
Vue.mixin({
beforeCreate() {
if (isDef(this.$options.router)) {
this._routerRoot = this;
this._router = this.$options.router;
this._router.init(this, Vue);
Vue.util.defineReactive(this, '_route', this._router.history.current);
} else {
this._routerRoot = this.$parent && this.$parent._routerRoot || this;
}
registerInstance(this, this);
},
destroyed() {
registerInstance(this);
}
});
Object.defineProperty(Vue.prototype, '$router', {
get() {
return this._routerRoot._router;
}
});
Object.defineProperty(Vue.prototype, '$route', {
get() {
return this._routerRoot._route;
}
});
Vue.component('RouterView', View);
Vue.component('RouterLink', Link);
const strats = Vue.config.optionMergeStrategies;
// use the same hook merging strategy for route hooks
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created;
}
var pathToRegexp$1 = {exports: {}};
var isarray$1 = Array.isArray || function (arr) {
return Object.prototype.toString.call(arr) == '[object Array]';
};
var isarray = isarray$1;
/**
* Expose `pathToRegexp`.
*/
pathToRegexp$1.exports = pathToRegexp;
pathToRegexp$1.exports.parse = parse;
pathToRegexp$1.exports.compile = compile;
pathToRegexp$1.exports.tokensToFunction = tokensToFunction;
pathToRegexp$1.exports.tokensToRegExp = tokensToRegExp;
/**
* The main path matching regexp utility.
*
* @type {RegExp}
*/
var PATH_REGEXP = new RegExp([
// Match escaped characters that would otherwise appear in future matches.
// This allows the user to escape special characters that won't transform.
'(\\\\.)',
// Match Express-style parameters and un-named parameters with a prefix
// and optional suffixes. Matches appear as:
//
// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
// "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
'([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'].join('|'), 'g');
/**
* Parse a string for the raw tokens.
*
* @param {string} str
* @param {Object=} options
* @return {!Array}
*/
function parse(str, options) {
var tokens = [];
var key = 0;
var index = 0;
var path = '';
var defaultDelimiter = options && options.delimiter || '/';
var res;
while ((res = PATH_REGEXP.exec(str)) != null) {
var m = res[0];
var escaped = res[1];
var offset = res.index;
path += str.slice(index, offset);
index = offset + m.length;
// Ignore already escaped sequences.
if (escaped) {
path += escaped[1];
continue;
}
var next = str[index];
var prefix = res[2];
var name = res[3];
var capture = res[4];
var group = res[5];
var modifier = res[6];
var asterisk = res[7];
// Push the current path onto the tokens.
if (path) {
tokens.push(path);
path = '';
}
var partial = prefix != null && next != null && next !== prefix;
var repeat = modifier === '+' || modifier === '*';
var optional = modifier === '?' || modifier === '*';
var delimiter = res[2] || defaultDelimiter;
var pattern = capture || group;
tokens.push({
name: name || key++,
prefix: prefix || '',
delimiter: delimiter,
optional: optional,
repeat: repeat,
partial: partial,
asterisk: !!asterisk,
pattern: pattern ? escapeGroup(pattern) : asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?'
});
}
// Match any characters still remaining.
if (index < str.length) {
path += str.substr(index);
}
// If the path exists, push it onto the end.
if (path) {
tokens.push(path);
}
return tokens;
}
/**
* Compile a string to a template function for the path.
*
* @param {string} str
* @param {Object=} options
* @return {!function(Object=, Object=)}
*/
function compile(str, options) {
return tokensToFunction(parse(str, options), options);
}
/**
* Prettier encoding of URI path segments.
*
* @param {string}
* @return {string}
*/
function encodeURIComponentPretty(str) {
return encodeURI(str).replace(/[\/?#]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16).toUpperCase();
});
}
/**
* Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
*
* @param {string}
* @return {string}
*/
function encodeAsterisk(str) {
return encodeURI(str).replace(/[?#]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16).toUpperCase();
});
}
/**
* Expose a method for transforming tokens into the path function.
*/
function tokensToFunction(tokens, options) {
// Compile all the tokens into regexps.
var matches = new Array(tokens.length);
// Compile all the patterns before compilation.
for (var i = 0; i < tokens.length; i++) {
if (typeof tokens[i] === 'object') {
matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options));
}
}
return function (obj, opts) {
var path = '';
var data = obj || {};
var options = opts || {};
var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent;
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (typeof token === 'string') {
path += token;
continue;
}
var value = data[token.name];
var segment;
if (value == null) {
if (token.optional) {
// Prepend partial segment prefixes.
if (token.partial) {
path += token.prefix;
}
continue;
} else {
throw new TypeError('Expected "' + token.name + '" to be defined');
}
}
if (isarray(value)) {
if (!token.repeat) {
throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`');
}
if (value.length === 0) {
if (token.optional) {
continue;
} else {
throw new TypeError('Expected "' + token.name + '" to not be empty');
}
}
for (var j = 0; j < value.length; j++) {
segment = encode(value[j]);
if (!matches[i].test(segment)) {
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`');
}
path += (j === 0 ? token.prefix : token.delimiter) + segment;
}
continue;
}
segment = token.asterisk ? encodeAsterisk(value) : encode(value);
if (!matches[i].test(segment)) {
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"');
}
path += token.prefix + segment;
}
return path;
};
}
/**
* Escape a regular expression string.
*
* @param {string} str
* @return {string}
*/
function escapeString(str) {
return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1');
}
/**
* Escape the capturing group by escaping special characters and meaning.
*
* @param {string} group
* @return {string}
*/
function escapeGroup(group) {
return group.replace(/([=!:$\/()])/g, '\\$1');
}
/**
* Attach the keys as a property of the regexp.
*
* @param {!RegExp} re
* @param {Array} keys
* @return {!RegExp}
*/
function attachKeys(re, keys) {
re.keys = keys;
return re;
}
/**
* Get the flags for a regexp from the options.
*
* @param {Object} options
* @return {string}
*/
function flags(options) {
return options && options.sensitive ? '' : 'i';
}
/**
* Pull out keys from a regexp.
*
* @param {!RegExp} path
* @param {!Array} keys
* @return {!RegExp}
*/
function regexpToRegexp(path, keys) {
// Use a negative lookahead to match only capturing groups.
var groups = path.source.match(/\((?!\?)/g);
if (groups) {
for (var i = 0; i < groups.length; i++) {
keys.push({
name: i,
prefix: null,
delimiter: null,
optional: false,
repeat: false,
partial: false,
asterisk: false,
pattern: null
});
}
}
return attachKeys(path, keys);
}
/**
* Transform an array into a regexp.
*
* @param {!Array} path
* @param {Array} keys
* @param {!Object} options
* @return {!RegExp}
*/
function arrayToRegexp(path, keys, options) {
var parts = [];
for (var i = 0; i < path.length; i++) {
parts.push(pathToRegexp(path[i], keys, options).source);
}
var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
return attachKeys(regexp, keys);
}
/**
* Create a path regexp from string input.
*
* @param {string} path
* @param {!Array} keys
* @param {!Object} options
* @return {!RegExp}
*/
function stringToRegexp(path, keys, options) {
return tokensToRegExp(parse(path, options), keys, options);
}
/**
* Expose a function for taking tokens and returning a RegExp.
*
* @param {!Array} tokens
* @param {(Array|Object)=} keys
* @param {Object=} options
* @return {!RegExp}
*/
function tokensToRegExp(tokens, keys, options) {
if (!isarray(keys)) {
options = /** @type {!Object} */keys || options;
keys = [];
}
options = options || {};
var strict = options.strict;
var end = options.end !== false;
var route = '';
// Iterate over the tokens and create our regexp string.
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (typeof token === 'string') {
route += escapeString(token);
} else {
var prefix = escapeString(token.prefix);
var capture = '(?:' + token.pattern + ')';
keys.push(token);
if (token.repeat) {
capture += '(?:' + prefix + capture + ')*';
}
if (token.optional) {
if (!token.partial) {
capture = '(?:' + prefix + '(' + capture + '))?';
} else {
capture = prefix + '(' + capture + ')?';
}
} else {
capture = prefix + '(' + capture + ')';
}
route += capture;
}
}
var delimiter = escapeString(options.delimiter || '/');
var endsWithDelimiter = route.slice(-delimiter.length) === delimiter;
// In non-strict mode we allow a slash at the end of match. If the path to
// match already ends with a slash, we remove it for consistency. The slash
// is valid at the end of a path match, not in the middle. This is important
// in non-ending mode, where "/test/" shouldn't match "/test//route".
if (!strict) {
route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?';
}
if (end) {
route += '$';
} else {
// In non-ending mode, we need the capturing groups to match as much as
// possible by using a positive lookahead to the end or next path segment.
route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)';
}
return attachKeys(new RegExp('^' + route, flags(options)), keys);
}
/**
* Normalize the given path string, returning a regular expression.
*
* An empty array can be passed in for the keys, which will hold the
* placeholder key descriptions. For example, using `/user/:id`, `keys` will
* contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
*
* @param {(string|RegExp|Array)} path
* @param {(Array|Object)=} keys
* @param {Object=} options
* @return {!RegExp}
*/
function pathToRegexp(path, keys, options) {
if (!isarray(keys)) {
options = /** @type {!Object} */keys || options;
keys = [];
}
options = options || {};
if (path instanceof RegExp) {
return regexpToRegexp(path, /** @type {!Array} */keys);
}
if (isarray(path)) {
return arrayToRegexp( /** @type {!Array} */path, /** @type {!Array} */keys, options);
}
return stringToRegexp( /** @type {string} */path, /** @type {!Array} */keys, options);
}
var Regexp = pathToRegexp$1.exports;
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const regexpCompileCache = Object.create(null);
function fillParams(path, params, routeMsg) {
try {
const filler = regexpCompileCache[path] || (regexpCompileCache[path] = Regexp.compile(path));
return filler(params || {}, {
pretty: true
});
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
warn(false, `missing param for ${routeMsg}: ${e.message}`);
}
return '';
}
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-param-reassign */
function resolvePath(relative, base, append) {
const firstChar = relative.charAt(0);
if (firstChar === '/') {
return relative;
}
if (firstChar === '?' || firstChar === '#') {
return base + relative;
}
const stack = base.split('/');
// remove trailing segment if:
// - not appending
// - appending to trailing slash (last segment is empty)
if (!append || !stack[stack.length - 1]) {
stack.pop();
}
// resolve relative path
const segments = relative.replace(/^\//, '').split('/');
for (let i = 0; i < segments.length; i += 1) {
const segment = segments[i];
if (segment === '..') {
stack.pop();
} else if (segment !== '.') {
stack.push(segment);
}
}
// ensure leading slash
if (stack[0] !== '') {
stack.unshift('');
}
return stack.join('/');
}
function parsePath(path) {
let hash = '';
let query = '';
const hashIndex = path.indexOf('#');
if (hashIndex >= 0) {
hash = path.slice(hashIndex);
path = path.slice(0, hashIndex);
}
const queryIndex = path.indexOf('?');
if (queryIndex >= 0) {
query = path.slice(queryIndex + 1);
path = path.slice(0, queryIndex);
}
return {
path,
query,
hash
};
}
function cleanPath(path) {
return path.replace(/\/\//g, '/');
}
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function normalizeLocation(raw, current, append, router) {
let next = typeof raw === 'string' ? {
path: raw
} : raw;
// named target
if (next.name || next._normalized) {
return next;
}
// relative params
if (!next.path && next.params && current) {
next = _objectSpread({}, next);
next._normalized = true;
const params = _objectSpread(_objectSpread({}, current.params), next.params);
if (current.name) {
next.name = current.name;
next.params = params;
} else if (current.matched.length) {
const rawPath = current.matched[current.matched.length - 1].path;
next.path = fillParams(rawPath, params, `path ${current.path}`);
} else if (process.env.NODE_ENV !== 'production') {
warn(false, 'relative params navigation requires a current route.');
}
return next;
}
const parsedPath = parsePath(next.path || '');
const basePath = current && current.path || '/';
const path = parsedPath.path ? resolvePath(parsedPath.path, basePath, append || next.append) : basePath;
const query = resolveQuery(parsedPath.query, next.query, router && router.options.parseQuery);
let hash = next.hash || parsedPath.hash;
if (hash && hash.charAt(0) !== '#') {
hash = `#${hash}`;
}
return {
_normalized: true,
path,
query,
hash
};
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function compileRouteRegex(path, pathToRegexpOptions) {
const regex = Regexp(path, [], pathToRegexpOptions);
if (process.env.NODE_ENV !== 'production') {
const keys = Object.create(null);
regex.keys.forEach(key => {
warn(!keys[key.name], `Duplicate param keys in route with path: "${path}"`);
keys[key.name] = true;
});
}
return regex;
}
function normalizePath(path, parent, strict) {
if (!strict) path = path.replace(/\/$/, '');
if (path[0] === '/') return path;
// eslint-disable-next-line eqeqeq
if (parent == null) return path;
return cleanPath(`${parent.path}/${path}`);
}
// #lizard forgives
function addRouteRecord(pathList, pathMap, nameMap, route, parent, matchAs) {
const {
path,
name
} = route;
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line eqeqeq
assert(path != null, '"path" is required in a route configuration.');
assert(typeof route.component !== 'string', `route config "component" for path: ${String(path || name)} cannot be a ` + 'string id. Use an actual component instead.');
}
const pathToRegexpOptions = route.pathToRegexpOptions || {};
const normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict);
if (typeof route.caseSensitive === 'boolean') {
pathToRegexpOptions.sensitive = route.caseSensitive;
}
const record = {
path: normalizedPath,
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
components: route.components || {
default: route.component
},
instances: {},
name,
parent,
matchAs,
redirect: route.redirect,
beforeEnter: route.beforeEnter,
meta: route.meta || {},
// eslint-disable-next-line eqeqeq
props: route.props == null ? {} : route.components ? route.props : {
default: route.props
}
};
if (route.children) {
// Warn if route is named, does not redirect and has a default child route.
// If users navigate to this route by name, the default child will
// not be rendered (GH Issue #629)
if (process.env.NODE_ENV !== 'production') {
if (route.name && !route.redirect && route.children.some(child => /^\/?$/.test(child.path))) {
warn(false, `Named Route '${route.name}' has a default child route. ` + `When navigating to this named route (:to="{name: '${route.name}'"), ` + 'the default child route will not be rendered. Remove the name from ' + 'this route and use the name of the default child route for named ' + 'links instead.');
}
}
route.children.forEach(child => {
const childMatchAs = matchAs ? cleanPath(`${matchAs}/${child.path}`) : undefined;
addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs);
});
}
if (route.alias !== undefined) {
const aliases = Array.isArray(route.alias) ? route.alias : [route.alias];
aliases.forEach(alias => {
const aliasRoute = {
path: alias,
children: route.children
};
addRouteRecord(pathList, pathMap, nameMap, aliasRoute, parent, record.path || '/' // matchAs
);
});
}
if (!pathMap[record.path]) {
pathList.push(record.path);
pathMap[record.path] = record;
}
if (name) {
if (!nameMap[name]) {
nameMap[name] = record;
} else if (process.env.NODE_ENV !== 'production' && !matchAs) {
warn(false, 'Duplicate named routes definition: ' + `{ name: "${name}", path: "${record.path}" }`);
}
}
}
function createRouteMap(routes, oldPathList, oldPathMap, oldNameMap) {
// the path list is used to control path matching priority
const pathList = oldPathList || [];
// $flow-disable-line
const pathMap = oldPathMap || Object.create(null);
// $flow-disable-line
const nameMap = oldNameMap || Object.create(null);
routes.forEach(route => {
addRouteRecord(pathList, pathMap, nameMap, route);
});
// ensure wildcard routes are always at the end
for (let i = 0, l = pathList.length; i < l; i += 1) {
if (pathList[i] === '*') {
pathList.push(pathList.splice(i, 1)[0]);
l -= 1;
i += 1;
}
}
return {
pathList,
pathMap,
nameMap
};
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function createMatcher(routes, router) {
const {
pathList,
pathMap,
nameMap
} = createRouteMap(routes);
function addRoutes(rs) {
createRouteMap(rs, pathList, pathMap, nameMap);
}
function match(raw, currentRoute, redirectedFrom) {
const location = normalizeLocation(raw, currentRoute, false, router);
const {
name
} = location;
if (name) {
const record = nameMap[name];
if (process.env.NODE_ENV !== 'production') {
warn(record, `Route with name '${name}' does not exist`);
}
if (!record) return _createRoute(null, location);
const paramNames = record.regex.keys.filter(key => !key.optional).map(key => key.name);
if (typeof location.params !== 'object') {
location.params = {};
}
if (currentRoute && typeof currentRoute.params === 'object') {
Object.keys(currentRoute.params).forEach(key => {
if (!(key in location.params) && paramNames.indexOf(key) > -1) {
location.params[key] = currentRoute.params[key];
}
});
}
if (record) {
location.path = fillParams(record.path, location.params, `named route "${name}"`);
return _createRoute(record, location, redirectedFrom);
}
} else if (location.path) {
location.params = {};
for (let i = 0; i < pathList.length; i += 1) {
const path = pathList[i];
const record = pathMap[path];
if (matchRoute(record.regex, location.path, location.params)) {
return _createRoute(record, location, redirectedFrom);
}
}
}
// no match
return _createRoute(null, location);
}
function redirect(record, location) {
const originalRedirect = record.redirect;
let redirect = typeof originalRedirect === 'function' ? originalRedirect(createRoute(record, location, null, router)) : originalRedirect;
if (typeof redirect === 'string') {
redirect = {
path: redirect
};
}
if (!redirect || typeof redirect !== 'object') {
if (process.env.NODE_ENV !== 'production') {
warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`);
}
return _createRoute(null, location);
}
const re = redirect;
const {
name,
path
} = re;
let {
query,
hash,
params
} = location;
query = Object.prototype.hasOwnProperty.call(re, 'query') ? re.query : query;
hash = Object.prototype.hasOwnProperty.call(re, 'hash') ? re.hash : hash;
params = Object.prototype.hasOwnProperty.call(re, 'params') ? re.params : params;
if (name) {
// resolved named direct
const targetRecord = nameMap[name];
if (process.env.NODE_ENV !== 'production') {
assert(targetRecord, `redirect failed: named route "${name}" not found.`);
}
return match({
_normalized: true,
name,
query,
hash,
params
}, undefined, location);
}
if (path) {
// 1. resolve relative redirect
const rawPath = resolveRecordPath(path, record);
// 2. resolve params
const resolvedPath = fillParams(rawPath, params, `redirect route with path "${rawPath}"`);
// 3. rematch with existing query and hash
return match({
_normalized: true,
path: resolvedPath,
query,
hash
}, undefined, location);
}
if (process.env.NODE_ENV !== 'production') {
warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`);
}
return _createRoute(null, location);
}
function alias(record, location, matchAs) {
const aliasedPath = fillParams(matchAs, location.params, `aliased route with path "${matchAs}"`);
const aliasedMatch = match({
_normalized: true,
path: aliasedPath
});
if (aliasedMatch) {
const {
matched
} = aliasedMatch;
const aliasedRecord = matched[matched.length - 1];
location.params = aliasedMatch.params;
return _createRoute(aliasedRecord, location);
}
return _createRoute(null, location);
}
function _createRoute(record, location, redirectedFrom) {
if (record && record.redirect) {
return redirect(record, redirectedFrom || location);
}
if (record && record.matchAs) {