@lwrjs/router 0.10.0-alpha.1 → 0.10.0-alpha.10

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 (97) hide show
  1. package/build/bundle/prod/lwr/navigation/es/modules/lwr/currentView/currentView.d.ts +28 -0
  2. package/build/bundle/prod/lwr/navigation/navigation.js +1 -1
  3. package/build/bundle/prod/lwr/router/es/modules/lwr/currentView/currentView.d.ts +28 -0
  4. package/build/bundle/prod/lwr/router/router.js +1 -1
  5. package/build/bundle/prod/lwr/routerContainer/es/modules/lwr/currentView/currentView.d.ts +28 -0
  6. package/build/bundle/prod/lwr/routerContainer/routerContainer.js +1 -1
  7. package/build/cjs/modules/lwr/outlet/outlet.cjs +4 -1
  8. package/build/es/modules/lwr/contextProvider/contextProvider.js +30 -0
  9. package/build/es/modules/lwr/contextUtils/contextInfo.js +93 -0
  10. package/build/es/modules/lwr/contextUtils/contextUtils.js +77 -0
  11. package/build/es/modules/lwr/contextUtils/navigationApiStore.js +46 -0
  12. package/build/{modules → es/modules}/lwr/currentPageReference/currentPageReference.d.ts +1 -1
  13. package/build/es/modules/lwr/currentPageReference/currentPageReference.js +14 -0
  14. package/build/{modules → es/modules}/lwr/currentView/currentView.d.ts +2 -2
  15. package/build/es/modules/lwr/currentView/currentView.js +62 -0
  16. package/build/{modules → es/modules}/lwr/domRouter/domRouter.d.ts +2 -2
  17. package/build/es/modules/lwr/domRouter/domRouter.js +441 -0
  18. package/build/es/modules/lwr/domRouterUtils/domRouterUtils.js +3 -0
  19. package/build/es/modules/lwr/domRouterUtils/historyUtils.js +30 -0
  20. package/build/{modules → es/modules}/lwr/domRouterUtils/types.d.ts +1 -1
  21. package/build/es/modules/lwr/domRouterUtils/types.js +2 -0
  22. package/build/es/modules/lwr/domRouterUtils/uriUtils.js +69 -0
  23. package/build/es/modules/lwr/historyRouter/historyRouter.js +88 -0
  24. package/build/es/modules/lwr/navigation/navigation.js +20 -0
  25. package/build/es/modules/lwr/navigation/navigationApi.js +27 -0
  26. package/build/es/modules/lwr/navigation/navigationMixin.js +76 -0
  27. package/build/{modules → es/modules}/lwr/navigationContext/navigationContext.d.ts +2 -2
  28. package/build/es/modules/lwr/navigationContext/navigationContext.js +10 -0
  29. package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +7 -0
  30. package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.js +5 -0
  31. package/build/es/modules/lwr/observable/observable.js +71 -0
  32. package/build/{modules → es/modules}/lwr/outlet/outlet.d.ts +1 -0
  33. package/build/es/modules/lwr/outlet/outlet.js +69 -0
  34. package/build/es/modules/lwr/router/router.js +201 -0
  35. package/build/es/modules/lwr/routerBridge/routerBridge.js +85 -0
  36. package/build/es/modules/lwr/routerContainer/routerContainer.js +116 -0
  37. package/build/es/modules/lwr/routerContainer/utils.js +83 -0
  38. package/build/es/modules/lwr/routerErrors/routerErrors.js +154 -0
  39. package/build/es/modules/lwr/routerUtils/domUtils.js +3 -0
  40. package/build/{modules → es/modules}/lwr/routerUtils/filterUtils.d.ts +1 -1
  41. package/build/es/modules/lwr/routerUtils/filterUtils.js +74 -0
  42. package/build/es/modules/lwr/routerUtils/parseUtils.js +182 -0
  43. package/build/{modules → es/modules}/lwr/routerUtils/pathToRegexp.d.ts +5 -5
  44. package/build/es/modules/lwr/routerUtils/pathToRegexp.js +415 -0
  45. package/build/es/modules/lwr/routerUtils/routeDefUtils.js +204 -0
  46. package/build/es/modules/lwr/routerUtils/routeUtils.js +239 -0
  47. package/build/es/modules/lwr/routerUtils/routerUtils.js +19 -0
  48. package/build/es/modules/lwr/routerUtils/typeUtils.js +112 -0
  49. package/build/{modules → es/modules}/lwr/routerUtils/types.d.ts +23 -23
  50. package/build/es/modules/lwr/routerUtils/types.js +2 -0
  51. package/build/es/modules/lwr/routerUtils/uriUtils.js +134 -0
  52. package/build/modules/lwr/contextUtils/contextUtils.js +5 -0
  53. package/build/modules/lwr/contextUtils/navigationApiStore.js +7 -0
  54. package/build/modules/lwr/domRouter/domRouter.js +6 -0
  55. package/build/modules/lwr/domRouterUtils/historyUtils.js +0 -1
  56. package/build/modules/lwr/outlet/outlet.css +1 -1
  57. package/build/modules/lwr/outlet/outlet.html +2 -2
  58. package/build/modules/lwr/outlet/outlet.js +3 -4
  59. package/build/modules/lwr/router/router.js +3 -0
  60. package/build/modules/lwr/routerContainer/utils.js +1 -3
  61. package/build/modules/lwr/routerUtils/pathToRegexp.js +17 -0
  62. package/build/modules/lwr/routerUtils/typeUtils.js +0 -1
  63. package/package.json +17 -9
  64. package/pageObjects/outlet.cjs +21 -4
  65. package/pageObjects/outlet.d.ts +10 -2
  66. package/pageObjects/outlet.js +22 -5
  67. package/build/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +0 -7
  68. /package/build/{modules → es/modules}/lwr/contextProvider/contextProvider.d.ts +0 -0
  69. /package/build/{modules → es/modules}/lwr/contextUtils/contextInfo.d.ts +0 -0
  70. /package/build/{modules → es/modules}/lwr/contextUtils/contextUtils.d.ts +0 -0
  71. /package/build/{modules → es/modules}/lwr/contextUtils/navigationApiStore.d.ts +0 -0
  72. /package/build/{modules → es/modules}/lwr/domRouterUtils/domRouterUtils.d.ts +0 -0
  73. /package/build/{modules → es/modules}/lwr/domRouterUtils/historyUtils.d.ts +0 -0
  74. /package/build/{modules → es/modules}/lwr/domRouterUtils/uriUtils.d.ts +0 -0
  75. /package/build/{modules → es/modules}/lwr/historyRouter/historyRouter.d.ts +0 -0
  76. /package/build/{modules → es/modules}/lwr/navigation/navigation.d.ts +0 -0
  77. /package/build/{modules → es/modules}/lwr/navigation/navigationApi.d.ts +0 -0
  78. /package/build/{modules → es/modules}/lwr/navigation/navigationMixin.d.ts +0 -0
  79. /package/build/{modules → es/modules}/lwr/observable/observable.d.ts +0 -0
  80. /package/build/{modules → es/modules}/lwr/router/router.d.ts +0 -0
  81. /package/build/{modules → es/modules}/lwr/routerBridge/routerBridge.d.ts +0 -0
  82. /package/build/{modules → es/modules}/lwr/routerContainer/routerContainer.d.ts +0 -0
  83. /package/build/{modules → es/modules}/lwr/routerContainer/utils.d.ts +0 -0
  84. /package/build/{modules → es/modules}/lwr/routerErrors/routerErrors.d.ts +0 -0
  85. /package/build/{modules → es/modules}/lwr/routerUtils/domUtils.d.ts +0 -0
  86. /package/build/{modules → es/modules}/lwr/routerUtils/parseUtils.d.ts +0 -0
  87. /package/build/{modules → es/modules}/lwr/routerUtils/routeDefUtils.d.ts +0 -0
  88. /package/build/{modules → es/modules}/lwr/routerUtils/routeUtils.d.ts +0 -0
  89. /package/build/{modules → es/modules}/lwr/routerUtils/routerUtils.d.ts +0 -0
  90. /package/build/{modules → es/modules}/lwr/routerUtils/typeUtils.d.ts +0 -0
  91. /package/build/{modules → es/modules}/lwr/routerUtils/uriUtils.d.ts +0 -0
  92. /package/build/{services → es/services}/index.d.ts +0 -0
  93. /package/build/{services → es/services}/index.js +0 -0
  94. /package/build/{services → es/services}/module-provider/index.d.ts +0 -0
  95. /package/build/{services → es/services}/module-provider/index.js +0 -0
  96. /package/build/{services → es/services}/module-provider/utils.d.ts +0 -0
  97. /package/build/{services → es/services}/module-provider/utils.js +0 -0
@@ -0,0 +1,182 @@
1
+ import { messages, invariant } from 'lwr/routerErrors';
2
+ import { pathToRegexp, compile } from './pathToRegexp';
3
+ import { getQueryFromUrl, getPathFromUrl, isParam, getParamName, getQueryNames } from './uriUtils';
4
+ const { INVALID_ROUTE_QUERY, MISSING_ROUTE_TEMPLATE, MISSING_PAGE_BINDING, INVALID_PAGE_BINDING, INVALID_URI_SYNTAX, } = messages;
5
+ /**
6
+ * Parse the route definitions with path-to-regex functionality for paths, and
7
+ * query parameter validation
8
+ *
9
+ * @example
10
+ * {
11
+ * original: the user-defined route definition
12
+ * regex: regular expression based on the route definition path
13
+ * toPath: function which takes an object of parameters and creates a path
14
+ * params: an array of objects with info on each path parameter
15
+ * compiledQuery: an object that represents defined routeDefintion query params
16
+ * queryMatcher: a function that can be used to see if this route defintion
17
+ * mataches against a given input QueryObject
18
+ * }
19
+ */
20
+ export function parseRoutes(config) {
21
+ const { routes, caseSensitive } = config;
22
+ return routes.map((def) => {
23
+ return parseUriRoute(def, caseSensitive);
24
+ });
25
+ }
26
+ /**
27
+ * Given a RouteDefintion, create a CompiledRouteDefinition that can be used to match
28
+ * against input URIs.
29
+ *
30
+ * @param {RouteDefinition} def - A RouteDefintion to parse into a CompiledRouteDefiniton
31
+ * @param {boolean} caseSensitive - determines whether or not the given RouteDefintion should
32
+ * be case sensitive against path literals, and query parameter values
33
+ * @returns {CompiledRouteDefinition} - Object that represents the compiled path so as to be used
34
+ * for matching input URIs
35
+ */
36
+ function parseUriRoute(def, caseSensitive = false) {
37
+ const params = [];
38
+ const { uri, page } = def;
39
+ invariant(!!uri, MISSING_ROUTE_TEMPLATE);
40
+ invariant(isValidUri(uri), INVALID_URI_SYNTAX);
41
+ invariant(!!page, MISSING_PAGE_BINDING);
42
+ const path = getPathFromUrl(uri);
43
+ const query = getQueryFromUrl(uri);
44
+ const regex = pathToRegexp(path, params, {
45
+ sensitive: caseSensitive,
46
+ // True if this is a leaf route, and must match URLs exactly with no trailing segments.
47
+ end: def.exact === false ? false : true,
48
+ });
49
+ const toPath = compile(path, { encode: encodeURIComponent });
50
+ const compiledQuery = compileQueryObject(query);
51
+ const queryMatcher = getQueryMatcher(compiledQuery, caseSensitive);
52
+ const compiledRoute = { original: def, regex, params, toPath, compiledQuery, queryMatcher };
53
+ invariant(isValidPageBinding(compiledRoute), INVALID_PAGE_BINDING);
54
+ return compiledRoute;
55
+ }
56
+ /**
57
+ * Returns true if URI format contains invalid characters
58
+ * - No *, (, ), ;
59
+ * @param uri
60
+ */
61
+ function isValidUri(uri = '') {
62
+ const invalid = ['*', '(', ')', ';'];
63
+ const containsInvalidCharacter = invalid.some((invalidChar) => uri.indexOf(invalidChar) >= 0);
64
+ return !containsInvalidCharacter;
65
+ }
66
+ /**
67
+ * Returns true if the given compiledDef has a valid pageReference binding.
68
+ * - All params in uri are accounted for
69
+ * - No params are used more than once
70
+ * - All params used in binding are defined in uri
71
+ * @param compiledDef
72
+ */
73
+ function isValidPageBinding(compiledDef) {
74
+ const { original: { page } = {}, params, compiledQuery } = compiledDef;
75
+ const pageType = page ? page.type : page;
76
+ const pageAttributes = (page ? page.attributes : page) || {};
77
+ const pageState = (page ? page.state : page) || {};
78
+ if (typeof pageType !== 'string' || typeof pageAttributes !== 'object' || typeof pageState !== 'object') {
79
+ return false;
80
+ }
81
+ const pathParams = Object.values(params).map(({ name }) => name);
82
+ const queryParams = getQueryNames(compiledQuery);
83
+ const allParams = [...pathParams, ...queryParams];
84
+ const attributeBindings = Object.values(pageAttributes).filter(isParam).map(getParamName);
85
+ const stateBindings = Object.values(pageState).filter(isParam).map(getParamName);
86
+ const hasAllParams = allParams.every((paramName) => {
87
+ // Key.name could be an index if it's an unnamed parameter (https://github.com/pillarjs/path-to-regexp#unnamed-parameters)
88
+ // We do not support unnamed parameters.
89
+ if (typeof paramName !== 'string') {
90
+ return false;
91
+ }
92
+ return attributeBindings.indexOf(paramName) >= 0 || stateBindings.indexOf(paramName) >= 0;
93
+ });
94
+ const paramsUsedOnlyOnce = allParams.length === attributeBindings.length + stateBindings.length;
95
+ return !!(page && pageType && pageAttributes && pageState && hasAllParams && paramsUsedOnlyOnce);
96
+ }
97
+ /**
98
+ * Converts a QueryObject create from the routeDefintion.uri, and converts it into a
99
+ * CompiledQuery object that represents the defined 1) queryStringKeys, 2) routeParameter name,
100
+ * and 3) and what input is valid for this query. e.g.,
101
+ *
102
+ * Given the following uri: /path?someKey=:qParam, then "someKey" is the queryStringKey, ":qParam"
103
+ * is the route parameter name, and any input would be valid.
104
+ *
105
+ * @param {QueryObject} queryObject - queryObject retrieved from the routeDefinition uri
106
+ * @returns {CompiledQuery} - CompiledQuery
107
+ */
108
+ export function compileQueryObject(queryObject) {
109
+ const compiled = {};
110
+ Object.keys(queryObject).forEach((qKey) => {
111
+ const qValue = queryObject[qKey];
112
+ // INVALID: ?:qParam=value
113
+ invariant(isParam(qKey) ? qValue === null : true, INVALID_ROUTE_QUERY);
114
+ if (isParam(qKey)) {
115
+ // ?:qParam => ?qParam=:qParam
116
+ compiled[qKey.substr(1)] = {
117
+ routeParamName: qKey,
118
+ };
119
+ }
120
+ else if (qValue && isParam(qValue)) {
121
+ // ?qKey=:qParam
122
+ compiled[qKey] = {
123
+ routeParamName: qValue,
124
+ };
125
+ }
126
+ else {
127
+ // ?qKey OR ?qKey=literal
128
+ compiled[qKey] = {
129
+ literalValue: qValue === null ? null : qValue,
130
+ };
131
+ }
132
+ });
133
+ return compiled;
134
+ }
135
+ /**
136
+ * Takes a CompiledQuery a returns a QueryMatcher function that will match a QueryObject
137
+ * against the CompiledQuery and returns either a MatchedQuery, representing all the
138
+ * input values that match the CompiledQuery, or null if any input query parameter
139
+ * doesn't match the CompiledQuery.
140
+ *
141
+ * @param compiledQuery - CompiledQuery object to use to create the matcher
142
+ * @param {boolean} caseSensitive - true if this should case-sensitive match query values
143
+ * @returns {QueryMatcher} - The queryMatcher function
144
+ */
145
+ export function getQueryMatcher(compiledQuery, caseSensitive = false) {
146
+ const queryMatcher = (queryObject) => {
147
+ const inputKeys = Object.keys(queryObject);
148
+ const defKeys = Object.keys(compiledQuery);
149
+ const hasAllDefKeys = defKeys.every((defKey) => inputKeys.indexOf(defKey) >= 0);
150
+ if (hasAllDefKeys) {
151
+ return defKeys.reduce((matched, defKey) => {
152
+ // If we find that any param doesn't match, escape out.
153
+ if (matched === null)
154
+ return null;
155
+ const { literalValue, routeParamName } = compiledQuery[defKey];
156
+ const inputValue = queryObject[defKey];
157
+ // no literal was provided, default true
158
+ let literalValueMatches = true;
159
+ if (typeof literalValue === 'string') {
160
+ literalValueMatches = caseSensitive
161
+ ? literalValue === inputValue
162
+ : literalValue.toUpperCase() ===
163
+ (inputValue == null ? inputValue : inputValue.toUpperCase());
164
+ }
165
+ else if (literalValue === null) {
166
+ literalValueMatches = inputValue === literalValue;
167
+ }
168
+ if (literalValueMatches) {
169
+ // force null to string for matching
170
+ matched = { ...matched, [defKey]: { value: inputValue, routeParamName } };
171
+ }
172
+ else {
173
+ matched = null;
174
+ }
175
+ return matched;
176
+ }, {});
177
+ }
178
+ return null;
179
+ };
180
+ return queryMatcher;
181
+ }
182
+ //# sourceMappingURL=parseUtils.js.map
@@ -26,7 +26,7 @@ export interface TokensToFunctionOptions {
26
26
  */
27
27
  validate?: boolean;
28
28
  }
29
- export declare type PathFunction<P extends object = object> = (data?: P) => string;
29
+ export type PathFunction<P extends object = object> = (data?: P) => string;
30
30
  /**
31
31
  * Expose a method for transforming tokens into the path function.
32
32
  */
@@ -52,11 +52,11 @@ export interface MatchResult<P extends object = object> {
52
52
  /**
53
53
  * A match is either `false` (no match) or a match result.
54
54
  */
55
- export declare type Match<P extends object = object> = false | MatchResult<P>;
55
+ export type Match<P extends object = object> = false | MatchResult<P>;
56
56
  /**
57
57
  * The match function takes a string and returns whether it matched the path.
58
58
  */
59
- export declare type MatchFunction<P extends object = object> = (path: string) => Match<P>;
59
+ export type MatchFunction<P extends object = object> = (path: string) => Match<P>;
60
60
  /**
61
61
  * Create a path match function from `path-to-regexp` output.
62
62
  */
@@ -74,7 +74,7 @@ export interface Key {
74
74
  /**
75
75
  * A token is a string (nothing special) or key metadata (capture group).
76
76
  */
77
- export declare type Token = string | Key;
77
+ export type Token = string | Key;
78
78
  export interface TokensToRegexpOptions {
79
79
  /**
80
80
  * When `true` the regexp will be case sensitive. (default: `false`)
@@ -112,7 +112,7 @@ export declare function tokensToRegexp(tokens: Token[], keys?: Key[], options?:
112
112
  /**
113
113
  * Supported `path-to-regexp` input types.
114
114
  */
115
- export declare type Path = string | RegExp | Array<string | RegExp>;
115
+ export type Path = string | RegExp | Array<string | RegExp>;
116
116
  /**
117
117
  * Normalize the given path string, returning a regular expression.
118
118
  *
@@ -0,0 +1,415 @@
1
+ /*
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ */
24
+ /**
25
+ * Tokenize input string.
26
+ */
27
+ function lexer(str) {
28
+ const tokens = [];
29
+ let i = 0;
30
+ while (i < str.length) {
31
+ const char = str[i];
32
+ if (char === '*' || char === '+' || char === '?') {
33
+ tokens.push({ type: 'MODIFIER', index: i, value: str[i++] });
34
+ continue;
35
+ }
36
+ if (char === '\\') {
37
+ tokens.push({ type: 'ESCAPED_CHAR', index: i++, value: str[i++] });
38
+ continue;
39
+ }
40
+ if (char === '{') {
41
+ tokens.push({ type: 'OPEN', index: i, value: str[i++] });
42
+ continue;
43
+ }
44
+ if (char === '}') {
45
+ tokens.push({ type: 'CLOSE', index: i, value: str[i++] });
46
+ continue;
47
+ }
48
+ if (char === ':') {
49
+ let name = '';
50
+ let j = i + 1;
51
+ while (j < str.length) {
52
+ const code = str.charCodeAt(j);
53
+ if (
54
+ // `0-9`
55
+ (code >= 48 && code <= 57) ||
56
+ // `A-Z`
57
+ (code >= 65 && code <= 90) ||
58
+ // `a-z`
59
+ (code >= 97 && code <= 122) ||
60
+ // `_`
61
+ code === 95) {
62
+ name += str[j++];
63
+ continue;
64
+ }
65
+ break;
66
+ }
67
+ if (!name)
68
+ throw new TypeError(`Missing parameter name at ${i}`);
69
+ tokens.push({ type: 'NAME', index: i, value: name });
70
+ i = j;
71
+ continue;
72
+ }
73
+ if (char === '(') {
74
+ let count = 1;
75
+ let pattern = '';
76
+ let j = i + 1;
77
+ if (str[j] === '?') {
78
+ throw new TypeError(`Pattern cannot start with "?" at ${j}`);
79
+ }
80
+ while (j < str.length) {
81
+ if (str[j] === '\\') {
82
+ pattern += str[j++] + str[j++];
83
+ continue;
84
+ }
85
+ if (str[j] === ')') {
86
+ count--;
87
+ if (count === 0) {
88
+ j++;
89
+ break;
90
+ }
91
+ }
92
+ else if (str[j] === '(') {
93
+ count++;
94
+ if (str[j + 1] !== '?') {
95
+ throw new TypeError(`Capturing groups are not allowed at ${j}`);
96
+ }
97
+ }
98
+ pattern += str[j++];
99
+ }
100
+ if (count)
101
+ throw new TypeError(`Unbalanced pattern at ${i}`);
102
+ if (!pattern)
103
+ throw new TypeError(`Missing pattern at ${i}`);
104
+ tokens.push({ type: 'PATTERN', index: i, value: pattern });
105
+ i = j;
106
+ continue;
107
+ }
108
+ tokens.push({ type: 'CHAR', index: i, value: str[i++] });
109
+ }
110
+ tokens.push({ type: 'END', index: i, value: '' });
111
+ return tokens;
112
+ }
113
+ /**
114
+ * Escape a regular expression string.
115
+ */
116
+ function escapeString(str) {
117
+ return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1');
118
+ }
119
+ /**
120
+ * Get the flags for a regexp from the options.
121
+ */
122
+ function flags(options) {
123
+ return options && options.sensitive ? '' : 'i';
124
+ }
125
+ /**
126
+ * Parse a string for the raw tokens.
127
+ */
128
+ export function parse(str, options = {}) {
129
+ const tokens = lexer(str);
130
+ const { prefixes = './' } = options;
131
+ const defaultPattern = `[^${escapeString(options.delimiter || '/#?')}]+?`;
132
+ const result = [];
133
+ let key = 0;
134
+ let i = 0;
135
+ let path = '';
136
+ const tryConsume = (type) => {
137
+ if (i < tokens.length && tokens[i].type === type)
138
+ return tokens[i++].value;
139
+ };
140
+ const mustConsume = (type) => {
141
+ const value = tryConsume(type);
142
+ if (value !== undefined)
143
+ return value;
144
+ const { type: nextType, index } = tokens[i];
145
+ throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);
146
+ };
147
+ const consumeText = () => {
148
+ let result = '';
149
+ let value;
150
+ // tslint:disable-next-line
151
+ while ((value = tryConsume('CHAR') || tryConsume('ESCAPED_CHAR'))) {
152
+ result += value;
153
+ }
154
+ return result;
155
+ };
156
+ while (i < tokens.length) {
157
+ const char = tryConsume('CHAR');
158
+ const name = tryConsume('NAME');
159
+ const pattern = tryConsume('PATTERN');
160
+ if (name || pattern) {
161
+ let prefix = char || '';
162
+ if (prefixes.indexOf(prefix) === -1) {
163
+ path += prefix;
164
+ prefix = '';
165
+ }
166
+ if (path) {
167
+ result.push(path);
168
+ path = '';
169
+ }
170
+ result.push({
171
+ name: name || key++,
172
+ prefix,
173
+ suffix: '',
174
+ pattern: pattern || defaultPattern,
175
+ modifier: tryConsume('MODIFIER') || '',
176
+ });
177
+ continue;
178
+ }
179
+ const value = char || tryConsume('ESCAPED_CHAR');
180
+ if (value) {
181
+ path += value;
182
+ continue;
183
+ }
184
+ if (path) {
185
+ result.push(path);
186
+ path = '';
187
+ }
188
+ const open = tryConsume('OPEN');
189
+ if (open) {
190
+ const prefix = consumeText();
191
+ const name = tryConsume('NAME') || '';
192
+ const pattern = tryConsume('PATTERN') || '';
193
+ const suffix = consumeText();
194
+ mustConsume('CLOSE');
195
+ result.push({
196
+ name: name || (pattern ? key++ : ''),
197
+ pattern: name && !pattern ? defaultPattern : pattern,
198
+ prefix,
199
+ suffix,
200
+ modifier: tryConsume('MODIFIER') || '',
201
+ });
202
+ continue;
203
+ }
204
+ mustConsume('END');
205
+ }
206
+ return result;
207
+ }
208
+ /**
209
+ * Expose a method for transforming tokens into the path function.
210
+ */
211
+ export function tokensToFunction(tokens, options = {}) {
212
+ const reFlags = flags(options);
213
+ const { encode = (x) => x, validate = true } = options;
214
+ // Compile all the tokens into regexps.
215
+ const matches = tokens.map((token) => {
216
+ if (typeof token === 'object') {
217
+ return new RegExp(`^(?:${token.pattern})$`, reFlags);
218
+ }
219
+ });
220
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
221
+ return (data) => {
222
+ let path = '';
223
+ for (let i = 0; i < tokens.length; i++) {
224
+ const token = tokens[i];
225
+ if (typeof token === 'string') {
226
+ path += token;
227
+ continue;
228
+ }
229
+ const value = data ? data[token.name] : undefined;
230
+ const optional = token.modifier === '?' || token.modifier === '*';
231
+ const repeat = token.modifier === '*' || token.modifier === '+';
232
+ if (Array.isArray(value)) {
233
+ if (!repeat) {
234
+ throw new TypeError(`Expected "${token.name}" to not repeat, but got an array`);
235
+ }
236
+ if (value.length === 0) {
237
+ if (optional)
238
+ continue;
239
+ throw new TypeError(`Expected "${token.name}" to not be empty`);
240
+ }
241
+ for (let j = 0; j < value.length; j++) {
242
+ const segment = encode(value[j], token);
243
+ if (validate && !matches[i].test(segment)) {
244
+ throw new TypeError(`Expected all "${token.name}" to match "${token.pattern}", but got "${segment}"`);
245
+ }
246
+ path += token.prefix + segment + token.suffix;
247
+ }
248
+ continue;
249
+ }
250
+ if (typeof value === 'string' || typeof value === 'number') {
251
+ const segment = encode(String(value), token);
252
+ if (validate && !matches[i].test(segment)) {
253
+ throw new TypeError(`Expected "${token.name}" to match "${token.pattern}", but got "${segment}"`);
254
+ }
255
+ path += token.prefix + segment + token.suffix;
256
+ continue;
257
+ }
258
+ if (optional)
259
+ continue;
260
+ const typeOfMessage = repeat ? 'an array' : 'a string';
261
+ throw new TypeError(`Expected "${token.name}" to be ${typeOfMessage}`);
262
+ }
263
+ return path;
264
+ };
265
+ }
266
+ /**
267
+ * Compile a string to a template function for the path.
268
+ */
269
+ export function compile(str, options) {
270
+ return tokensToFunction(parse(str, options), options);
271
+ }
272
+ /**
273
+ * Create a path match function from `path-to-regexp` output.
274
+ */
275
+ export function regexpToFunction(re, keys, options = {}) {
276
+ const { decode = (x) => x } = options;
277
+ return function (pathname) {
278
+ const m = re.exec(pathname);
279
+ if (!m)
280
+ return false;
281
+ const { 0: path, index } = m;
282
+ const params = Object.create(null);
283
+ for (let i = 1; i < m.length; i++) {
284
+ // tslint:disable-next-line
285
+ if (m[i] === undefined)
286
+ continue;
287
+ const key = keys[i - 1];
288
+ if (key.modifier === '*' || key.modifier === '+') {
289
+ params[key.name] = m[i].split(key.prefix + key.suffix).map((value) => {
290
+ return decode(value, key);
291
+ });
292
+ }
293
+ else {
294
+ params[key.name] = decode(m[i], key);
295
+ }
296
+ }
297
+ return { path, index, params };
298
+ };
299
+ }
300
+ /**
301
+ * Pull out keys from a regexp.
302
+ */
303
+ function regexpToRegexp(path, keys) {
304
+ if (!keys)
305
+ return path;
306
+ // Use a negative lookahead to match only capturing groups.
307
+ const groups = path.source.match(/\((?!\?)/g);
308
+ if (groups) {
309
+ for (let i = 0; i < groups.length; i++) {
310
+ keys.push({
311
+ name: i,
312
+ prefix: '',
313
+ suffix: '',
314
+ modifier: '',
315
+ pattern: '',
316
+ });
317
+ }
318
+ }
319
+ return path;
320
+ }
321
+ /**
322
+ * Expose a function for taking tokens and returning a RegExp.
323
+ */
324
+ export function tokensToRegexp(tokens, keys, options = {}) {
325
+ const { strict = false, start = true, end = true, encode = (x) => x } = options;
326
+ const endsWith = `[${escapeString(options.endsWith || '')}]|$`;
327
+ const delimiter = `[${escapeString(options.delimiter || '/#?')}]`;
328
+ let route = start ? '^' : '';
329
+ // Iterate over the tokens and create our regexp string.
330
+ for (const token of tokens) {
331
+ if (typeof token === 'string') {
332
+ route += escapeString(encode(token));
333
+ }
334
+ else {
335
+ const prefix = escapeString(encode(token.prefix));
336
+ const suffix = escapeString(encode(token.suffix));
337
+ if (token.pattern) {
338
+ if (keys)
339
+ keys.push(token);
340
+ if (prefix || suffix) {
341
+ if (token.modifier === '+' || token.modifier === '*') {
342
+ const mod = token.modifier === '*' ? '?' : '';
343
+ route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;
344
+ }
345
+ else {
346
+ route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;
347
+ }
348
+ }
349
+ else {
350
+ route += `(${token.pattern})${token.modifier}`;
351
+ }
352
+ }
353
+ else {
354
+ route += `(?:${prefix}${suffix})${token.modifier}`;
355
+ }
356
+ }
357
+ }
358
+ if (end) {
359
+ if (!strict)
360
+ route += `${delimiter}?`;
361
+ route += !options.endsWith ? '$' : `(?=${endsWith})`;
362
+ }
363
+ else {
364
+ const endToken = tokens[tokens.length - 1];
365
+ const isEndDelimited = typeof endToken === 'string'
366
+ ? delimiter.indexOf(endToken[endToken.length - 1]) > -1
367
+ : // tslint:disable-next-line
368
+ endToken === undefined;
369
+ if (!strict) {
370
+ route += `(?:${delimiter}(?=${endsWith}))?`;
371
+ }
372
+ if (!isEndDelimited) {
373
+ route += `(?=${delimiter}|${endsWith})`;
374
+ }
375
+ }
376
+ return new RegExp(route, flags(options));
377
+ }
378
+ /**
379
+ * Create a path regexp from string input.
380
+ */
381
+ function stringToRegexp(path, keys, options) {
382
+ return tokensToRegexp(parse(path, options), keys, options);
383
+ }
384
+ /**
385
+ * Normalize the given path string, returning a regular expression.
386
+ *
387
+ * An empty array can be passed in for the keys, which will hold the
388
+ * placeholder key descriptions. For example, using `/user/:id`, `keys` will
389
+ * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
390
+ */
391
+ export function pathToRegexp(path, keys, options) {
392
+ if (path instanceof RegExp)
393
+ return regexpToRegexp(path, keys);
394
+ // eslint disable reason: co-dependent functions
395
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
396
+ if (Array.isArray(path))
397
+ return arrayToRegexp(path, keys, options);
398
+ return stringToRegexp(path, keys, options);
399
+ }
400
+ /**
401
+ * Create path match function from `path-to-regexp` spec.
402
+ */
403
+ export function match(str, options) {
404
+ const keys = [];
405
+ const re = pathToRegexp(str, keys, options);
406
+ return regexpToFunction(re, keys, options);
407
+ }
408
+ /**
409
+ * Transform an array into a regexp.
410
+ */
411
+ function arrayToRegexp(paths, keys, options) {
412
+ const parts = paths.map((path) => pathToRegexp(path, keys, options).source);
413
+ return new RegExp(`(?:${parts.join('|')})`, flags(options));
414
+ }
415
+ //# sourceMappingURL=pathToRegexp.js.map