1 | const { parse: parseUrl, format: formatUrl } = require('url');
|
2 | const { pathToRegexp, compile, parse } = require('path-to-regexp');
|
3 |
|
4 | module.exports = Layer;
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | function Layer(path, methods, middleware, opts = {}) {
|
21 | this.opts = opts;
|
22 | this.name = this.opts.name || null;
|
23 | this.methods = [];
|
24 | this.paramNames = [];
|
25 | this.stack = Array.isArray(middleware) ? middleware : [middleware];
|
26 |
|
27 | for (const method of methods) {
|
28 | const l = this.methods.push(method.toUpperCase());
|
29 | if (this.methods[l - 1] === 'GET') this.methods.unshift('HEAD');
|
30 | }
|
31 |
|
32 |
|
33 | for (let i = 0; i < this.stack.length; i++) {
|
34 | const fn = this.stack[i];
|
35 | const type = typeof fn;
|
36 | if (type !== 'function')
|
37 | throw new Error(
|
38 | `${methods.toString()} \`${
|
39 | this.opts.name || path
|
40 | }\`: \`middleware\` must be a function, not \`${type}\``
|
41 | );
|
42 | }
|
43 |
|
44 | this.path = path;
|
45 | this.regexp = pathToRegexp(path, this.paramNames, this.opts);
|
46 | }
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | Layer.prototype.match = function (path) {
|
57 | return this.regexp.test(path);
|
58 | };
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | Layer.prototype.params = function (path, captures, params = {}) {
|
71 | for (let len = captures.length, i = 0; i < len; i++) {
|
72 | if (this.paramNames[i]) {
|
73 | const c = captures[i];
|
74 | if (c && c.length > 0)
|
75 | params[this.paramNames[i].name] = c ? safeDecodeURIComponent(c) : c;
|
76 | }
|
77 | }
|
78 |
|
79 | return params;
|
80 | };
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | Layer.prototype.captures = function (path) {
|
91 | return this.opts.ignoreCaptures ? [] : path.match(this.regexp).slice(1);
|
92 | };
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | Layer.prototype.url = function (params, options) {
|
111 | let args = params;
|
112 | const url = this.path.replace(/\(\.\*\)/g, '');
|
113 |
|
114 | if (typeof params !== 'object') {
|
115 | args = Array.prototype.slice.call(arguments);
|
116 | if (typeof args[args.length - 1] === 'object') {
|
117 | options = args[args.length - 1];
|
118 | args = args.slice(0, -1);
|
119 | }
|
120 | }
|
121 |
|
122 | const toPath = compile(url, { encode: encodeURIComponent, ...options });
|
123 | let replaced;
|
124 |
|
125 | const tokens = parse(url);
|
126 | let replace = {};
|
127 |
|
128 | if (Array.isArray(args)) {
|
129 | for (let len = tokens.length, i = 0, j = 0; i < len; i++) {
|
130 | if (tokens[i].name) replace[tokens[i].name] = args[j++];
|
131 | }
|
132 | } else if (tokens.some((token) => token.name)) {
|
133 | replace = params;
|
134 | } else if (!options) {
|
135 | options = params;
|
136 | }
|
137 |
|
138 | replaced = toPath(replace);
|
139 |
|
140 | if (options && options.query) {
|
141 | replaced = parseUrl(replaced);
|
142 | if (typeof options.query === 'string') {
|
143 | replaced.search = options.query;
|
144 | } else {
|
145 | replaced.search = undefined;
|
146 | replaced.query = options.query;
|
147 | }
|
148 |
|
149 | return formatUrl(replaced);
|
150 | }
|
151 |
|
152 | return replaced;
|
153 | };
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 | Layer.prototype.param = function (param, fn) {
|
179 | const { stack } = this;
|
180 | const params = this.paramNames;
|
181 | const middleware = function (ctx, next) {
|
182 | return fn.call(this, ctx.params[param], ctx, next);
|
183 | };
|
184 |
|
185 | middleware.param = param;
|
186 |
|
187 | const names = params.map(function (p) {
|
188 | return p.name;
|
189 | });
|
190 |
|
191 | const x = names.indexOf(param);
|
192 | if (x > -1) {
|
193 |
|
194 | stack.some(function (fn, i) {
|
195 |
|
196 |
|
197 | if (!fn.param || names.indexOf(fn.param) > x) {
|
198 |
|
199 | stack.splice(i, 0, middleware);
|
200 | return true;
|
201 | }
|
202 | });
|
203 | }
|
204 |
|
205 | return this;
|
206 | };
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 | Layer.prototype.setPrefix = function (prefix) {
|
217 | if (this.path) {
|
218 | this.path =
|
219 | this.path !== '/' || this.opts.strict === true
|
220 | ? `${prefix}${this.path}`
|
221 | : prefix;
|
222 | this.paramNames = [];
|
223 | this.regexp = pathToRegexp(this.path, this.paramNames, this.opts);
|
224 | }
|
225 |
|
226 | return this;
|
227 | };
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | function safeDecodeURIComponent(text) {
|
239 | try {
|
240 | return decodeURIComponent(text);
|
241 | } catch {
|
242 | return text;
|
243 | }
|
244 | }
|