@esmx/router 3.0.0-rc.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/history/abstract.d.ts +29 -0
  2. package/dist/history/abstract.mjs +107 -0
  3. package/dist/history/base.d.ts +79 -0
  4. package/dist/history/base.mjs +275 -0
  5. package/dist/history/html.d.ts +22 -0
  6. package/dist/history/html.mjs +181 -0
  7. package/dist/history/index.d.ts +7 -0
  8. package/dist/history/index.mjs +16 -0
  9. package/dist/index.d.ts +3 -0
  10. package/dist/index.mjs +3 -0
  11. package/dist/matcher/create-matcher.d.ts +5 -0
  12. package/dist/matcher/create-matcher.mjs +218 -0
  13. package/dist/matcher/create-matcher.spec.d.ts +1 -0
  14. package/dist/matcher/create-matcher.spec.mjs +0 -0
  15. package/dist/matcher/index.d.ts +1 -0
  16. package/dist/matcher/index.mjs +1 -0
  17. package/dist/router.d.ts +111 -0
  18. package/dist/router.mjs +399 -0
  19. package/dist/task-pipe/index.d.ts +1 -0
  20. package/dist/task-pipe/index.mjs +1 -0
  21. package/dist/task-pipe/task.d.ts +30 -0
  22. package/dist/task-pipe/task.mjs +66 -0
  23. package/dist/utils/bom.d.ts +5 -0
  24. package/dist/utils/bom.mjs +10 -0
  25. package/dist/utils/encoding.d.ts +48 -0
  26. package/dist/utils/encoding.mjs +44 -0
  27. package/dist/utils/guards.d.ts +9 -0
  28. package/dist/utils/guards.mjs +12 -0
  29. package/dist/utils/index.d.ts +7 -0
  30. package/dist/utils/index.mjs +27 -0
  31. package/dist/utils/path.d.ts +60 -0
  32. package/dist/utils/path.mjs +264 -0
  33. package/dist/utils/path.spec.d.ts +1 -0
  34. package/dist/utils/path.spec.mjs +30 -0
  35. package/dist/utils/scroll.d.ts +25 -0
  36. package/dist/utils/scroll.mjs +59 -0
  37. package/dist/utils/utils.d.ts +16 -0
  38. package/dist/utils/utils.mjs +11 -0
  39. package/dist/utils/warn.d.ts +2 -0
  40. package/dist/utils/warn.mjs +12 -0
  41. package/package.json +66 -0
  42. package/src/history/abstract.ts +149 -0
  43. package/src/history/base.ts +408 -0
  44. package/src/history/html.ts +231 -0
  45. package/src/history/index.ts +20 -0
  46. package/src/index.ts +3 -0
  47. package/src/matcher/create-matcher.spec.ts +3 -0
  48. package/src/matcher/create-matcher.ts +293 -0
  49. package/src/matcher/index.ts +1 -0
  50. package/src/router.ts +521 -0
  51. package/src/task-pipe/index.ts +1 -0
  52. package/src/task-pipe/task.ts +97 -0
  53. package/src/utils/bom.ts +14 -0
  54. package/src/utils/encoding.ts +153 -0
  55. package/src/utils/guards.ts +25 -0
  56. package/src/utils/index.ts +27 -0
  57. package/src/utils/path.spec.ts +44 -0
  58. package/src/utils/path.ts +397 -0
  59. package/src/utils/scroll.ts +120 -0
  60. package/src/utils/utils.ts +30 -0
  61. package/src/utils/warn.ts +13 -0
@@ -0,0 +1,153 @@
1
+ import { warn } from './warn';
2
+
3
+ /**
4
+ * Encoding Rules (␣ = Space)
5
+ * - Path: ␣ " < > # ? { }
6
+ * - Query: ␣ " < > # & =
7
+ * - Hash: ␣ " < > `
8
+ *
9
+ * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2)
10
+ * defines some extra characters to be encoded. Most browsers do not encode them
11
+ * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to
12
+ * also encode `!'()*`. Leaving un-encoded only ASCII alphanumeric(`a-zA-Z0-9`)
13
+ * plus `-._~`. This extra safety should be applied to query by patching the
14
+ * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\`
15
+ * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\`
16
+ * into a `/` if directly typed in. The _backtick_ (`````) should also be
17
+ * encoded everywhere because some browsers like FF encode it when directly
18
+ * written while others don't. Safari and IE don't encode ``"<>{}``` in hash.
19
+ */
20
+ // const EXTRA_RESERVED_RE = /[!'()*]/g
21
+ // const encodeReservedReplacer = (c: string) => '%' + c.charCodeAt(0).toString(16)
22
+
23
+ const HASH_RE = /#/g; // %23
24
+ const AMPERSAND_RE = /&/g; // %26
25
+ const SLASH_RE = /\//g; // %2F
26
+ const EQUAL_RE = /=/g; // %3D
27
+ const IM_RE = /\?/g; // %3F
28
+ export const PLUS_RE = /\+/g; // %2B
29
+ /**
30
+ * NOTE: It's not clear to me if we should encode the + symbol in queries, it
31
+ * seems to be less flexible than not doing so and I can't find out the legacy
32
+ * systems requiring this for regular requests like text/html. In the standard,
33
+ * the encoding of the plus character is only mentioned for
34
+ * application/x-www-form-urlencoded
35
+ * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo
36
+ * leave the plus character as is in queries. To be more flexible, we allow the
37
+ * plus character on the query, but it can also be manually encoded by the user.
38
+ *
39
+ * Resources:
40
+ * - https://url.spec.whatwg.org/#urlencoded-parsing
41
+ * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20
42
+ */
43
+
44
+ const ENC_BRACKET_OPEN_RE = /%5B/g; // [
45
+ const ENC_BRACKET_CLOSE_RE = /%5D/g; // ]
46
+ const ENC_CARET_RE = /%5E/g; // ^
47
+ const ENC_BACKTICK_RE = /%60/g; // `
48
+ const ENC_CURLY_OPEN_RE = /%7B/g; // {
49
+ const ENC_PIPE_RE = /%7C/g; // |
50
+ const ENC_CURLY_CLOSE_RE = /%7D/g; // }
51
+ const ENC_SPACE_RE = /%20/g; // }
52
+
53
+ /**
54
+ * Encode characters that need to be encoded on the path, search and hash
55
+ * sections of the URL.
56
+ *
57
+ * @internal
58
+ * @param text - string to encode
59
+ * @returns encoded string
60
+ */
61
+ function commonEncode(text: string | number): string {
62
+ return encodeURIComponent('' + text)
63
+ .replace(ENC_PIPE_RE, '|')
64
+ .replace(ENC_BRACKET_OPEN_RE, '[')
65
+ .replace(ENC_BRACKET_CLOSE_RE, ']');
66
+ }
67
+
68
+ /**
69
+ * Encode characters that need to be encoded on the hash section of the URL.
70
+ *
71
+ * @param text - string to encode
72
+ * @returns encoded string
73
+ */
74
+ export function encodeHash(text: string): string {
75
+ return commonEncode(text)
76
+ .replace(ENC_CURLY_OPEN_RE, '{')
77
+ .replace(ENC_CURLY_CLOSE_RE, '}')
78
+ .replace(ENC_CARET_RE, '^');
79
+ }
80
+
81
+ /**
82
+ * Encode characters that need to be encoded query values on the query
83
+ * section of the URL.
84
+ *
85
+ * @param text - string to encode
86
+ * @returns encoded string
87
+ */
88
+ export function encodeQueryValue(text: string | number): string {
89
+ return (
90
+ commonEncode(text)
91
+ // Encode the space as +, encode the + to differentiate it from the space
92
+ .replace(PLUS_RE, '%2B')
93
+ .replace(ENC_SPACE_RE, '+')
94
+ .replace(HASH_RE, '%23')
95
+ .replace(AMPERSAND_RE, '%26')
96
+ .replace(ENC_BACKTICK_RE, '`')
97
+ .replace(ENC_CURLY_OPEN_RE, '{')
98
+ .replace(ENC_CURLY_CLOSE_RE, '}')
99
+ .replace(ENC_CARET_RE, '^')
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Like `encodeQueryValue` but also encodes the `=` character.
105
+ *
106
+ * @param text - string to encode
107
+ */
108
+ export function encodeQueryKey(text: string | number): string {
109
+ return encodeQueryValue(text).replace(EQUAL_RE, '%3D');
110
+ }
111
+
112
+ /**
113
+ * Encode characters that need to be encoded on the path section of the URL.
114
+ *
115
+ * @param text - string to encode
116
+ * @returns encoded string
117
+ */
118
+ export function encodePath(text: string | number): string {
119
+ return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F');
120
+ }
121
+
122
+ /**
123
+ * Encode characters that need to be encoded on the path section of the URL as a
124
+ * param. This function encodes everything {@link encodePath} does plus the
125
+ * slash (`/`) character. If `text` is `null` or `undefined`, returns an empty
126
+ * string instead.
127
+ *
128
+ * @param text - string to encode
129
+ * @returns encoded string
130
+ */
131
+ export function encodeParam(text: string | number | null | undefined): string {
132
+ return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F');
133
+ }
134
+
135
+ /**
136
+ * Decode text using `decodeURIComponent`. Returns the original text if it
137
+ * fails.
138
+ *
139
+ * @param text - string to decode
140
+ * @returns decoded string
141
+ */
142
+ export function decode(text: string | number): string {
143
+ try {
144
+ return decodeURIComponent('' + text);
145
+ } catch (err) {
146
+ warn(`Error decoding "${text}". Using original value`);
147
+ }
148
+ return '' + text;
149
+ }
150
+
151
+ export function decodeQuery(text: string): string {
152
+ return decode(text.replace(PLUS_RE, ' '));
153
+ }
@@ -0,0 +1,25 @@
1
+ import type { RouteRecord } from '../types';
2
+
3
+ /**
4
+ * 判断是否是同一个路由
5
+ */
6
+ export function isSameRoute(from: RouteRecord, to: RouteRecord) {
7
+ return (
8
+ from.matched.length === to.matched.length &&
9
+ from.matched.every((record, i) => record === to.matched[i])
10
+ );
11
+ }
12
+
13
+ /**
14
+ * 判断是否是全等的路由: 路径完全相同
15
+ */
16
+ export function isEqualRoute(from: RouteRecord, to: RouteRecord) {
17
+ return (
18
+ // 这里不仅仅判断了前后的path是否一致
19
+ // 同时判断了匹配路由对象的个数
20
+ // 这是因为在首次初始化时 this.current 的值为 { path:'/',matched:[] }
21
+ // 假如我们打开页面同样为 / 路径时,此时如果单纯判断path那么就会造成无法渲染
22
+ from.fullPath === to.fullPath &&
23
+ from.matched.length === to.matched.length
24
+ );
25
+ }
@@ -0,0 +1,27 @@
1
+ export {
2
+ regexDomain,
3
+ normalizePath,
4
+ parsePath,
5
+ stringifyPath,
6
+ normalizeLocation,
7
+ isPathWithProtocolOrDomain
8
+ } from './path';
9
+ export { isESModule, inBrowser } from './utils';
10
+ export { warn } from './warn';
11
+ export { isSameRoute, isEqualRoute } from './guards';
12
+ export {
13
+ computeScrollPosition,
14
+ scrollToPosition,
15
+ saveScrollPosition,
16
+ getSavedScrollPosition,
17
+ getKeepScrollPosition
18
+ } from './scroll';
19
+ export { openWindow } from './bom';
20
+ export {
21
+ encodeHash,
22
+ encodeParam,
23
+ encodePath,
24
+ encodeQueryKey,
25
+ encodeQueryValue,
26
+ decode
27
+ } from './encoding';
@@ -0,0 +1,44 @@
1
+ import { describe, it } from 'vitest';
2
+
3
+ import {
4
+ isPathWithProtocolOrDomain,
5
+ normalizeLocation,
6
+ normalizePath
7
+ } from './path';
8
+
9
+ // console.log(normalizeLocation({ path: '/' }));
10
+
11
+ // console.log(isPathWithProtocolOrDomain('/'));
12
+ // console.log(isPathWithProtocolOrDomain('www.a.b'));
13
+ // console.log(isPathWithProtocolOrDomain('a.b.cm'));
14
+ // console.log(isPathWithProtocolOrDomain('http://a.cn'));
15
+ // console.log(isPathWithProtocolOrDomain('a.b/path'));
16
+
17
+ describe('testing normalizeLocation', () => {
18
+ it('testing normal domain', ({ expect }) => {
19
+ expect(
20
+ normalizeLocation(
21
+ 'http://localhost:5173/en/en/en/en/en',
22
+ 'http://localhost:5173/en/'
23
+ ).path
24
+ ).toBe('/en/en/en/en');
25
+ });
26
+ it('testing path', ({ expect }) => {
27
+ expect(
28
+ normalizeLocation('/test1/test2?t=https://www-six.betafollowme.com')
29
+ .path
30
+ ).toBe('/test1/test2');
31
+ });
32
+ });
33
+
34
+ describe('testing normalizePath', () => {
35
+ it('testing normal domain', ({ expect }) => {
36
+ expect(normalizePath('test2', 'test1')).toBe('/test1/test2');
37
+ });
38
+ it('testing path', ({ expect }) => {
39
+ expect(
40
+ normalizeLocation('/test1/test2?t=https://www-six.betafollowme.com')
41
+ .path
42
+ ).toBe('/test1/test2');
43
+ });
44
+ });
@@ -0,0 +1,397 @@
1
+ import URLParse from 'url-parse';
2
+
3
+ import type {
4
+ HistoryState,
5
+ Route,
6
+ RouterBase,
7
+ RouterLocation,
8
+ RouterRawLocation
9
+ } from '../types';
10
+ import {
11
+ decode,
12
+ decodeQuery,
13
+ encodeHash,
14
+ encodeQueryKey,
15
+ encodeQueryValue
16
+ } from './encoding';
17
+ import { isValidValue } from './utils';
18
+ import { warn } from './warn';
19
+
20
+ /**
21
+ * 判断路径是否以 http 或 https 开头 或者直接是域名开头
22
+ */
23
+ export const regexDomain =
24
+ /^(?:https?:\/\/|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9](\/.*)?/i;
25
+
26
+ /**
27
+ * 判断路径是否以 scheme 协议开头
28
+ */
29
+ export const regexScheme = /^(?:[a-z][a-z\d+.-]*:.+)/i;
30
+
31
+ /**
32
+ * 判断路径是否以 http(s) 协议开头
33
+ */
34
+ export const regexHttpScheme = /^(http(s)?:\/\/)/;
35
+
36
+ /**
37
+ * 去除URL路径中重复的斜杠,但不改变协议部分的双斜杠。
38
+ *
39
+ * @param url 需要处理的URL字符串。
40
+ * @returns 处理后的URL,重复的斜杠被去除。
41
+ */
42
+ function removeDuplicateSlashes(url: string): string {
43
+ // 正则表达式匹配除了://之外的连续两个或以上斜杠,并替换为一个斜杠
44
+ if (url.includes('://')) {
45
+ const [base, path] = url.split('://');
46
+ const result = path.replace(/\/{2,}/g, '/');
47
+ return `${base}://${result}`;
48
+ }
49
+
50
+ const result = url.replace(/\/{2,}/g, '/');
51
+ return result;
52
+ }
53
+
54
+ /**
55
+ * 格式化路径 主要用于拼接嵌套路由的路径
56
+ * 返回的格式化后的路径如果有协议则以协议开头,如果没有协议则以/开头
57
+ */
58
+ export function normalizePath(path: string, parentPath?: string) {
59
+ // 如果path以/开头 说明是绝对路径,不需要拼接路径
60
+ // 按 Hanson 要求,不提供绝对路径
61
+ // if (path.startsWith('/')) {
62
+ // return removeDuplicateSlashes(path);
63
+ // }
64
+ let normalizedPath = parentPath ? `${parentPath}/${path}` : `${path}`;
65
+
66
+ // 当解析的路径不是以http 或 https 协议开头时,给开头加上/
67
+ if (
68
+ !regexHttpScheme.test(normalizedPath) &&
69
+ !normalizedPath.startsWith('/')
70
+ ) {
71
+ normalizedPath = `/${normalizedPath}`;
72
+ }
73
+
74
+ return (
75
+ // 只有存在父级路由才进行路由拼接
76
+ removeDuplicateSlashes(normalizedPath) // 将多个斜杠 / 替换为单个斜杠 /
77
+ .replace(/\/$/, '') || // 移除结尾的斜杠 /
78
+ '/' // 为空字符串时至少返回单个 /
79
+ );
80
+ }
81
+
82
+ /**
83
+ * 路径解析方法
84
+ * @example 输入 https://www.google.com/test1/test2?a=1&b=2#123 输出 { pathname: '/test1/test2', query: { a: '1', b: '2' }, queryArray: { a: ['1'], b: ['2'] }, hash: '123' }
85
+ * 输入 /test1/test2?a=1&b=2#123 同样输出 { pathname: '/test1/test2', query: { a: '1', b: '2' }, queryArray: { a: ['1'], b: ['2'] }, hash: '123' }
86
+ */
87
+ export function parsePath(path = ''): {
88
+ pathname: string;
89
+ query: Record<string, string>;
90
+ queryArray: Record<string, string[]>;
91
+ hash: string;
92
+ } {
93
+ path = normalizePath(path);
94
+ const { pathname, query, hash } = new URLParse(path || '/');
95
+ const queryObj = {};
96
+ const queryArray = {};
97
+ if (query.length > 0) {
98
+ query
99
+ .slice(1)
100
+ .split('&')
101
+ .forEach((item) => {
102
+ let [key = '', value = ''] = item.split('=');
103
+ key = decode(key);
104
+ value = decodeQuery(value);
105
+ if (key) {
106
+ queryObj[key] = value;
107
+ queryArray[key] = (queryArray[key] || []).concat(value);
108
+ }
109
+ // queryArray[key] = [
110
+ // ...(queryArray[key] || []),
111
+ // ...(value !== undefined ? [value] : [])
112
+ // ];
113
+ });
114
+ }
115
+ return {
116
+ pathname,
117
+ query: queryObj,
118
+ queryArray,
119
+ hash: decode(hash)
120
+ };
121
+ }
122
+
123
+ /**
124
+ * 将path query hash合并为完整路径
125
+ * @example stringifyPath({ pathname: '/news', query: { a: '1' }, hash: '123' }) 输出 '/news?a=1#123'
126
+ */
127
+ export function stringifyPath(
128
+ {
129
+ pathname = '',
130
+ query = {},
131
+ queryArray = {},
132
+ hash = ''
133
+ }: {
134
+ pathname: string;
135
+ /* 按 Hanson 要求加入 undefined 类型 */
136
+ query: Record<string, string | undefined>;
137
+ queryArray: Record<string, string[]>;
138
+ hash: string;
139
+ } = {
140
+ pathname: '',
141
+ query: {},
142
+ queryArray: {},
143
+ hash: ''
144
+ }
145
+ ): string {
146
+ const queryString = Object.entries(
147
+ Object.assign({}, query, queryArray)
148
+ ).reduce((acc, [key, value]) => {
149
+ let query = '';
150
+ const encodedKey = encodeQueryKey(key);
151
+
152
+ if (Array.isArray(value)) {
153
+ query = value.reduce((all, item) => {
154
+ if (!isValidValue(item)) return all;
155
+ const encodedValue = encodeQueryValue(item);
156
+ if (encodedValue) {
157
+ all = all
158
+ ? `${all}&${encodedKey}=${encodedValue}`
159
+ : `${encodedKey}=${encodedValue}`;
160
+ }
161
+ return all;
162
+ }, '');
163
+ } else {
164
+ const encodedValue = encodeQueryValue(value);
165
+ if (isValidValue(value)) {
166
+ query = `${encodedKey}=${encodedValue}`;
167
+ }
168
+ }
169
+
170
+ if (query) {
171
+ acc = acc ? `${acc}&${query}` : `?${query}`;
172
+ }
173
+
174
+ return acc;
175
+ }, '');
176
+
177
+ const hashContent = hash.startsWith('#') ? hash.replace(/^#/, '') : hash;
178
+ const hashString = hashContent ? `#${encodeHash(hashContent)}` : '';
179
+ return `${pathname}${queryString}${hashString}`;
180
+ }
181
+
182
+ /**
183
+ * 标准化 RouterLocation 字段
184
+ */
185
+ export function normalizeLocation(
186
+ rawLocation: RouterRawLocation,
187
+ base: RouterBase = ''
188
+ ): RouterLocation & {
189
+ path: string;
190
+ base: string;
191
+ queryArray: Record<string, string[]>;
192
+ } {
193
+ let pathname = '';
194
+ /* 按 Hanson 要求加入 undefined 类型 */
195
+ let query: Record<string, string | undefined> = {};
196
+ let queryArray: Record<string, string[]> = {};
197
+ let hash = '';
198
+ let params: Record<string, string> | undefined;
199
+ let state: HistoryState = {};
200
+
201
+ if (typeof rawLocation === 'object') {
202
+ const parsedOption = parsePath(rawLocation.path);
203
+ pathname = parsedOption.pathname;
204
+
205
+ // 只有在rawLocation初始传入了 query 或 queryArray 时才使用 rawLocation
206
+ if (rawLocation.query || rawLocation.queryArray) {
207
+ queryArray = rawLocation.queryArray || {};
208
+ query = rawLocation.query || {};
209
+ } else {
210
+ queryArray = parsedOption.queryArray;
211
+ query = parsedOption.query;
212
+ }
213
+
214
+ hash = rawLocation.hash || parsedOption.hash;
215
+
216
+ params = rawLocation.params; // params 不使用默认值
217
+ state = rawLocation.state || {};
218
+ } else {
219
+ const parsedOption = parsePath(rawLocation);
220
+ pathname = parsedOption.pathname;
221
+ query = parsedOption.query;
222
+ queryArray = parsedOption.queryArray;
223
+ hash = parsedOption.hash;
224
+ }
225
+
226
+ const fullPath = stringifyPath({
227
+ pathname,
228
+ query,
229
+ queryArray,
230
+ hash
231
+ });
232
+ const baseString = normalizePath(
233
+ typeof base === 'function'
234
+ ? base({
235
+ fullPath,
236
+ query,
237
+ queryArray,
238
+ hash
239
+ })
240
+ : base
241
+ );
242
+
243
+ let path = pathname;
244
+ // 如果 base 部分包含域名
245
+ if (regexDomain.test(baseString)) {
246
+ const { pathname } = new URLParse(baseString);
247
+ path = normalizePath(path.replace(new RegExp(`^(${pathname})`), ''));
248
+ }
249
+ path = normalizePath(path.replace(new RegExp(`^(${baseString})`), ''));
250
+
251
+ const { query: realQuery, queryArray: realQueryArray } =
252
+ parsePath(fullPath);
253
+
254
+ const res: RouterLocation & {
255
+ path: string;
256
+ base: string;
257
+ queryArray: Record<string, string[]>;
258
+ } = {
259
+ base: baseString,
260
+ path,
261
+ query: realQuery,
262
+ queryArray: realQueryArray,
263
+ hash,
264
+ state
265
+ };
266
+ if (params) res.params = params;
267
+ return res;
268
+ }
269
+
270
+ /**
271
+ * 判断路径是否以协议或域名开头
272
+ */
273
+ export function isPathWithProtocolOrDomain(location: RouterRawLocation): {
274
+ /**
275
+ * 是否以协议或域名开头
276
+ */
277
+ flag: boolean;
278
+ /**
279
+ * 虚假的 route 信息,内部跳转时无法信任,只有外站跳转时使用
280
+ */
281
+ route: Route;
282
+ } {
283
+ let url = '';
284
+ let state = {};
285
+
286
+ if (typeof location === 'string') {
287
+ url = location;
288
+ } else {
289
+ state = location.state || {};
290
+ const {
291
+ path,
292
+ query = {},
293
+ queryArray,
294
+ hash = '',
295
+ ...nLocation
296
+ } = normalizeLocation(location);
297
+ url = stringifyPath({
298
+ ...nLocation,
299
+ query,
300
+ queryArray,
301
+ pathname: path,
302
+ hash
303
+ });
304
+ }
305
+
306
+ // 如果以 scheme 协议开头 并且不是 http(s) 协议开头 则认为是外站跳转
307
+ if (regexScheme.test(url) && !regexHttpScheme.test(url)) {
308
+ try {
309
+ const {
310
+ hash,
311
+ host,
312
+ hostname,
313
+ href,
314
+ origin,
315
+ pathname,
316
+ port,
317
+ protocol,
318
+ search
319
+ } = new URL(url);
320
+ const route: Route = {
321
+ hash,
322
+ host,
323
+ hostname,
324
+ href,
325
+ origin,
326
+ pathname,
327
+ port,
328
+ protocol,
329
+ search,
330
+ params: {},
331
+ query: {},
332
+ queryArray: {},
333
+ state,
334
+ meta: {},
335
+ path: pathname,
336
+ fullPath: url,
337
+ base: '',
338
+ matched: []
339
+ };
340
+ return {
341
+ flag: true,
342
+ route
343
+ };
344
+ } catch (error) {
345
+ warn(error);
346
+ }
347
+ }
348
+
349
+ if (!/^https?:\/\//i.test(url)) {
350
+ url = `http://${url}`;
351
+ }
352
+
353
+ const {
354
+ hash,
355
+ host,
356
+ hostname,
357
+ href,
358
+ origin,
359
+ pathname,
360
+ port,
361
+ protocol,
362
+ query: search
363
+ } = new URLParse(url);
364
+ const { query = {}, queryArray } = normalizeLocation(url);
365
+ const route: Route = {
366
+ href,
367
+ origin,
368
+ host,
369
+ protocol,
370
+ hostname,
371
+ port,
372
+ pathname,
373
+ search,
374
+ hash,
375
+ query,
376
+ queryArray,
377
+ params: {},
378
+ state,
379
+ meta: {},
380
+ path: pathname,
381
+ fullPath: `${pathname}${search}${hash}`,
382
+ base: '',
383
+ matched: []
384
+ };
385
+
386
+ if (regexDomain.test(url)) {
387
+ return {
388
+ flag: true,
389
+ route
390
+ };
391
+ }
392
+
393
+ return {
394
+ flag: false,
395
+ route
396
+ };
397
+ }