@midwayjs/core 3.4.0-beta.6 → 3.4.0-beta.7

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.
@@ -101,6 +101,7 @@ class WebControllerGenerator {
101
101
  methodMiddlewares.push(routeMiddlewareFn);
102
102
  }
103
103
  if (this.app.getFrameworkType() === decorator_1.MidwayFrameworkType.WEB_KOA) {
104
+ // egg use path-to-regexp v1 but koa use v6
104
105
  if (typeof routeInfo.url === 'string' && /\*$/.test(routeInfo.url)) {
105
106
  routeInfo.url = routeInfo.url.replace('*', '(.*)');
106
107
  }
@@ -144,7 +144,7 @@ let MidwayServerlessFunctionService = class MidwayServerlessFunctionService exte
144
144
  this.routes.set(prefix, []);
145
145
  this.routesPriority.push({
146
146
  prefix,
147
- priority: -999,
147
+ priority: 0,
148
148
  middleware: [],
149
149
  routerOptions: {},
150
150
  controllerId: undefined,
@@ -28,6 +28,9 @@ export interface RouterInfo {
28
28
  * router description
29
29
  */
30
30
  description?: string;
31
+ /**
32
+ * @deprecated
33
+ */
31
34
  summary?: string;
32
35
  /**
33
36
  * router handler function key,for IoC container load
@@ -73,10 +76,18 @@ export interface RouterInfo {
73
76
  * serverless function metadata
74
77
  */
75
78
  functionMetadata?: any;
79
+ /**
80
+ * url with prefix
81
+ */
82
+ fullUrl?: string;
76
83
  /**
77
84
  * pattern after path-regexp compile
78
85
  */
79
- urlCompiledPattern?: RegExp;
86
+ fullUrlCompiledRegexp?: RegExp;
87
+ /**
88
+ * url after wildcard and can be path-to-regexp by path-to-regexp v6
89
+ */
90
+ fullUrlFlattenString?: string;
80
91
  }
81
92
  export declare type DynamicRouterInfo = Omit<RouterInfo, 'id' | 'method' | 'controllerId' | 'controllerMiddleware' | 'responseMetadata'>;
82
93
  export interface RouterPriority {
@@ -153,6 +164,9 @@ export declare class MidwayWebRouterService {
153
164
  * router description
154
165
  */
155
166
  description?: string;
167
+ /**
168
+ * @deprecated
169
+ */
156
170
  summary?: string;
157
171
  /**
158
172
  * router handler function key,for IoC container load
@@ -198,10 +212,18 @@ export declare class MidwayWebRouterService {
198
212
  * serverless function metadata
199
213
  */
200
214
  functionMetadata?: any;
215
+ /**
216
+ * url with prefix
217
+ */
218
+ fullUrl?: string;
201
219
  /**
202
220
  * pattern after path-regexp compile
203
221
  */
204
- urlCompiledPattern?: RegExp;
222
+ fullUrlCompiledRegexp?: RegExp;
223
+ /**
224
+ * url after wildcard and can be path-to-regexp by path-to-regexp v6
225
+ */
226
+ fullUrlFlattenString?: string;
205
227
  }[];
206
228
  getRoutePriorityList(): Promise<RouterPriority[]>;
207
229
  getRouterTable(): Promise<Map<string, RouterInfo[]>>;
@@ -160,11 +160,12 @@ let MidwayWebRouterService = class MidwayWebRouterService {
160
160
  */
161
161
  addRouter(routerFunction, routerInfoOption) {
162
162
  const prefix = routerInfoOption.prefix || '';
163
+ routerInfoOption.requestMethod = (routerInfoOption.requestMethod || 'GET').toUpperCase();
163
164
  if (!this.routes.has(prefix)) {
164
165
  this.routes.set(prefix, []);
165
166
  this.routesPriority.push({
166
167
  prefix,
167
- priority: -999,
168
+ priority: 0,
168
169
  middleware: [],
169
170
  routerOptions: {},
170
171
  controllerId: undefined,
@@ -257,10 +258,8 @@ let MidwayWebRouterService = class MidwayWebRouterService {
257
258
  this.includeCompileUrlPattern = true;
258
259
  // attach match pattern function
259
260
  for (const item of this.cachedFlattenRouteList) {
260
- if (item.url) {
261
- item.urlCompiledPattern = (0, pathToRegexp_1.pathToRegexp)(item.url, [], {
262
- end: false,
263
- });
261
+ if (item.fullUrlFlattenString) {
262
+ item.fullUrlCompiledRegexp = pathToRegexp_1.PathToRegexpUtil.toRegexp(item.fullUrlFlattenString);
264
263
  }
265
264
  }
266
265
  }
@@ -278,9 +277,9 @@ let MidwayWebRouterService = class MidwayWebRouterService {
278
277
  this.includeCompileUrlPattern = true;
279
278
  // attach match pattern function
280
279
  for (const item of routeArr) {
281
- item.urlCompiledPattern = (0, pathToRegexp_1.pathToRegexp)(item.url, [], {
282
- end: false,
283
- });
280
+ if (item.fullUrlFlattenString) {
281
+ item.fullUrlCompiledRegexp = pathToRegexp_1.PathToRegexpUtil.toRegexp(item.fullUrlFlattenString);
282
+ }
284
283
  }
285
284
  }
286
285
  this.cachedFlattenRouteList = routeArr;
@@ -292,9 +291,9 @@ let MidwayWebRouterService = class MidwayWebRouterService {
292
291
  });
293
292
  let matchedRouterInfo;
294
293
  for (const item of routes) {
295
- if (item.urlCompiledPattern) {
294
+ if (item.fullUrlCompiledRegexp) {
296
295
  if (method.toUpperCase() === item['requestMethod'].toUpperCase() &&
297
- item.urlCompiledPattern.test(routerUrl)) {
296
+ item.fullUrlCompiledRegexp.test(routerUrl)) {
298
297
  matchedRouterInfo = item;
299
298
  break;
300
299
  }
@@ -313,6 +312,18 @@ let MidwayWebRouterService = class MidwayWebRouterService {
313
312
  if (matched && matched.length) {
314
313
  throw new error_1.MidwayDuplicateRouteError(`${routerInfo.requestMethod} ${routerInfo.url}`, `${matched[0].handlerName}`, `${routerInfo.handlerName}`);
315
314
  }
315
+ // format url
316
+ if (!routerInfo.fullUrlFlattenString &&
317
+ routerInfo.url &&
318
+ typeof routerInfo.url === 'string') {
319
+ routerInfo.fullUrl = (0, util_1.joinURLPath)(prefix, routerInfo.url);
320
+ if (/\*$/.test(routerInfo.fullUrl)) {
321
+ routerInfo.fullUrlFlattenString = routerInfo.fullUrl.replace('*', '(.*)');
322
+ }
323
+ else {
324
+ routerInfo.fullUrlFlattenString = routerInfo.fullUrl;
325
+ }
326
+ }
316
327
  prefixList.push(routerInfo);
317
328
  }
318
329
  };
@@ -238,7 +238,7 @@ function toPathMatch(pattern) {
238
238
  return ctx => pattern;
239
239
  }
240
240
  if (typeof pattern === 'string') {
241
- const reg = (0, pathToRegexp_1.pathToRegexp)(pattern, [], { end: false });
241
+ const reg = pathToRegexp_1.PathToRegexpUtil.toRegexp(pattern.replace('*', '(.*)'));
242
242
  if (reg.global)
243
243
  reg.lastIndex = 0;
244
244
  return ctx => reg.test(ctx.path);
@@ -1,24 +1,123 @@
1
+ interface ParseOptions {
2
+ /**
3
+ * Set the default delimiter for repeat parameters. (default: `'/'`)
4
+ */
5
+ delimiter?: string;
6
+ /**
7
+ * List of characters to automatically consider prefixes when parsing.
8
+ */
9
+ prefixes?: string;
10
+ }
1
11
  /**
2
- * this file fork from path-to-regexp package v1.8.0
12
+ * Parse a string for the raw tokens.
3
13
  */
4
- export interface PathToRegexpOptions {
5
- end?: boolean;
14
+ declare function parse(str: string, options?: ParseOptions): Token[];
15
+ interface TokensToFunctionOptions {
16
+ /**
17
+ * When `true` the regexp will be case sensitive. (default: `false`)
18
+ */
19
+ sensitive?: boolean;
20
+ /**
21
+ * Function for encoding input strings for output.
22
+ */
23
+ encode?: (value: string, token: Key) => string;
24
+ /**
25
+ * When `false` the function can produce an invalid (unmatched) path. (default: `true`)
26
+ */
27
+ validate?: boolean;
28
+ }
29
+ /**
30
+ * Compile a string to a template function for the path.
31
+ */
32
+ declare function compile<P extends object = object>(str: string, options?: ParseOptions & TokensToFunctionOptions): PathFunction<P>;
33
+ declare type PathFunction<P extends object = object> = (data?: P) => string;
34
+ interface RegexpToFunctionOptions {
35
+ /**
36
+ * Function for decoding strings for params.
37
+ */
38
+ decode?: (value: string, token: Key) => string;
39
+ }
40
+ /**
41
+ * A match result contains data about the path match.
42
+ */
43
+ interface MatchResult<P extends object = object> {
44
+ path: string;
45
+ index: number;
46
+ params: P;
47
+ }
48
+ /**
49
+ * A match is either `false` (no match) or a match result.
50
+ */
51
+ declare type Match<P extends object = object> = false | MatchResult<P>;
52
+ /**
53
+ * The match function takes a string and returns whether it matched the path.
54
+ */
55
+ declare type MatchFunction<P extends object = object> = (path: string) => Match<P>;
56
+ /**
57
+ * Create path match function from `path-to-regexp` spec.
58
+ */
59
+ declare function match<P extends object = object>(str: Path, options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions): MatchFunction<P>;
60
+ /**
61
+ * Metadata about a key.
62
+ */
63
+ interface Key {
64
+ name: string | number;
65
+ prefix: string;
66
+ suffix: string;
67
+ pattern: string;
68
+ modifier: string;
69
+ }
70
+ /**
71
+ * A token is a string (nothing special) or key metadata (capture group).
72
+ */
73
+ declare type Token = string | Key;
74
+ interface TokensToRegexpOptions {
75
+ /**
76
+ * When `true` the regexp will be case sensitive. (default: `false`)
77
+ */
78
+ sensitive?: boolean;
79
+ /**
80
+ * When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)
81
+ */
6
82
  strict?: boolean;
83
+ /**
84
+ * When `true` the regexp will match to the end of the string. (default: `true`)
85
+ */
86
+ end?: boolean;
87
+ /**
88
+ * When `true` the regexp will match from the beginning of the string. (default: `true`)
89
+ */
90
+ start?: boolean;
91
+ /**
92
+ * Sets the final character for non-ending optimistic matches. (default: `/`)
93
+ */
7
94
  delimiter?: string;
8
- sensitive?: boolean;
95
+ /**
96
+ * List of characters that can also be "end" characters.
97
+ */
98
+ endsWith?: string;
99
+ /**
100
+ * Encode path tokens for use in the `RegExp`.
101
+ */
102
+ encode?: (value: string) => string;
9
103
  }
104
+ /**
105
+ * Supported `path-to-regexp` input types.
106
+ */
107
+ declare type Path = string | RegExp | Array<string | RegExp>;
10
108
  /**
11
109
  * Normalize the given path string, returning a regular expression.
12
110
  *
13
111
  * An empty array can be passed in for the keys, which will hold the
14
112
  * placeholder key descriptions. For example, using `/user/:id`, `keys` will
15
113
  * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
16
- *
17
- * @param {(string|RegExp|Array)} path
18
- * @param {(Array|Object)=} keys
19
- * @param {Object=} options
20
- * @return {!RegExp}
21
114
  */
22
- export declare function pathToRegexp(path: string | RegExp | Array<string>, options?: PathToRegexpOptions): RegExp;
23
- export declare function pathToRegexp(path: string | RegExp | Array<string>, keys: Array<string>, options?: PathToRegexpOptions): RegExp;
115
+ declare function toRegexp(path: Path, keys?: Key[], options?: TokensToRegexpOptions & ParseOptions): RegExp;
116
+ export declare const PathToRegexpUtil: {
117
+ toRegexp: typeof toRegexp;
118
+ compile: typeof compile;
119
+ parse: typeof parse;
120
+ match: typeof match;
121
+ };
122
+ export {};
24
123
  //# sourceMappingURL=pathToRegexp.d.ts.map
@@ -1,268 +1,401 @@
1
1
  "use strict";
2
- /**
3
- * this file fork from path-to-regexp package v1.8.0
4
- */
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.pathToRegexp = void 0;
3
+ exports.PathToRegexpUtil = void 0;
7
4
  /**
8
- * The main path matching regexp utility.
9
- *
10
- * @type {RegExp}
5
+ * Tokenize input string.
11
6
  */
12
- const PATH_REGEXP = new RegExp([
13
- // Match escaped characters that would otherwise appear in future matches.
14
- // This allows the user to escape special characters that won't transform.
15
- '(\\\\.)',
16
- // Match Express-style parameters and un-named parameters with a prefix
17
- // and optional suffixes. Matches appear as:
18
- //
19
- // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
20
- // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
21
- // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
22
- '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))',
23
- ].join('|'), 'g');
7
+ function lexer(str) {
8
+ const tokens = [];
9
+ let i = 0;
10
+ while (i < str.length) {
11
+ const char = str[i];
12
+ if (char === '*' || char === '+' || char === '?') {
13
+ tokens.push({ type: 'MODIFIER', index: i, value: str[i++] });
14
+ continue;
15
+ }
16
+ if (char === '\\') {
17
+ tokens.push({ type: 'ESCAPED_CHAR', index: i++, value: str[i++] });
18
+ continue;
19
+ }
20
+ if (char === '{') {
21
+ tokens.push({ type: 'OPEN', index: i, value: str[i++] });
22
+ continue;
23
+ }
24
+ if (char === '}') {
25
+ tokens.push({ type: 'CLOSE', index: i, value: str[i++] });
26
+ continue;
27
+ }
28
+ if (char === ':') {
29
+ let name = '';
30
+ let j = i + 1;
31
+ while (j < str.length) {
32
+ const code = str.charCodeAt(j);
33
+ if (
34
+ // `0-9`
35
+ (code >= 48 && code <= 57) ||
36
+ // `A-Z`
37
+ (code >= 65 && code <= 90) ||
38
+ // `a-z`
39
+ (code >= 97 && code <= 122) ||
40
+ // `_`
41
+ code === 95) {
42
+ name += str[j++];
43
+ continue;
44
+ }
45
+ break;
46
+ }
47
+ if (!name)
48
+ throw new TypeError(`Missing parameter name at ${i}`);
49
+ tokens.push({ type: 'NAME', index: i, value: name });
50
+ i = j;
51
+ continue;
52
+ }
53
+ if (char === '(') {
54
+ let count = 1;
55
+ let pattern = '';
56
+ let j = i + 1;
57
+ if (str[j] === '?') {
58
+ throw new TypeError(`Pattern cannot start with "?" at ${j}`);
59
+ }
60
+ while (j < str.length) {
61
+ if (str[j] === '\\') {
62
+ pattern += str[j++] + str[j++];
63
+ continue;
64
+ }
65
+ if (str[j] === ')') {
66
+ count--;
67
+ if (count === 0) {
68
+ j++;
69
+ break;
70
+ }
71
+ }
72
+ else if (str[j] === '(') {
73
+ count++;
74
+ if (str[j + 1] !== '?') {
75
+ throw new TypeError(`Capturing groups are not allowed at ${j}`);
76
+ }
77
+ }
78
+ pattern += str[j++];
79
+ }
80
+ if (count)
81
+ throw new TypeError(`Unbalanced pattern at ${i}`);
82
+ if (!pattern)
83
+ throw new TypeError(`Missing pattern at ${i}`);
84
+ tokens.push({ type: 'PATTERN', index: i, value: pattern });
85
+ i = j;
86
+ continue;
87
+ }
88
+ tokens.push({ type: 'CHAR', index: i, value: str[i++] });
89
+ }
90
+ tokens.push({ type: 'END', index: i, value: '' });
91
+ return tokens;
92
+ }
24
93
  /**
25
94
  * Parse a string for the raw tokens.
26
- *
27
- * @param {string} str
28
- * @param {Object=} options
29
- * @return {!Array}
30
95
  */
31
- function parse(str, options) {
32
- const tokens = [];
96
+ function parse(str, options = {}) {
97
+ const tokens = lexer(str);
98
+ const { prefixes = './' } = options;
99
+ const defaultPattern = `[^${escapeString(options.delimiter || '/#?')}]+?`;
100
+ const result = [];
33
101
  let key = 0;
34
- let index = 0;
102
+ let i = 0;
35
103
  let path = '';
36
- const defaultDelimiter = (options && options.delimiter) || '/';
37
- let res;
38
- while ((res = PATH_REGEXP.exec(str)) != null) {
39
- const m = res[0];
40
- const escaped = res[1];
41
- const offset = res.index;
42
- path += str.slice(index, offset);
43
- index = offset + m.length;
44
- // Ignore already escaped sequences.
45
- if (escaped) {
46
- path += escaped[1];
104
+ const tryConsume = (type) => {
105
+ if (i < tokens.length && tokens[i].type === type)
106
+ return tokens[i++].value;
107
+ };
108
+ const mustConsume = (type) => {
109
+ const value = tryConsume(type);
110
+ if (value !== undefined)
111
+ return value;
112
+ const { type: nextType, index } = tokens[i];
113
+ throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);
114
+ };
115
+ const consumeText = () => {
116
+ let result = '';
117
+ let value;
118
+ while ((value = tryConsume('CHAR') || tryConsume('ESCAPED_CHAR'))) {
119
+ result += value;
120
+ }
121
+ return result;
122
+ };
123
+ while (i < tokens.length) {
124
+ const char = tryConsume('CHAR');
125
+ const name = tryConsume('NAME');
126
+ const pattern = tryConsume('PATTERN');
127
+ if (name || pattern) {
128
+ let prefix = char || '';
129
+ if (prefixes.indexOf(prefix) === -1) {
130
+ path += prefix;
131
+ prefix = '';
132
+ }
133
+ if (path) {
134
+ result.push(path);
135
+ path = '';
136
+ }
137
+ result.push({
138
+ name: name || key++,
139
+ prefix,
140
+ suffix: '',
141
+ pattern: pattern || defaultPattern,
142
+ modifier: tryConsume('MODIFIER') || '',
143
+ });
144
+ continue;
145
+ }
146
+ const value = char || tryConsume('ESCAPED_CHAR');
147
+ if (value) {
148
+ path += value;
47
149
  continue;
48
150
  }
49
- const next = str[index];
50
- const prefix = res[2];
51
- const name = res[3];
52
- const capture = res[4];
53
- const group = res[5];
54
- const modifier = res[6];
55
- const asterisk = res[7];
56
- // Push the current path onto the tokens.
57
151
  if (path) {
58
- tokens.push(path);
152
+ result.push(path);
59
153
  path = '';
60
154
  }
61
- const partial = prefix != null && next != null && next !== prefix;
62
- const repeat = modifier === '+' || modifier === '*';
63
- const optional = modifier === '?' || modifier === '*';
64
- const delimiter = res[2] || defaultDelimiter;
65
- const pattern = capture || group;
66
- tokens.push({
67
- name: name || key++,
68
- prefix: prefix || '',
69
- delimiter: delimiter,
70
- optional: optional,
71
- repeat: repeat,
72
- partial: partial,
73
- asterisk: !!asterisk,
74
- pattern: pattern
75
- ? escapeGroup(pattern)
76
- : asterisk
77
- ? '.*'
78
- : '[^' + escapeString(delimiter) + ']+?',
79
- });
80
- }
81
- // Match any characters still remaining.
82
- if (index < str.length) {
83
- path += str.slice(index);
84
- }
85
- // If the path exists, push it onto the end.
86
- if (path) {
87
- tokens.push(path);
155
+ const open = tryConsume('OPEN');
156
+ if (open) {
157
+ const prefix = consumeText();
158
+ const name = tryConsume('NAME') || '';
159
+ const pattern = tryConsume('PATTERN') || '';
160
+ const suffix = consumeText();
161
+ mustConsume('CLOSE');
162
+ result.push({
163
+ name: name || (pattern ? key++ : ''),
164
+ pattern: name && !pattern ? defaultPattern : pattern,
165
+ prefix,
166
+ suffix,
167
+ modifier: tryConsume('MODIFIER') || '',
168
+ });
169
+ continue;
170
+ }
171
+ mustConsume('END');
88
172
  }
89
- return tokens;
173
+ return result;
90
174
  }
91
175
  /**
92
- * Escape a regular expression string.
93
- *
94
- * @param {string} str
95
- * @return {string}
176
+ * Compile a string to a template function for the path.
96
177
  */
97
- function escapeString(str) {
98
- return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1');
178
+ function compile(str, options) {
179
+ return tokensToFunction(parse(str, options), options);
99
180
  }
100
181
  /**
101
- * Escape the capturing group by escaping special characters and meaning.
102
- *
103
- * @param {string} group
104
- * @return {string}
182
+ * Expose a method for transforming tokens into the path function.
105
183
  */
106
- function escapeGroup(group) {
107
- return group.replace(/([=!:$/()])/g, '\\$1');
184
+ function tokensToFunction(tokens, options = {}) {
185
+ const reFlags = flags(options);
186
+ const { encode = (x) => x, validate = true } = options;
187
+ // Compile all the tokens into regexps.
188
+ const matches = tokens.map(token => {
189
+ if (typeof token === 'object') {
190
+ return new RegExp(`^(?:${token.pattern})$`, reFlags);
191
+ }
192
+ });
193
+ return (data) => {
194
+ let path = '';
195
+ for (let i = 0; i < tokens.length; i++) {
196
+ const token = tokens[i];
197
+ if (typeof token === 'string') {
198
+ path += token;
199
+ continue;
200
+ }
201
+ const value = data ? data[token.name] : undefined;
202
+ const optional = token.modifier === '?' || token.modifier === '*';
203
+ const repeat = token.modifier === '*' || token.modifier === '+';
204
+ if (Array.isArray(value)) {
205
+ if (!repeat) {
206
+ throw new TypeError(`Expected "${token.name}" to not repeat, but got an array`);
207
+ }
208
+ if (value.length === 0) {
209
+ if (optional)
210
+ continue;
211
+ throw new TypeError(`Expected "${token.name}" to not be empty`);
212
+ }
213
+ for (let j = 0; j < value.length; j++) {
214
+ const segment = encode(value[j], token);
215
+ if (validate && !matches[i].test(segment)) {
216
+ throw new TypeError(`Expected all "${token.name}" to match "${token.pattern}", but got "${segment}"`);
217
+ }
218
+ path += token.prefix + segment + token.suffix;
219
+ }
220
+ continue;
221
+ }
222
+ if (typeof value === 'string' || typeof value === 'number') {
223
+ const segment = encode(String(value), token);
224
+ if (validate && !matches[i].test(segment)) {
225
+ throw new TypeError(`Expected "${token.name}" to match "${token.pattern}", but got "${segment}"`);
226
+ }
227
+ path += token.prefix + segment + token.suffix;
228
+ continue;
229
+ }
230
+ if (optional)
231
+ continue;
232
+ const typeOfMessage = repeat ? 'an array' : 'a string';
233
+ throw new TypeError(`Expected "${token.name}" to be ${typeOfMessage}`);
234
+ }
235
+ return path;
236
+ };
108
237
  }
109
238
  /**
110
- * Attach the keys as a property of the regexp.
111
- *
112
- * @param {!RegExp} re
113
- * @param {Array} keys
114
- * @return {!RegExp}
239
+ * Create path match function from `path-to-regexp` spec.
115
240
  */
116
- function attachKeys(re, keys) {
117
- re.keys = keys;
118
- return re;
241
+ function match(str, options) {
242
+ const keys = [];
243
+ const re = toRegexp(str, keys, options);
244
+ return regexpToFunction(re, keys, options);
245
+ }
246
+ /**
247
+ * Create a path match function from `path-to-regexp` output.
248
+ */
249
+ function regexpToFunction(re, keys, options = {}) {
250
+ const { decode = (x) => x } = options;
251
+ return function (pathname) {
252
+ const m = re.exec(pathname);
253
+ if (!m)
254
+ return false;
255
+ const { 0: path, index } = m;
256
+ const params = Object.create(null);
257
+ for (let i = 1; i < m.length; i++) {
258
+ if (m[i] === undefined)
259
+ continue;
260
+ const key = keys[i - 1];
261
+ if (key.modifier === '*' || key.modifier === '+') {
262
+ params[key.name] = m[i].split(key.prefix + key.suffix).map(value => {
263
+ return decode(value, key);
264
+ });
265
+ }
266
+ else {
267
+ params[key.name] = decode(m[i], key);
268
+ }
269
+ }
270
+ return { path, index, params };
271
+ };
272
+ }
273
+ /**
274
+ * Escape a regular expression string.
275
+ */
276
+ function escapeString(str) {
277
+ return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1');
119
278
  }
120
279
  /**
121
280
  * Get the flags for a regexp from the options.
122
- *
123
- * @param {Object} options
124
- * @return {string}
125
281
  */
126
282
  function flags(options) {
127
283
  return options && options.sensitive ? '' : 'i';
128
284
  }
129
285
  /**
130
286
  * Pull out keys from a regexp.
131
- *
132
- * @param {!RegExp} path
133
- * @param {!Array} keys
134
- * @return {!RegExp}
135
287
  */
136
288
  function regexpToRegexp(path, keys) {
137
- // Use a negative lookahead to match only capturing groups.
138
- const groups = path.source.match(/\((?!\?)/g);
139
- if (groups) {
140
- for (let i = 0; i < groups.length; i++) {
141
- keys.push({
142
- name: i,
143
- prefix: null,
144
- delimiter: null,
145
- optional: false,
146
- repeat: false,
147
- partial: false,
148
- asterisk: false,
149
- pattern: null,
150
- });
151
- }
289
+ if (!keys)
290
+ return path;
291
+ const groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g;
292
+ let index = 0;
293
+ let execResult = groupsRegex.exec(path.source);
294
+ while (execResult) {
295
+ keys.push({
296
+ // Use parenthesized substring match if available, index otherwise
297
+ name: execResult[1] || index++,
298
+ prefix: '',
299
+ suffix: '',
300
+ modifier: '',
301
+ pattern: '',
302
+ });
303
+ execResult = groupsRegex.exec(path.source);
152
304
  }
153
- return attachKeys(path, keys);
305
+ return path;
154
306
  }
155
307
  /**
156
308
  * Transform an array into a regexp.
157
- *
158
- * @param {!Array} path
159
- * @param {Array} keys
160
- * @param {!Object} options
161
- * @return {!RegExp}
162
309
  */
163
- function arrayToRegexp(path, keys, options) {
164
- const parts = [];
165
- for (let i = 0; i < path.length; i++) {
166
- parts.push(pathToRegexp(path[i], keys, options).source);
167
- }
168
- const regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
169
- return attachKeys(regexp, keys);
310
+ function arrayToRegexp(paths, keys, options) {
311
+ const parts = paths.map(path => toRegexp(path, keys, options).source);
312
+ return new RegExp(`(?:${parts.join('|')})`, flags(options));
170
313
  }
171
314
  /**
172
315
  * Create a path regexp from string input.
173
- *
174
- * @param {string} path
175
- * @param {!Array} keys
176
- * @param {!Object} options
177
- * @return {!RegExp}
178
316
  */
179
317
  function stringToRegexp(path, keys, options) {
180
- return tokensToRegExp(parse(path, options), keys, options);
318
+ return tokensToRegexp(parse(path, options), keys, options);
181
319
  }
182
320
  /**
183
321
  * Expose a function for taking tokens and returning a RegExp.
184
- *
185
- * @param {!Array} tokens
186
- * @param {(Array|Object)=} keys
187
- * @param {Object=} options
188
- * @return {!RegExp}
189
322
  */
190
- function tokensToRegExp(tokens, keys, options) {
191
- if (!Array.isArray(keys)) {
192
- options = /** @type {!Object} */ keys || options;
193
- keys = [];
194
- }
195
- options = options || {};
196
- const strict = options.strict;
197
- const end = options.end !== false;
198
- let route = '';
323
+ function tokensToRegexp(tokens, keys, options = {}) {
324
+ const { strict = false, start = true, end = true, encode = (x) => x, delimiter = '/#?', endsWith = '', } = options;
325
+ const endsWithRe = `[${escapeString(endsWith)}]|$`;
326
+ const delimiterRe = `[${escapeString(delimiter)}]`;
327
+ let route = start ? '^' : '';
199
328
  // Iterate over the tokens and create our regexp string.
200
- for (let i = 0; i < tokens.length; i++) {
201
- const token = tokens[i];
329
+ for (const token of tokens) {
202
330
  if (typeof token === 'string') {
203
- route += escapeString(token);
331
+ route += escapeString(encode(token));
204
332
  }
205
333
  else {
206
- const prefix = escapeString(token.prefix);
207
- let capture = '(?:' + token.pattern + ')';
208
- keys.push(token);
209
- if (token.repeat) {
210
- capture += '(?:' + prefix + capture + ')*';
211
- }
212
- if (token.optional) {
213
- if (!token.partial) {
214
- capture = '(?:' + prefix + '(' + capture + '))?';
334
+ const prefix = escapeString(encode(token.prefix));
335
+ const suffix = escapeString(encode(token.suffix));
336
+ if (token.pattern) {
337
+ if (keys)
338
+ keys.push(token);
339
+ if (prefix || suffix) {
340
+ if (token.modifier === '+' || token.modifier === '*') {
341
+ const mod = token.modifier === '*' ? '?' : '';
342
+ route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;
343
+ }
344
+ else {
345
+ route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;
346
+ }
215
347
  }
216
348
  else {
217
- capture = prefix + '(' + capture + ')?';
349
+ if (token.modifier === '+' || token.modifier === '*') {
350
+ route += `((?:${token.pattern})${token.modifier})`;
351
+ }
352
+ else {
353
+ route += `(${token.pattern})${token.modifier}`;
354
+ }
218
355
  }
219
356
  }
220
357
  else {
221
- capture = prefix + '(' + capture + ')';
358
+ route += `(?:${prefix}${suffix})${token.modifier}`;
222
359
  }
223
- route += capture;
224
360
  }
225
361
  }
226
- const delimiter = escapeString(options.delimiter || '/');
227
- const endsWithDelimiter = route.slice(-delimiter.length) === delimiter;
228
- // In non-strict mode we allow a slash at the end of match. If the path to
229
- // match already ends with a slash, we remove it for consistency. The slash
230
- // is valid at the end of a path match, not in the middle. This is important
231
- // in non-ending mode, where "/test/" shouldn't match "/test//route".
232
- if (!strict) {
233
- route =
234
- (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) +
235
- '(?:' +
236
- delimiter +
237
- '(?=$))?';
238
- }
239
362
  if (end) {
240
- route += '$';
363
+ if (!strict)
364
+ route += `${delimiterRe}?`;
365
+ route += !options.endsWith ? '$' : `(?=${endsWithRe})`;
241
366
  }
242
367
  else {
243
- // In non-ending mode, we need the capturing groups to match as much as
244
- // possible by using a positive lookahead to the end or next path segment.
245
- route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)';
368
+ const endToken = tokens[tokens.length - 1];
369
+ const isEndDelimited = typeof endToken === 'string'
370
+ ? delimiterRe.indexOf(endToken[endToken.length - 1]) > -1
371
+ : endToken === undefined;
372
+ if (!strict) {
373
+ route += `(?:${delimiterRe}(?=${endsWithRe}))?`;
374
+ }
375
+ if (!isEndDelimited) {
376
+ route += `(?=${delimiterRe}|${endsWithRe})`;
377
+ }
246
378
  }
247
- return attachKeys(new RegExp('^' + route, flags(options)), keys);
379
+ return new RegExp(route, flags(options));
248
380
  }
249
- function pathToRegexp(path, keys, options) {
250
- if (!Array.isArray(keys)) {
251
- options = /** @type {!Object} */ keys || options;
252
- keys = [];
253
- }
254
- options = options || {};
255
- if (path instanceof RegExp) {
256
- return regexpToRegexp(path, /** @type {!Array} */ keys);
257
- }
258
- if (Array.isArray(path)) {
259
- return arrayToRegexp(
260
- /** @type {!Array} */ path,
261
- /** @type {!Array} */ keys, options);
262
- }
263
- return stringToRegexp(
264
- /** @type {string} */ path,
265
- /** @type {!Array} */ keys, options);
381
+ /**
382
+ * Normalize the given path string, returning a regular expression.
383
+ *
384
+ * An empty array can be passed in for the keys, which will hold the
385
+ * placeholder key descriptions. For example, using `/user/:id`, `keys` will
386
+ * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
387
+ */
388
+ function toRegexp(path, keys, options) {
389
+ if (path instanceof RegExp)
390
+ return regexpToRegexp(path, keys);
391
+ if (Array.isArray(path))
392
+ return arrayToRegexp(path, keys, options);
393
+ return stringToRegexp(path, keys, options);
266
394
  }
267
- exports.pathToRegexp = pathToRegexp;
395
+ exports.PathToRegexpUtil = {
396
+ toRegexp,
397
+ compile,
398
+ parse,
399
+ match,
400
+ };
268
401
  //# sourceMappingURL=pathToRegexp.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midwayjs/core",
3
- "version": "3.4.0-beta.6",
3
+ "version": "3.4.0-beta.7",
4
4
  "description": "midway core",
5
5
  "main": "dist/index",
6
6
  "typings": "dist/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "license": "MIT",
23
23
  "devDependencies": {
24
- "@midwayjs/decorator": "^3.4.0-beta.6",
24
+ "@midwayjs/decorator": "^3.4.0-beta.7",
25
25
  "koa": "2.13.4",
26
26
  "midway-test-component": "*",
27
27
  "mm": "3.2.0",
@@ -45,5 +45,5 @@
45
45
  "engines": {
46
46
  "node": ">=12"
47
47
  },
48
- "gitHead": "8e187c4593d4832de32b6745af3e195f6b90816c"
48
+ "gitHead": "4d5cc59a7a33e49beeaf20fcfaf766438649959c"
49
49
  }