@tinkoff/router 0.2.7 → 0.2.8
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.
- package/lib/components/react/context.browser.js +7 -0
- package/lib/components/react/context.es.js +7 -0
- package/lib/components/react/context.js +13 -0
- package/lib/components/react/provider.browser.js +12 -0
- package/lib/components/react/provider.es.js +12 -0
- package/lib/components/react/provider.js +16 -0
- package/lib/components/react/useNavigate.browser.js +17 -0
- package/lib/components/react/useNavigate.es.js +17 -0
- package/lib/components/react/useNavigate.js +21 -0
- package/lib/components/react/useRoute.browser.js +8 -0
- package/lib/components/react/useRoute.es.js +8 -0
- package/lib/components/react/useRoute.js +12 -0
- package/lib/components/react/useRouter.browser.js +8 -0
- package/lib/components/react/useRouter.es.js +8 -0
- package/lib/components/react/useRouter.js +12 -0
- package/lib/components/react/useUrl.browser.js +8 -0
- package/lib/components/react/useUrl.es.js +8 -0
- package/lib/components/react/useUrl.js +12 -0
- package/lib/history/base.browser.js +11 -0
- package/lib/history/base.es.js +11 -0
- package/lib/history/base.js +15 -0
- package/lib/history/client.browser.js +121 -0
- package/lib/history/client.es.js +121 -0
- package/lib/history/client.js +125 -0
- package/lib/history/server.es.js +15 -0
- package/lib/history/server.js +19 -0
- package/lib/history/wrapper.browser.js +72 -0
- package/lib/history/wrapper.es.js +72 -0
- package/lib/history/wrapper.js +80 -0
- package/lib/index.browser.js +12 -1169
- package/lib/index.es.js +12 -1097
- package/lib/index.js +36 -1124
- package/lib/logger.browser.js +15 -0
- package/lib/logger.es.js +15 -0
- package/lib/logger.js +23 -0
- package/lib/router/abstract.browser.js +395 -0
- package/lib/router/abstract.es.js +395 -0
- package/lib/router/abstract.js +404 -0
- package/lib/router/browser.browser.js +166 -0
- package/lib/router/client.browser.js +116 -0
- package/lib/router/client.es.js +116 -0
- package/lib/router/client.js +120 -0
- package/lib/router/clientNoSpa.browser.js +17 -0
- package/lib/router/clientNoSpa.es.js +17 -0
- package/lib/router/clientNoSpa.js +21 -0
- package/lib/router/server.es.js +85 -0
- package/lib/router/server.js +89 -0
- package/lib/tree/constants.browser.js +6 -0
- package/lib/tree/constants.es.js +6 -0
- package/lib/tree/constants.js +13 -0
- package/lib/tree/tree.browser.js +148 -0
- package/lib/tree/tree.es.js +148 -0
- package/lib/tree/tree.js +158 -0
- package/lib/tree/utils.browser.js +77 -0
- package/lib/tree/utils.es.js +77 -0
- package/lib/tree/utils.js +90 -0
- package/lib/utils.browser.js +35 -0
- package/lib/utils.es.js +35 -0
- package/lib/utils.js +48 -0
- package/package.json +5 -6
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import each from '@tinkoff/utils/array/each';
|
|
2
|
+
import find from '@tinkoff/utils/array/find';
|
|
3
|
+
import findIndex from '@tinkoff/utils/array/findIndex';
|
|
4
|
+
import { getParts, parse } from './utils.es.js';
|
|
5
|
+
import { HISTORY_FALLBACK_REGEXP } from './constants.es.js';
|
|
6
|
+
|
|
7
|
+
const createTree = (route) => {
|
|
8
|
+
return {
|
|
9
|
+
route,
|
|
10
|
+
children: Object.create(null),
|
|
11
|
+
parameters: [],
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const createNavigationRoute = (route, pathname, params) => {
|
|
15
|
+
return {
|
|
16
|
+
...route,
|
|
17
|
+
actualPath: pathname,
|
|
18
|
+
params: params !== null && params !== void 0 ? params : {},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
class RouteTree {
|
|
22
|
+
constructor(routes) {
|
|
23
|
+
this.tree = createTree();
|
|
24
|
+
each((route) => this.addRoute(route), routes);
|
|
25
|
+
}
|
|
26
|
+
// eslint-disable-next-line max-statements
|
|
27
|
+
addRoute(route) {
|
|
28
|
+
const parts = getParts(route.path);
|
|
29
|
+
let currentTree = this.tree;
|
|
30
|
+
for (let i = 0; i < parts.length; i++) {
|
|
31
|
+
const part = parts[i];
|
|
32
|
+
const parsed = parse(part);
|
|
33
|
+
if (parsed.type === 1 /* PartType.historyFallback */) {
|
|
34
|
+
currentTree.historyFallbackRoute = route;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (parsed.type === 2 /* PartType.wildcard */) {
|
|
38
|
+
currentTree.wildcardRoute = route;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (parsed.type === 3 /* PartType.parameter */) {
|
|
42
|
+
const { paramName, regexp, optional } = parsed;
|
|
43
|
+
// prevent from creating new entries for same route
|
|
44
|
+
const found = find((par) => par.key === part, currentTree.parameters);
|
|
45
|
+
if (found) {
|
|
46
|
+
currentTree = found.tree;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const parameter = {
|
|
50
|
+
key: part,
|
|
51
|
+
paramName,
|
|
52
|
+
regexp,
|
|
53
|
+
optional,
|
|
54
|
+
tree: createTree(),
|
|
55
|
+
};
|
|
56
|
+
if (regexp && !optional) {
|
|
57
|
+
// insert parameters with regexp before
|
|
58
|
+
const index = findIndex((par) => !par.regexp, currentTree.parameters);
|
|
59
|
+
currentTree.parameters.splice(index, 0, parameter);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
currentTree.parameters.push(parameter);
|
|
63
|
+
}
|
|
64
|
+
currentTree = parameter.tree;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
if (!currentTree.children[part]) {
|
|
69
|
+
currentTree.children[part] = createTree();
|
|
70
|
+
}
|
|
71
|
+
currentTree = currentTree.children[part];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
currentTree.route = route;
|
|
75
|
+
}
|
|
76
|
+
getRoute(pathname) {
|
|
77
|
+
return this.findRoute(pathname, 'route');
|
|
78
|
+
}
|
|
79
|
+
getWildcard(pathname) {
|
|
80
|
+
return this.findRoute(pathname, 'wildcardRoute');
|
|
81
|
+
}
|
|
82
|
+
getHistoryFallback(pathname) {
|
|
83
|
+
const route = this.findRoute(pathname, 'historyFallbackRoute');
|
|
84
|
+
return (route && {
|
|
85
|
+
...route,
|
|
86
|
+
// remove <history-fallback> from path
|
|
87
|
+
actualPath: route.path.replace(HISTORY_FALLBACK_REGEXP, '') || '/',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// eslint-disable-next-line max-statements
|
|
91
|
+
findRoute(pathname, propertyName) {
|
|
92
|
+
// we should use exact match only for classic route
|
|
93
|
+
// as special routes (for not-found and history-fallback) are defined for whole subtree
|
|
94
|
+
const exactMatch = propertyName === 'route';
|
|
95
|
+
const parts = getParts(pathname);
|
|
96
|
+
const queue = [
|
|
97
|
+
[this.tree, 0],
|
|
98
|
+
];
|
|
99
|
+
while (queue.length) {
|
|
100
|
+
const [currentTree, index, params] = queue.pop();
|
|
101
|
+
const { children, parameters } = currentTree;
|
|
102
|
+
// this flag mean we can only check for optional parameters
|
|
103
|
+
// as we didn't find static route for this path, but still may find
|
|
104
|
+
// some inner route inside optional branch
|
|
105
|
+
// the value of parameter will be empty in this case ofc
|
|
106
|
+
let optionalOnly = false;
|
|
107
|
+
if (index >= parts.length) {
|
|
108
|
+
if (currentTree[propertyName]) {
|
|
109
|
+
return createNavigationRoute(currentTree[propertyName], pathname, params);
|
|
110
|
+
}
|
|
111
|
+
// here we cant check any options except for optional parameters
|
|
112
|
+
optionalOnly = true;
|
|
113
|
+
}
|
|
114
|
+
// first add to queue special routes (not-found or history-fallback) to check it after other cases, as it will be processed last
|
|
115
|
+
if (!exactMatch && currentTree[propertyName]) {
|
|
116
|
+
queue.push([currentTree, parts.length, params]);
|
|
117
|
+
}
|
|
118
|
+
const part = parts[index];
|
|
119
|
+
const child = children[part];
|
|
120
|
+
// then add checks for only optional
|
|
121
|
+
for (const param of parameters) {
|
|
122
|
+
const { optional, tree } = param;
|
|
123
|
+
if (optional) {
|
|
124
|
+
queue.push([tree, index, params]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (optionalOnly) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// for non-optional cases
|
|
131
|
+
for (let i = parameters.length - 1; i >= 0; i--) {
|
|
132
|
+
const param = parameters[i];
|
|
133
|
+
const { paramName, tree, regexp } = param;
|
|
134
|
+
const match = regexp === null || regexp === void 0 ? void 0 : regexp.exec(part);
|
|
135
|
+
const paramValue = regexp ? match === null || match === void 0 ? void 0 : match[1] : part;
|
|
136
|
+
if (paramValue) {
|
|
137
|
+
queue.push([tree, index + 1, { ...params, [paramName]: paramValue }]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (child) {
|
|
141
|
+
// add checks for static child subtree last as it will be processed first after queue.pop
|
|
142
|
+
queue.push([child, index + 1, params]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export { RouteTree };
|
package/lib/tree/tree.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var each = require('@tinkoff/utils/array/each');
|
|
6
|
+
var find = require('@tinkoff/utils/array/find');
|
|
7
|
+
var findIndex = require('@tinkoff/utils/array/findIndex');
|
|
8
|
+
var utils = require('./utils.js');
|
|
9
|
+
var constants = require('./constants.js');
|
|
10
|
+
|
|
11
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
12
|
+
|
|
13
|
+
var each__default = /*#__PURE__*/_interopDefaultLegacy(each);
|
|
14
|
+
var find__default = /*#__PURE__*/_interopDefaultLegacy(find);
|
|
15
|
+
var findIndex__default = /*#__PURE__*/_interopDefaultLegacy(findIndex);
|
|
16
|
+
|
|
17
|
+
const createTree = (route) => {
|
|
18
|
+
return {
|
|
19
|
+
route,
|
|
20
|
+
children: Object.create(null),
|
|
21
|
+
parameters: [],
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
const createNavigationRoute = (route, pathname, params) => {
|
|
25
|
+
return {
|
|
26
|
+
...route,
|
|
27
|
+
actualPath: pathname,
|
|
28
|
+
params: params !== null && params !== void 0 ? params : {},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
class RouteTree {
|
|
32
|
+
constructor(routes) {
|
|
33
|
+
this.tree = createTree();
|
|
34
|
+
each__default["default"]((route) => this.addRoute(route), routes);
|
|
35
|
+
}
|
|
36
|
+
// eslint-disable-next-line max-statements
|
|
37
|
+
addRoute(route) {
|
|
38
|
+
const parts = utils.getParts(route.path);
|
|
39
|
+
let currentTree = this.tree;
|
|
40
|
+
for (let i = 0; i < parts.length; i++) {
|
|
41
|
+
const part = parts[i];
|
|
42
|
+
const parsed = utils.parse(part);
|
|
43
|
+
if (parsed.type === 1 /* PartType.historyFallback */) {
|
|
44
|
+
currentTree.historyFallbackRoute = route;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (parsed.type === 2 /* PartType.wildcard */) {
|
|
48
|
+
currentTree.wildcardRoute = route;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (parsed.type === 3 /* PartType.parameter */) {
|
|
52
|
+
const { paramName, regexp, optional } = parsed;
|
|
53
|
+
// prevent from creating new entries for same route
|
|
54
|
+
const found = find__default["default"]((par) => par.key === part, currentTree.parameters);
|
|
55
|
+
if (found) {
|
|
56
|
+
currentTree = found.tree;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const parameter = {
|
|
60
|
+
key: part,
|
|
61
|
+
paramName,
|
|
62
|
+
regexp,
|
|
63
|
+
optional,
|
|
64
|
+
tree: createTree(),
|
|
65
|
+
};
|
|
66
|
+
if (regexp && !optional) {
|
|
67
|
+
// insert parameters with regexp before
|
|
68
|
+
const index = findIndex__default["default"]((par) => !par.regexp, currentTree.parameters);
|
|
69
|
+
currentTree.parameters.splice(index, 0, parameter);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
currentTree.parameters.push(parameter);
|
|
73
|
+
}
|
|
74
|
+
currentTree = parameter.tree;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
if (!currentTree.children[part]) {
|
|
79
|
+
currentTree.children[part] = createTree();
|
|
80
|
+
}
|
|
81
|
+
currentTree = currentTree.children[part];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
currentTree.route = route;
|
|
85
|
+
}
|
|
86
|
+
getRoute(pathname) {
|
|
87
|
+
return this.findRoute(pathname, 'route');
|
|
88
|
+
}
|
|
89
|
+
getWildcard(pathname) {
|
|
90
|
+
return this.findRoute(pathname, 'wildcardRoute');
|
|
91
|
+
}
|
|
92
|
+
getHistoryFallback(pathname) {
|
|
93
|
+
const route = this.findRoute(pathname, 'historyFallbackRoute');
|
|
94
|
+
return (route && {
|
|
95
|
+
...route,
|
|
96
|
+
// remove <history-fallback> from path
|
|
97
|
+
actualPath: route.path.replace(constants.HISTORY_FALLBACK_REGEXP, '') || '/',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// eslint-disable-next-line max-statements
|
|
101
|
+
findRoute(pathname, propertyName) {
|
|
102
|
+
// we should use exact match only for classic route
|
|
103
|
+
// as special routes (for not-found and history-fallback) are defined for whole subtree
|
|
104
|
+
const exactMatch = propertyName === 'route';
|
|
105
|
+
const parts = utils.getParts(pathname);
|
|
106
|
+
const queue = [
|
|
107
|
+
[this.tree, 0],
|
|
108
|
+
];
|
|
109
|
+
while (queue.length) {
|
|
110
|
+
const [currentTree, index, params] = queue.pop();
|
|
111
|
+
const { children, parameters } = currentTree;
|
|
112
|
+
// this flag mean we can only check for optional parameters
|
|
113
|
+
// as we didn't find static route for this path, but still may find
|
|
114
|
+
// some inner route inside optional branch
|
|
115
|
+
// the value of parameter will be empty in this case ofc
|
|
116
|
+
let optionalOnly = false;
|
|
117
|
+
if (index >= parts.length) {
|
|
118
|
+
if (currentTree[propertyName]) {
|
|
119
|
+
return createNavigationRoute(currentTree[propertyName], pathname, params);
|
|
120
|
+
}
|
|
121
|
+
// here we cant check any options except for optional parameters
|
|
122
|
+
optionalOnly = true;
|
|
123
|
+
}
|
|
124
|
+
// first add to queue special routes (not-found or history-fallback) to check it after other cases, as it will be processed last
|
|
125
|
+
if (!exactMatch && currentTree[propertyName]) {
|
|
126
|
+
queue.push([currentTree, parts.length, params]);
|
|
127
|
+
}
|
|
128
|
+
const part = parts[index];
|
|
129
|
+
const child = children[part];
|
|
130
|
+
// then add checks for only optional
|
|
131
|
+
for (const param of parameters) {
|
|
132
|
+
const { optional, tree } = param;
|
|
133
|
+
if (optional) {
|
|
134
|
+
queue.push([tree, index, params]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (optionalOnly) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
// for non-optional cases
|
|
141
|
+
for (let i = parameters.length - 1; i >= 0; i--) {
|
|
142
|
+
const param = parameters[i];
|
|
143
|
+
const { paramName, tree, regexp } = param;
|
|
144
|
+
const match = regexp === null || regexp === void 0 ? void 0 : regexp.exec(part);
|
|
145
|
+
const paramValue = regexp ? match === null || match === void 0 ? void 0 : match[1] : part;
|
|
146
|
+
if (paramValue) {
|
|
147
|
+
queue.push([tree, index + 1, { ...params, [paramName]: paramValue }]);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (child) {
|
|
151
|
+
// add checks for static child subtree last as it will be processed first after queue.pop
|
|
152
|
+
queue.push([child, index + 1, params]);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
exports.RouteTree = RouteTree;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import map from '@tinkoff/utils/array/map';
|
|
2
|
+
import { HISTORY_FALLBACK_REGEXP, WILDCARD_REGEXP, PARAMETER_DELIMITER, PARAM_PARSER_REGEXP } from './constants.browser.js';
|
|
3
|
+
import { normalizeTrailingSlash } from '../utils.browser.js';
|
|
4
|
+
|
|
5
|
+
const getParts = (pathname) => pathname
|
|
6
|
+
.split('/')
|
|
7
|
+
.slice(pathname.startsWith('/') ? 1 : 0, pathname.endsWith('/') ? -1 : Infinity);
|
|
8
|
+
const isHistoryFallback = (part) => {
|
|
9
|
+
return HISTORY_FALLBACK_REGEXP.test(part);
|
|
10
|
+
};
|
|
11
|
+
const isWildcard = (part) => {
|
|
12
|
+
return WILDCARD_REGEXP.test(part);
|
|
13
|
+
};
|
|
14
|
+
const isParameterized = (part) => {
|
|
15
|
+
return part.includes(PARAMETER_DELIMITER);
|
|
16
|
+
};
|
|
17
|
+
const parseParameter = (part) => {
|
|
18
|
+
const [prefix = '', param, postfix = ''] = part.split(PARAMETER_DELIMITER);
|
|
19
|
+
const match = PARAM_PARSER_REGEXP.exec(param);
|
|
20
|
+
if (!match) {
|
|
21
|
+
throw new Error('parameters should satisfy pattern "prefix:paramName(regexp)\\?:postfix"');
|
|
22
|
+
}
|
|
23
|
+
const [, paramName, regexp, optional] = match;
|
|
24
|
+
const useRegexp = prefix || postfix || regexp;
|
|
25
|
+
return {
|
|
26
|
+
type: 3 /* PartType.parameter */,
|
|
27
|
+
paramName,
|
|
28
|
+
regexp: useRegexp
|
|
29
|
+
? new RegExp(`^${prefix}(${regexp || '.+'})${optional ? '?' : ''}${postfix}$`)
|
|
30
|
+
: undefined,
|
|
31
|
+
optional: !!optional && !prefix && !postfix,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const parse = (part) => {
|
|
35
|
+
if (isHistoryFallback(part)) {
|
|
36
|
+
return { type: 1 /* PartType.historyFallback */ };
|
|
37
|
+
}
|
|
38
|
+
if (isWildcard(part)) {
|
|
39
|
+
return { type: 2 /* PartType.wildcard */ };
|
|
40
|
+
}
|
|
41
|
+
if (isParameterized(part)) {
|
|
42
|
+
return parseParameter(part);
|
|
43
|
+
}
|
|
44
|
+
return { type: 0 /* PartType.literal */, value: part };
|
|
45
|
+
};
|
|
46
|
+
const makePath = (pathname, params) => {
|
|
47
|
+
const parts = getParts(pathname);
|
|
48
|
+
const result = map((part) => {
|
|
49
|
+
var _a;
|
|
50
|
+
if (isHistoryFallback(part) || isWildcard(part)) {
|
|
51
|
+
throw new Error(`Pathname should be only a string with dynamic parameters, not a special string, got ${pathname}`);
|
|
52
|
+
}
|
|
53
|
+
if (isParameterized(part)) {
|
|
54
|
+
const [prefix = '', param = '', postfix = ''] = part.split(PARAMETER_DELIMITER);
|
|
55
|
+
const match = PARAM_PARSER_REGEXP.exec(param);
|
|
56
|
+
if (!match) {
|
|
57
|
+
throw new Error('parameters should satisfy pattern "prefix:paramName(regexp)\\?:postfix"');
|
|
58
|
+
}
|
|
59
|
+
const [, paramName, regexp, optional] = match;
|
|
60
|
+
const value = (_a = params[paramName]) === null || _a === void 0 ? void 0 : _a.toString();
|
|
61
|
+
if (optional && !value) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
if (!value) {
|
|
65
|
+
throw new Error(`value for parameter for ${paramName} should be defined in params`);
|
|
66
|
+
}
|
|
67
|
+
if (regexp && !new RegExp(regexp).test(value)) {
|
|
68
|
+
throw new Error(`passed parameter for ${paramName} should satisfy regxep: ${regexp}, got: ${value}`);
|
|
69
|
+
}
|
|
70
|
+
return prefix + value + postfix || part;
|
|
71
|
+
}
|
|
72
|
+
return part;
|
|
73
|
+
}, parts).join('/');
|
|
74
|
+
return normalizeTrailingSlash(`/${result}`, pathname.endsWith('/'));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { getParts, isHistoryFallback, isParameterized, isWildcard, makePath, parse };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import map from '@tinkoff/utils/array/map';
|
|
2
|
+
import { HISTORY_FALLBACK_REGEXP, WILDCARD_REGEXP, PARAMETER_DELIMITER, PARAM_PARSER_REGEXP } from './constants.es.js';
|
|
3
|
+
import { normalizeTrailingSlash } from '../utils.es.js';
|
|
4
|
+
|
|
5
|
+
const getParts = (pathname) => pathname
|
|
6
|
+
.split('/')
|
|
7
|
+
.slice(pathname.startsWith('/') ? 1 : 0, pathname.endsWith('/') ? -1 : Infinity);
|
|
8
|
+
const isHistoryFallback = (part) => {
|
|
9
|
+
return HISTORY_FALLBACK_REGEXP.test(part);
|
|
10
|
+
};
|
|
11
|
+
const isWildcard = (part) => {
|
|
12
|
+
return WILDCARD_REGEXP.test(part);
|
|
13
|
+
};
|
|
14
|
+
const isParameterized = (part) => {
|
|
15
|
+
return part.includes(PARAMETER_DELIMITER);
|
|
16
|
+
};
|
|
17
|
+
const parseParameter = (part) => {
|
|
18
|
+
const [prefix = '', param, postfix = ''] = part.split(PARAMETER_DELIMITER);
|
|
19
|
+
const match = PARAM_PARSER_REGEXP.exec(param);
|
|
20
|
+
if (!match) {
|
|
21
|
+
throw new Error('parameters should satisfy pattern "prefix:paramName(regexp)\\?:postfix"');
|
|
22
|
+
}
|
|
23
|
+
const [, paramName, regexp, optional] = match;
|
|
24
|
+
const useRegexp = prefix || postfix || regexp;
|
|
25
|
+
return {
|
|
26
|
+
type: 3 /* PartType.parameter */,
|
|
27
|
+
paramName,
|
|
28
|
+
regexp: useRegexp
|
|
29
|
+
? new RegExp(`^${prefix}(${regexp || '.+'})${optional ? '?' : ''}${postfix}$`)
|
|
30
|
+
: undefined,
|
|
31
|
+
optional: !!optional && !prefix && !postfix,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const parse = (part) => {
|
|
35
|
+
if (isHistoryFallback(part)) {
|
|
36
|
+
return { type: 1 /* PartType.historyFallback */ };
|
|
37
|
+
}
|
|
38
|
+
if (isWildcard(part)) {
|
|
39
|
+
return { type: 2 /* PartType.wildcard */ };
|
|
40
|
+
}
|
|
41
|
+
if (isParameterized(part)) {
|
|
42
|
+
return parseParameter(part);
|
|
43
|
+
}
|
|
44
|
+
return { type: 0 /* PartType.literal */, value: part };
|
|
45
|
+
};
|
|
46
|
+
const makePath = (pathname, params) => {
|
|
47
|
+
const parts = getParts(pathname);
|
|
48
|
+
const result = map((part) => {
|
|
49
|
+
var _a;
|
|
50
|
+
if (isHistoryFallback(part) || isWildcard(part)) {
|
|
51
|
+
throw new Error(`Pathname should be only a string with dynamic parameters, not a special string, got ${pathname}`);
|
|
52
|
+
}
|
|
53
|
+
if (isParameterized(part)) {
|
|
54
|
+
const [prefix = '', param = '', postfix = ''] = part.split(PARAMETER_DELIMITER);
|
|
55
|
+
const match = PARAM_PARSER_REGEXP.exec(param);
|
|
56
|
+
if (!match) {
|
|
57
|
+
throw new Error('parameters should satisfy pattern "prefix:paramName(regexp)\\?:postfix"');
|
|
58
|
+
}
|
|
59
|
+
const [, paramName, regexp, optional] = match;
|
|
60
|
+
const value = (_a = params[paramName]) === null || _a === void 0 ? void 0 : _a.toString();
|
|
61
|
+
if (optional && !value) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
if (!value) {
|
|
65
|
+
throw new Error(`value for parameter for ${paramName} should be defined in params`);
|
|
66
|
+
}
|
|
67
|
+
if (regexp && !new RegExp(regexp).test(value)) {
|
|
68
|
+
throw new Error(`passed parameter for ${paramName} should satisfy regxep: ${regexp}, got: ${value}`);
|
|
69
|
+
}
|
|
70
|
+
return prefix + value + postfix || part;
|
|
71
|
+
}
|
|
72
|
+
return part;
|
|
73
|
+
}, parts).join('/');
|
|
74
|
+
return normalizeTrailingSlash(`/${result}`, pathname.endsWith('/'));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { getParts, isHistoryFallback, isParameterized, isWildcard, makePath, parse };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var map = require('@tinkoff/utils/array/map');
|
|
6
|
+
var constants = require('./constants.js');
|
|
7
|
+
var utils = require('../utils.js');
|
|
8
|
+
|
|
9
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
10
|
+
|
|
11
|
+
var map__default = /*#__PURE__*/_interopDefaultLegacy(map);
|
|
12
|
+
|
|
13
|
+
const getParts = (pathname) => pathname
|
|
14
|
+
.split('/')
|
|
15
|
+
.slice(pathname.startsWith('/') ? 1 : 0, pathname.endsWith('/') ? -1 : Infinity);
|
|
16
|
+
const isHistoryFallback = (part) => {
|
|
17
|
+
return constants.HISTORY_FALLBACK_REGEXP.test(part);
|
|
18
|
+
};
|
|
19
|
+
const isWildcard = (part) => {
|
|
20
|
+
return constants.WILDCARD_REGEXP.test(part);
|
|
21
|
+
};
|
|
22
|
+
const isParameterized = (part) => {
|
|
23
|
+
return part.includes(constants.PARAMETER_DELIMITER);
|
|
24
|
+
};
|
|
25
|
+
const parseParameter = (part) => {
|
|
26
|
+
const [prefix = '', param, postfix = ''] = part.split(constants.PARAMETER_DELIMITER);
|
|
27
|
+
const match = constants.PARAM_PARSER_REGEXP.exec(param);
|
|
28
|
+
if (!match) {
|
|
29
|
+
throw new Error('parameters should satisfy pattern "prefix:paramName(regexp)\\?:postfix"');
|
|
30
|
+
}
|
|
31
|
+
const [, paramName, regexp, optional] = match;
|
|
32
|
+
const useRegexp = prefix || postfix || regexp;
|
|
33
|
+
return {
|
|
34
|
+
type: 3 /* PartType.parameter */,
|
|
35
|
+
paramName,
|
|
36
|
+
regexp: useRegexp
|
|
37
|
+
? new RegExp(`^${prefix}(${regexp || '.+'})${optional ? '?' : ''}${postfix}$`)
|
|
38
|
+
: undefined,
|
|
39
|
+
optional: !!optional && !prefix && !postfix,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
const parse = (part) => {
|
|
43
|
+
if (isHistoryFallback(part)) {
|
|
44
|
+
return { type: 1 /* PartType.historyFallback */ };
|
|
45
|
+
}
|
|
46
|
+
if (isWildcard(part)) {
|
|
47
|
+
return { type: 2 /* PartType.wildcard */ };
|
|
48
|
+
}
|
|
49
|
+
if (isParameterized(part)) {
|
|
50
|
+
return parseParameter(part);
|
|
51
|
+
}
|
|
52
|
+
return { type: 0 /* PartType.literal */, value: part };
|
|
53
|
+
};
|
|
54
|
+
const makePath = (pathname, params) => {
|
|
55
|
+
const parts = getParts(pathname);
|
|
56
|
+
const result = map__default["default"]((part) => {
|
|
57
|
+
var _a;
|
|
58
|
+
if (isHistoryFallback(part) || isWildcard(part)) {
|
|
59
|
+
throw new Error(`Pathname should be only a string with dynamic parameters, not a special string, got ${pathname}`);
|
|
60
|
+
}
|
|
61
|
+
if (isParameterized(part)) {
|
|
62
|
+
const [prefix = '', param = '', postfix = ''] = part.split(constants.PARAMETER_DELIMITER);
|
|
63
|
+
const match = constants.PARAM_PARSER_REGEXP.exec(param);
|
|
64
|
+
if (!match) {
|
|
65
|
+
throw new Error('parameters should satisfy pattern "prefix:paramName(regexp)\\?:postfix"');
|
|
66
|
+
}
|
|
67
|
+
const [, paramName, regexp, optional] = match;
|
|
68
|
+
const value = (_a = params[paramName]) === null || _a === void 0 ? void 0 : _a.toString();
|
|
69
|
+
if (optional && !value) {
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
if (!value) {
|
|
73
|
+
throw new Error(`value for parameter for ${paramName} should be defined in params`);
|
|
74
|
+
}
|
|
75
|
+
if (regexp && !new RegExp(regexp).test(value)) {
|
|
76
|
+
throw new Error(`passed parameter for ${paramName} should satisfy regxep: ${regexp}, got: ${value}`);
|
|
77
|
+
}
|
|
78
|
+
return prefix + value + postfix || part;
|
|
79
|
+
}
|
|
80
|
+
return part;
|
|
81
|
+
}, parts).join('/');
|
|
82
|
+
return utils.normalizeTrailingSlash(`/${result}`, pathname.endsWith('/'));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
exports.getParts = getParts;
|
|
86
|
+
exports.isHistoryFallback = isHistoryFallback;
|
|
87
|
+
exports.isParameterized = isParameterized;
|
|
88
|
+
exports.isWildcard = isWildcard;
|
|
89
|
+
exports.makePath = makePath;
|
|
90
|
+
exports.parse = parse;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import T from '@tinkoff/utils/function/T';
|
|
2
|
+
|
|
3
|
+
const isFilePath = (pathname) => {
|
|
4
|
+
return /\/.+\.[^/]+$/.test(pathname);
|
|
5
|
+
};
|
|
6
|
+
const normalizeTrailingSlash = (pathname, trailingSlash = false) => {
|
|
7
|
+
const hasTrailingSlash = pathname.endsWith('/');
|
|
8
|
+
if (trailingSlash) {
|
|
9
|
+
return hasTrailingSlash || isFilePath(pathname) ? pathname : `${pathname}/`;
|
|
10
|
+
}
|
|
11
|
+
return pathname.length > 1 && hasTrailingSlash ? pathname.slice(0, -1) : pathname;
|
|
12
|
+
};
|
|
13
|
+
const normalizeManySlashes = (hrefOrPath) => {
|
|
14
|
+
const [href, ...search] = hrefOrPath.split('?');
|
|
15
|
+
return [href.replace(/\/+/g, '/').replace(/^(\w+):\//, '$1://'), ...search].join('?');
|
|
16
|
+
};
|
|
17
|
+
const isSameHost = typeof window === 'undefined'
|
|
18
|
+
? T
|
|
19
|
+
: (url) => {
|
|
20
|
+
return !url.host || url.host === window.location.host;
|
|
21
|
+
};
|
|
22
|
+
const makeNavigateOptions = (options) => {
|
|
23
|
+
if (typeof options === 'string') {
|
|
24
|
+
return { url: options };
|
|
25
|
+
}
|
|
26
|
+
return options;
|
|
27
|
+
};
|
|
28
|
+
const registerHook = (hooksSet, hook) => {
|
|
29
|
+
hooksSet.add(hook);
|
|
30
|
+
return () => {
|
|
31
|
+
hooksSet.delete(hook);
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { isFilePath, isSameHost, makeNavigateOptions, normalizeManySlashes, normalizeTrailingSlash, registerHook };
|
package/lib/utils.es.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import T from '@tinkoff/utils/function/T';
|
|
2
|
+
|
|
3
|
+
const isFilePath = (pathname) => {
|
|
4
|
+
return /\/.+\.[^/]+$/.test(pathname);
|
|
5
|
+
};
|
|
6
|
+
const normalizeTrailingSlash = (pathname, trailingSlash = false) => {
|
|
7
|
+
const hasTrailingSlash = pathname.endsWith('/');
|
|
8
|
+
if (trailingSlash) {
|
|
9
|
+
return hasTrailingSlash || isFilePath(pathname) ? pathname : `${pathname}/`;
|
|
10
|
+
}
|
|
11
|
+
return pathname.length > 1 && hasTrailingSlash ? pathname.slice(0, -1) : pathname;
|
|
12
|
+
};
|
|
13
|
+
const normalizeManySlashes = (hrefOrPath) => {
|
|
14
|
+
const [href, ...search] = hrefOrPath.split('?');
|
|
15
|
+
return [href.replace(/\/+/g, '/').replace(/^(\w+):\//, '$1://'), ...search].join('?');
|
|
16
|
+
};
|
|
17
|
+
const isSameHost = typeof window === 'undefined'
|
|
18
|
+
? T
|
|
19
|
+
: (url) => {
|
|
20
|
+
return !url.host || url.host === window.location.host;
|
|
21
|
+
};
|
|
22
|
+
const makeNavigateOptions = (options) => {
|
|
23
|
+
if (typeof options === 'string') {
|
|
24
|
+
return { url: options };
|
|
25
|
+
}
|
|
26
|
+
return options;
|
|
27
|
+
};
|
|
28
|
+
const registerHook = (hooksSet, hook) => {
|
|
29
|
+
hooksSet.add(hook);
|
|
30
|
+
return () => {
|
|
31
|
+
hooksSet.delete(hook);
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { isFilePath, isSameHost, makeNavigateOptions, normalizeManySlashes, normalizeTrailingSlash, registerHook };
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var T = require('@tinkoff/utils/function/T');
|
|
6
|
+
|
|
7
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
|
+
|
|
9
|
+
var T__default = /*#__PURE__*/_interopDefaultLegacy(T);
|
|
10
|
+
|
|
11
|
+
const isFilePath = (pathname) => {
|
|
12
|
+
return /\/.+\.[^/]+$/.test(pathname);
|
|
13
|
+
};
|
|
14
|
+
const normalizeTrailingSlash = (pathname, trailingSlash = false) => {
|
|
15
|
+
const hasTrailingSlash = pathname.endsWith('/');
|
|
16
|
+
if (trailingSlash) {
|
|
17
|
+
return hasTrailingSlash || isFilePath(pathname) ? pathname : `${pathname}/`;
|
|
18
|
+
}
|
|
19
|
+
return pathname.length > 1 && hasTrailingSlash ? pathname.slice(0, -1) : pathname;
|
|
20
|
+
};
|
|
21
|
+
const normalizeManySlashes = (hrefOrPath) => {
|
|
22
|
+
const [href, ...search] = hrefOrPath.split('?');
|
|
23
|
+
return [href.replace(/\/+/g, '/').replace(/^(\w+):\//, '$1://'), ...search].join('?');
|
|
24
|
+
};
|
|
25
|
+
const isSameHost = typeof window === 'undefined'
|
|
26
|
+
? T__default["default"]
|
|
27
|
+
: (url) => {
|
|
28
|
+
return !url.host || url.host === window.location.host;
|
|
29
|
+
};
|
|
30
|
+
const makeNavigateOptions = (options) => {
|
|
31
|
+
if (typeof options === 'string') {
|
|
32
|
+
return { url: options };
|
|
33
|
+
}
|
|
34
|
+
return options;
|
|
35
|
+
};
|
|
36
|
+
const registerHook = (hooksSet, hook) => {
|
|
37
|
+
hooksSet.add(hook);
|
|
38
|
+
return () => {
|
|
39
|
+
hooksSet.delete(hook);
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
exports.isFilePath = isFilePath;
|
|
44
|
+
exports.isSameHost = isSameHost;
|
|
45
|
+
exports.makeNavigateOptions = makeNavigateOptions;
|
|
46
|
+
exports.normalizeManySlashes = normalizeManySlashes;
|
|
47
|
+
exports.normalizeTrailingSlash = normalizeTrailingSlash;
|
|
48
|
+
exports.registerHook = registerHook;
|