js-routes 2.3.5 → 2.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/Readme.md +7 -9
- data/lib/js_routes/instance.rb +7 -1
- data/lib/js_routes/route.rb +1 -1
- data/lib/js_routes/version.rb +1 -1
- data/lib/routes.d.ts +22 -21
- data/lib/routes.js +25 -36
- data/lib/routes.ts +601 -617
- metadata +4 -4
data/lib/routes.ts
CHANGED
|
@@ -25,17 +25,17 @@ type RouteHelperExtras = {
|
|
|
25
25
|
type RequiredParameters<T extends number> = T extends 1
|
|
26
26
|
? [RequiredRouteParameter]
|
|
27
27
|
: T extends 2
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
? [RequiredRouteParameter, RequiredRouteParameter]
|
|
29
|
+
: T extends 3
|
|
30
|
+
? [RequiredRouteParameter, RequiredRouteParameter, RequiredRouteParameter]
|
|
31
|
+
: T extends 4
|
|
32
|
+
? [
|
|
33
|
+
RequiredRouteParameter,
|
|
34
|
+
RequiredRouteParameter,
|
|
35
|
+
RequiredRouteParameter,
|
|
36
|
+
RequiredRouteParameter,
|
|
37
|
+
]
|
|
38
|
+
: RequiredRouteParameter[];
|
|
39
39
|
|
|
40
40
|
type RouteHelperOptions = RouteOptions & Collection<OptionalRouteParameter>;
|
|
41
41
|
|
|
@@ -79,6 +79,7 @@ type ModuleType = "CJS" | "AMD" | "UMD" | "ESM" | "DTS" | "NIL";
|
|
|
79
79
|
declare const RubyVariables: {
|
|
80
80
|
PREFIX: string;
|
|
81
81
|
DEPRECATED_FALSE_PARAMETER_BEHAVIOR: boolean;
|
|
82
|
+
DEPRECATED_NIL_QUERY_PARAMETER_BEHAVIOR: boolean;
|
|
82
83
|
SPECIAL_OPTIONS_KEY: string;
|
|
83
84
|
DEFAULT_URL_OPTIONS: RouteParameters;
|
|
84
85
|
SERIALIZER: Serializer;
|
|
@@ -93,679 +94,662 @@ declare const define:
|
|
|
93
94
|
|
|
94
95
|
declare const module: { exports: unknown } | undefined;
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return typeof module === "object";
|
|
154
|
-
},
|
|
97
|
+
RubyVariables.WRAPPER((): RouterExposedMethods => {
|
|
98
|
+
const hasProp = (value: unknown, key: string): boolean =>
|
|
99
|
+
Object.prototype.hasOwnProperty.call(value, key);
|
|
100
|
+
enum NodeTypes {
|
|
101
|
+
GROUP = 1,
|
|
102
|
+
CAT = 2,
|
|
103
|
+
SYMBOL = 3,
|
|
104
|
+
OR = 4,
|
|
105
|
+
STAR = 5,
|
|
106
|
+
LITERAL = 6,
|
|
107
|
+
SLASH = 7,
|
|
108
|
+
DOT = 8,
|
|
109
|
+
}
|
|
110
|
+
type RouteNodes = {
|
|
111
|
+
[NodeTypes.GROUP]: { left: RouteTree; right: never };
|
|
112
|
+
[NodeTypes.STAR]: { left: RouteTree; right: never };
|
|
113
|
+
[NodeTypes.LITERAL]: { left: string; right: never };
|
|
114
|
+
[NodeTypes.SLASH]: { left: "/"; right: never };
|
|
115
|
+
[NodeTypes.DOT]: { left: "."; right: never };
|
|
116
|
+
[NodeTypes.CAT]: { left: RouteTree; right: RouteTree };
|
|
117
|
+
[NodeTypes.SYMBOL]: { left: string; right: never };
|
|
118
|
+
};
|
|
119
|
+
type RouteNode<T extends keyof RouteNodes> = [
|
|
120
|
+
T,
|
|
121
|
+
RouteNodes[T]["left"],
|
|
122
|
+
RouteNodes[T]["right"],
|
|
123
|
+
];
|
|
124
|
+
type RouteTree = {
|
|
125
|
+
[T in keyof RouteNodes]: RouteNode<T>;
|
|
126
|
+
}[keyof RouteNodes];
|
|
127
|
+
|
|
128
|
+
const isBrowser = typeof window !== "undefined";
|
|
129
|
+
type ModuleDefinition = {
|
|
130
|
+
define: (routes: RouterExposedMethods) => void;
|
|
131
|
+
isSupported: () => boolean;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const UnescapedSpecials = "-._~!$&'()*+,;=:@"
|
|
135
|
+
.split("")
|
|
136
|
+
.map((s) => s.charCodeAt(0));
|
|
137
|
+
const UnescapedRanges = [
|
|
138
|
+
["a", "z"],
|
|
139
|
+
["A", "Z"],
|
|
140
|
+
["0", "9"],
|
|
141
|
+
].map((range) => range.map((s) => s.charCodeAt(0)));
|
|
142
|
+
|
|
143
|
+
const ModuleReferences: Record<ModuleType, ModuleDefinition> = {
|
|
144
|
+
CJS: {
|
|
145
|
+
define(routes) {
|
|
146
|
+
if (module) {
|
|
147
|
+
// Some javascript processors (like vite/rolldown)
|
|
148
|
+
// warn on using module dot exports in an ESM module.
|
|
149
|
+
// This just obfuscates that assignment a little so
|
|
150
|
+
// users don't get a warning they can't fix.
|
|
151
|
+
const _mod = module;
|
|
152
|
+
_mod.exports = routes;
|
|
153
|
+
}
|
|
155
154
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (define) {
|
|
159
|
-
define([], function () {
|
|
160
|
-
return routes;
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
isSupported() {
|
|
165
|
-
return typeof define === "function" && !!define.amd;
|
|
166
|
-
},
|
|
155
|
+
isSupported() {
|
|
156
|
+
return typeof module === "object";
|
|
167
157
|
},
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
158
|
+
},
|
|
159
|
+
AMD: {
|
|
160
|
+
define(routes) {
|
|
161
|
+
if (define) {
|
|
162
|
+
define([], function () {
|
|
163
|
+
return routes;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
isSupported() {
|
|
168
|
+
return typeof define === "function" && !!define.amd;
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
UMD: {
|
|
172
|
+
define(routes) {
|
|
173
|
+
if (ModuleReferences.AMD.isSupported()) {
|
|
174
|
+
ModuleReferences.AMD.define(routes);
|
|
175
|
+
} else {
|
|
176
|
+
if (ModuleReferences.CJS.isSupported()) {
|
|
177
|
+
try {
|
|
178
|
+
ModuleReferences.CJS.define(routes);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if ((error as Error).name !== "TypeError") throw error;
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
isSupported() {
|
|
186
|
+
return (
|
|
187
|
+
ModuleReferences.AMD.isSupported() ||
|
|
188
|
+
ModuleReferences.CJS.isSupported()
|
|
189
|
+
);
|
|
188
190
|
},
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
isSupported() {
|
|
194
|
-
// Its impossible to check if "export" keyword is supported
|
|
195
|
-
return true;
|
|
196
|
-
},
|
|
191
|
+
},
|
|
192
|
+
ESM: {
|
|
193
|
+
define() {
|
|
194
|
+
// Module can only be defined using ruby code generation
|
|
197
195
|
},
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
},
|
|
202
|
-
isSupported() {
|
|
203
|
-
return true;
|
|
204
|
-
},
|
|
196
|
+
isSupported() {
|
|
197
|
+
// Its impossible to check if "export" keyword is supported
|
|
198
|
+
return true;
|
|
205
199
|
},
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
},
|
|
211
|
-
isSupported() {
|
|
212
|
-
return ModuleReferences.ESM.isSupported();
|
|
213
|
-
},
|
|
200
|
+
},
|
|
201
|
+
NIL: {
|
|
202
|
+
define() {
|
|
203
|
+
// Defined using RubyVariables . WRAPPER
|
|
214
204
|
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
205
|
+
isSupported() {
|
|
206
|
+
return true;
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
DTS: {
|
|
210
|
+
// Acts the same as ESM
|
|
211
|
+
define(routes) {
|
|
212
|
+
ModuleReferences.ESM.define(routes);
|
|
213
|
+
},
|
|
214
|
+
isSupported() {
|
|
215
|
+
return ModuleReferences.ESM.isSupported();
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
class ParametersMissing extends Error {
|
|
221
|
+
readonly keys: string[];
|
|
222
|
+
constructor(...keys: string[]) {
|
|
223
|
+
super(`Route missing required keys: ${keys.join(", ")}`);
|
|
224
|
+
this.keys = keys;
|
|
225
|
+
Object.setPrototypeOf(this, Object.getPrototypeOf(this));
|
|
226
|
+
this.name = ParametersMissing.name;
|
|
225
227
|
}
|
|
228
|
+
}
|
|
226
229
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
230
|
+
const ReservedOptions = [
|
|
231
|
+
"anchor",
|
|
232
|
+
"trailing_slash",
|
|
233
|
+
"subdomain",
|
|
234
|
+
"host",
|
|
235
|
+
"port",
|
|
236
|
+
"protocol",
|
|
237
|
+
"script_name",
|
|
238
|
+
] as const;
|
|
239
|
+
|
|
240
|
+
type ReservedOption = (typeof ReservedOptions)[any];
|
|
241
|
+
|
|
242
|
+
class UtilsClass {
|
|
243
|
+
configuration: Configuration = {
|
|
244
|
+
prefix: RubyVariables.PREFIX,
|
|
245
|
+
default_url_options: RubyVariables.DEFAULT_URL_OPTIONS,
|
|
246
|
+
special_options_key: RubyVariables.SPECIAL_OPTIONS_KEY,
|
|
247
|
+
serializer:
|
|
248
|
+
RubyVariables.SERIALIZER || this.default_serializer.bind(this),
|
|
249
|
+
};
|
|
247
250
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
default_serializer(value: unknown, prefix?: string | null): string {
|
|
252
|
+
if (!prefix && !this.is_object(value)) {
|
|
253
|
+
throw new Error("Url parameters should be a javascript hash");
|
|
254
|
+
}
|
|
255
|
+
prefix = prefix || "";
|
|
256
|
+
const result: string[] = [];
|
|
257
|
+
if (this.is_array(value)) {
|
|
258
|
+
for (const element of value) {
|
|
259
|
+
result.push(this.default_serializer(element, prefix + "[]"));
|
|
254
260
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
} else if (this.is_object(value)) {
|
|
262
|
-
for (let key in value) {
|
|
263
|
-
if (!hasProp(value, key)) continue;
|
|
264
|
-
let prop = value[key];
|
|
265
|
-
if (this.is_nullable(prop) && prefix) {
|
|
266
|
-
prop = "";
|
|
267
|
-
}
|
|
268
|
-
if (this.is_not_nullable(prop)) {
|
|
269
|
-
if (prefix) {
|
|
270
|
-
key = prefix + "[" + key + "]";
|
|
271
|
-
}
|
|
272
|
-
result.push(this.default_serializer(prop, key));
|
|
273
|
-
}
|
|
261
|
+
} else if (this.is_object(value)) {
|
|
262
|
+
for (let key in value) {
|
|
263
|
+
if (!hasProp(value, key)) continue;
|
|
264
|
+
let prop = value[key];
|
|
265
|
+
if (prefix) {
|
|
266
|
+
key = prefix + "[" + key + "]";
|
|
274
267
|
}
|
|
275
|
-
|
|
276
|
-
if (
|
|
277
|
-
result.push(
|
|
278
|
-
encodeURIComponent(prefix) + "=" + encodeURIComponent("" + value)
|
|
279
|
-
);
|
|
268
|
+
const subvalue = this.default_serializer(prop, key);
|
|
269
|
+
if (subvalue.length) {
|
|
270
|
+
result.push(subvalue);
|
|
280
271
|
}
|
|
281
272
|
}
|
|
282
|
-
|
|
273
|
+
} else {
|
|
274
|
+
const key = encodeURIComponent(prefix);
|
|
275
|
+
result.push(
|
|
276
|
+
this.is_not_nullable(value) ||
|
|
277
|
+
RubyVariables.DEPRECATED_NIL_QUERY_PARAMETER_BEHAVIOR
|
|
278
|
+
? key + "=" + encodeURIComponent("" + (value ?? ""))
|
|
279
|
+
: key,
|
|
280
|
+
);
|
|
283
281
|
}
|
|
282
|
+
return result.join("&");
|
|
283
|
+
}
|
|
284
284
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
285
|
+
serialize(object: Serializable): string {
|
|
286
|
+
return this.configuration.serializer(object);
|
|
287
|
+
}
|
|
288
288
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
) {
|
|
302
|
-
|
|
303
|
-
delete last_el[this.configuration.special_options_key];
|
|
304
|
-
}
|
|
305
|
-
return {
|
|
306
|
-
args: args.slice(0, args.length - 1),
|
|
307
|
-
options: last_el as unknown as RouteOptions,
|
|
308
|
-
};
|
|
309
|
-
} else {
|
|
310
|
-
return { args, options: {} };
|
|
289
|
+
extract_options(
|
|
290
|
+
number_of_params: number,
|
|
291
|
+
args: OptionalRouteParameter[],
|
|
292
|
+
): {
|
|
293
|
+
args: OptionalRouteParameter[];
|
|
294
|
+
options: RouteOptions;
|
|
295
|
+
} {
|
|
296
|
+
const last_el = args[args.length - 1];
|
|
297
|
+
if (
|
|
298
|
+
(args.length > number_of_params && last_el === 0) ||
|
|
299
|
+
(this.is_object(last_el) && !this.looks_like_serialized_model(last_el))
|
|
300
|
+
) {
|
|
301
|
+
if (this.is_object(last_el)) {
|
|
302
|
+
delete last_el[this.configuration.special_options_key];
|
|
311
303
|
}
|
|
304
|
+
return {
|
|
305
|
+
args: args.slice(0, args.length - 1),
|
|
306
|
+
options: last_el as unknown as RouteOptions,
|
|
307
|
+
};
|
|
308
|
+
} else {
|
|
309
|
+
return { args, options: {} };
|
|
312
310
|
}
|
|
311
|
+
}
|
|
313
312
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
313
|
+
looks_like_serialized_model(
|
|
314
|
+
object: unknown,
|
|
315
|
+
): object is ModelRouteParameter {
|
|
316
|
+
return (
|
|
317
|
+
this.is_object(object) &&
|
|
318
|
+
!(this.configuration.special_options_key in object) &&
|
|
319
|
+
("id" in object || "to_param" in object || "toParam" in object)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
323
322
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
323
|
+
path_identifier(object: QueryRouteParameter): string {
|
|
324
|
+
const result = this.unwrap_path_identifier(object);
|
|
325
|
+
return this.is_nullable(result) ||
|
|
326
|
+
(RubyVariables.DEPRECATED_FALSE_PARAMETER_BEHAVIOR && result === false)
|
|
327
|
+
? ""
|
|
328
|
+
: "" + result;
|
|
329
|
+
}
|
|
332
330
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
331
|
+
unwrap_path_identifier(object: QueryRouteParameter): unknown {
|
|
332
|
+
let result: unknown = object;
|
|
333
|
+
if (!this.is_object(object)) {
|
|
334
|
+
return object;
|
|
335
|
+
}
|
|
336
|
+
if ("to_param" in object) {
|
|
337
|
+
result = object.to_param;
|
|
338
|
+
} else if ("toParam" in object) {
|
|
339
|
+
result = object.toParam;
|
|
340
|
+
} else if ("id" in object) {
|
|
341
|
+
result = object.id;
|
|
342
|
+
} else {
|
|
343
|
+
result = object;
|
|
344
|
+
}
|
|
345
|
+
return this.is_callable(result) ? result.call(object) : result;
|
|
346
|
+
}
|
|
349
347
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
parts_options[key] = value;
|
|
377
|
-
}
|
|
348
|
+
partition_parameters(
|
|
349
|
+
parts: string[],
|
|
350
|
+
required_params: string[],
|
|
351
|
+
default_options: RouteParameters,
|
|
352
|
+
call_arguments: OptionalRouteParameter[],
|
|
353
|
+
): {
|
|
354
|
+
keyword_parameters: KeywordUrlOptions;
|
|
355
|
+
query_parameters: RouteParameters;
|
|
356
|
+
} {
|
|
357
|
+
let { args, options } = this.extract_options(
|
|
358
|
+
parts.length,
|
|
359
|
+
call_arguments,
|
|
360
|
+
);
|
|
361
|
+
if (args.length > parts.length) {
|
|
362
|
+
throw new Error("Too many parameters provided for path");
|
|
363
|
+
}
|
|
364
|
+
let use_all_parts = args.length > required_params.length;
|
|
365
|
+
const parts_options: RouteParameters = {
|
|
366
|
+
...this.configuration.default_url_options,
|
|
367
|
+
};
|
|
368
|
+
for (const key in options) {
|
|
369
|
+
const value = options[key];
|
|
370
|
+
if (!hasProp(options, key)) continue;
|
|
371
|
+
use_all_parts = true;
|
|
372
|
+
if (parts.includes(key)) {
|
|
373
|
+
parts_options[key] = value;
|
|
378
374
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
375
|
+
}
|
|
376
|
+
options = {
|
|
377
|
+
...this.configuration.default_url_options,
|
|
378
|
+
...default_options,
|
|
379
|
+
...options,
|
|
380
|
+
};
|
|
384
381
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
} else {
|
|
397
|
-
throw new Error("params value should always be an object");
|
|
398
|
-
}
|
|
399
|
-
} else if (this.is_reserved_option(key)) {
|
|
400
|
-
keyword_parameters[key] = value as any;
|
|
382
|
+
const keyword_parameters: KeywordUrlOptions = {};
|
|
383
|
+
let query_parameters: RouteParameters = {};
|
|
384
|
+
for (const key in options) {
|
|
385
|
+
if (!hasProp(options, key)) continue;
|
|
386
|
+
const value = options[key];
|
|
387
|
+
if (key === "params") {
|
|
388
|
+
if (this.is_object(value)) {
|
|
389
|
+
query_parameters = {
|
|
390
|
+
...query_parameters,
|
|
391
|
+
...(value as RouteParameters),
|
|
392
|
+
};
|
|
401
393
|
} else {
|
|
402
|
-
|
|
403
|
-
!this.is_nullable(value) &&
|
|
404
|
-
(value !== default_options[key] || required_params.includes(key))
|
|
405
|
-
) {
|
|
406
|
-
query_parameters[key] = value;
|
|
407
|
-
}
|
|
394
|
+
throw new Error("params value should always be an object");
|
|
408
395
|
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
++i;
|
|
418
|
-
}
|
|
396
|
+
} else if (this.is_reserved_option(key)) {
|
|
397
|
+
keyword_parameters[key] = value as any;
|
|
398
|
+
} else {
|
|
399
|
+
if (
|
|
400
|
+
!this.is_nullable(value) &&
|
|
401
|
+
(value !== default_options[key] || required_params.includes(key))
|
|
402
|
+
) {
|
|
403
|
+
query_parameters[key] = value;
|
|
419
404
|
}
|
|
420
405
|
}
|
|
421
|
-
return { keyword_parameters, query_parameters };
|
|
422
406
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
): string {
|
|
432
|
-
const { keyword_parameters, query_parameters } =
|
|
433
|
-
this.partition_parameters(
|
|
434
|
-
parts,
|
|
435
|
-
required_params,
|
|
436
|
-
default_options,
|
|
437
|
-
args
|
|
438
|
-
);
|
|
439
|
-
|
|
440
|
-
let { trailing_slash, anchor, script_name } = keyword_parameters;
|
|
441
|
-
const missing_params = required_params.filter(
|
|
442
|
-
(param) =>
|
|
443
|
-
!hasProp(query_parameters, param) ||
|
|
444
|
-
this.is_nullable(query_parameters[param])
|
|
445
|
-
);
|
|
446
|
-
if (missing_params.length) {
|
|
447
|
-
throw new ParametersMissing(...missing_params);
|
|
448
|
-
}
|
|
449
|
-
let result = this.get_prefix() + this.visit(route, query_parameters);
|
|
450
|
-
if (trailing_slash) {
|
|
451
|
-
result = result.replace(/(.*?)[/]?$/, "$1/");
|
|
452
|
-
}
|
|
453
|
-
const url_params = this.serialize(query_parameters);
|
|
454
|
-
if (url_params.length) {
|
|
455
|
-
result += "?" + url_params;
|
|
456
|
-
}
|
|
457
|
-
if (anchor) {
|
|
458
|
-
result += "#" + anchor;
|
|
459
|
-
}
|
|
460
|
-
if (script_name) {
|
|
461
|
-
const last_index = script_name.length - 1;
|
|
462
|
-
if (script_name[last_index] == "/" && result[0] == "/") {
|
|
463
|
-
script_name = script_name.slice(0, last_index);
|
|
407
|
+
const route_parts = use_all_parts ? parts : required_params;
|
|
408
|
+
let i = 0;
|
|
409
|
+
for (const part of route_parts) {
|
|
410
|
+
if (i < args.length) {
|
|
411
|
+
const value = args[i];
|
|
412
|
+
if (!hasProp(parts_options, part)) {
|
|
413
|
+
query_parameters[part] = value;
|
|
414
|
+
++i;
|
|
464
415
|
}
|
|
465
|
-
result = script_name + result;
|
|
466
|
-
}
|
|
467
|
-
if (absolute) {
|
|
468
|
-
result = this.route_url(keyword_parameters) + result;
|
|
469
|
-
}
|
|
470
|
-
return result;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
visit(
|
|
474
|
-
route: RouteTree,
|
|
475
|
-
parameters: RouteParameters,
|
|
476
|
-
optional = false
|
|
477
|
-
): string {
|
|
478
|
-
switch (route[0]) {
|
|
479
|
-
case NodeTypes.GROUP:
|
|
480
|
-
return this.visit(route[1], parameters, true);
|
|
481
|
-
case NodeTypes.CAT:
|
|
482
|
-
return this.visit_cat(route, parameters, optional);
|
|
483
|
-
case NodeTypes.SYMBOL:
|
|
484
|
-
return this.visit_symbol(route, parameters, optional);
|
|
485
|
-
case NodeTypes.STAR:
|
|
486
|
-
return this.visit_globbing(route[1], parameters, true);
|
|
487
|
-
case NodeTypes.LITERAL:
|
|
488
|
-
case NodeTypes.SLASH:
|
|
489
|
-
case NodeTypes.DOT:
|
|
490
|
-
return route[1];
|
|
491
|
-
default:
|
|
492
|
-
throw new Error("Unknown Rails node type");
|
|
493
416
|
}
|
|
494
417
|
}
|
|
418
|
+
return { keyword_parameters, query_parameters };
|
|
419
|
+
}
|
|
495
420
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
421
|
+
build_route(
|
|
422
|
+
parts: string[],
|
|
423
|
+
required_params: string[],
|
|
424
|
+
default_options: RouteParameters,
|
|
425
|
+
route: RouteTree,
|
|
426
|
+
absolute: boolean,
|
|
427
|
+
args: OptionalRouteParameter[],
|
|
428
|
+
): string {
|
|
429
|
+
const { keyword_parameters, query_parameters } =
|
|
430
|
+
this.partition_parameters(
|
|
431
|
+
parts,
|
|
432
|
+
required_params,
|
|
433
|
+
default_options,
|
|
434
|
+
args,
|
|
435
|
+
);
|
|
503
436
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
437
|
+
let { trailing_slash, anchor, script_name } = keyword_parameters;
|
|
438
|
+
const missing_params = required_params.filter(
|
|
439
|
+
(param) =>
|
|
440
|
+
!hasProp(query_parameters, param) ||
|
|
441
|
+
this.is_nullable(query_parameters[param]),
|
|
442
|
+
);
|
|
443
|
+
if (missing_params.length) {
|
|
444
|
+
throw new ParametersMissing(...missing_params);
|
|
445
|
+
}
|
|
446
|
+
let result = this.get_prefix() + this.visit(route, query_parameters);
|
|
447
|
+
if (trailing_slash) {
|
|
448
|
+
result = result.replace(/(.*?)[/]?$/, "$1/");
|
|
449
|
+
}
|
|
450
|
+
const url_params = this.serialize(query_parameters);
|
|
451
|
+
if (url_params.length) {
|
|
452
|
+
result += "?" + url_params;
|
|
453
|
+
}
|
|
454
|
+
if (anchor) {
|
|
455
|
+
result += "#" + anchor;
|
|
456
|
+
}
|
|
457
|
+
if (script_name) {
|
|
458
|
+
const last_index = script_name.length - 1;
|
|
459
|
+
if (script_name[last_index] == "/" && result[0] == "/") {
|
|
460
|
+
script_name = script_name.slice(0, last_index);
|
|
524
461
|
}
|
|
525
|
-
|
|
462
|
+
result = script_name + result;
|
|
526
463
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
530
|
-
[_type, key]: RouteNode<NodeTypes.SYMBOL>,
|
|
531
|
-
parameters: RouteParameters,
|
|
532
|
-
optional: boolean
|
|
533
|
-
): string {
|
|
534
|
-
const value = this.path_identifier(parameters[key]);
|
|
535
|
-
delete parameters[key];
|
|
536
|
-
if (value.length) {
|
|
537
|
-
return this.encode_segment(value);
|
|
538
|
-
}
|
|
539
|
-
if (optional) {
|
|
540
|
-
return "";
|
|
541
|
-
} else {
|
|
542
|
-
throw new ParametersMissing(key);
|
|
543
|
-
}
|
|
464
|
+
if (absolute) {
|
|
465
|
+
result = this.route_url(keyword_parameters) + result;
|
|
544
466
|
}
|
|
467
|
+
return result;
|
|
468
|
+
}
|
|
545
469
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
470
|
+
visit(
|
|
471
|
+
route: RouteTree,
|
|
472
|
+
parameters: RouteParameters,
|
|
473
|
+
optional = false,
|
|
474
|
+
): string {
|
|
475
|
+
switch (route[0]) {
|
|
476
|
+
case NodeTypes.GROUP:
|
|
477
|
+
return this.visit(route[1], parameters, true);
|
|
478
|
+
case NodeTypes.CAT:
|
|
479
|
+
return this.visit_cat(route, parameters, optional);
|
|
480
|
+
case NodeTypes.SYMBOL:
|
|
481
|
+
return this.visit_symbol(route, parameters, optional);
|
|
482
|
+
case NodeTypes.STAR:
|
|
483
|
+
return this.visit_globbing(route[1], parameters, true);
|
|
484
|
+
case NodeTypes.LITERAL:
|
|
485
|
+
case NodeTypes.SLASH:
|
|
486
|
+
case NodeTypes.DOT:
|
|
487
|
+
return route[1];
|
|
488
|
+
default:
|
|
489
|
+
throw new Error("Unknown Rails node type");
|
|
566
490
|
}
|
|
491
|
+
}
|
|
567
492
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
493
|
+
is_not_nullable<T>(object: T): object is NonNullable<T> {
|
|
494
|
+
return !this.is_nullable(object);
|
|
495
|
+
}
|
|
571
496
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
case NodeTypes.GROUP:
|
|
576
|
-
return `(${this.build_path_spec(route[1])})`;
|
|
577
|
-
case NodeTypes.CAT:
|
|
578
|
-
return (
|
|
579
|
-
this.build_path_spec(route[1]) + this.build_path_spec(route[2])
|
|
580
|
-
);
|
|
581
|
-
case NodeTypes.STAR:
|
|
582
|
-
return this.build_path_spec(route[1], true);
|
|
583
|
-
case NodeTypes.SYMBOL:
|
|
584
|
-
key = route[1];
|
|
585
|
-
if (wildcard) {
|
|
586
|
-
return (key.startsWith("*") ? "" : "*") + key;
|
|
587
|
-
} else {
|
|
588
|
-
return ":" + key;
|
|
589
|
-
}
|
|
590
|
-
break;
|
|
591
|
-
case NodeTypes.SLASH:
|
|
592
|
-
case NodeTypes.DOT:
|
|
593
|
-
case NodeTypes.LITERAL:
|
|
594
|
-
return route[1];
|
|
595
|
-
default:
|
|
596
|
-
throw new Error("Unknown Rails node type");
|
|
597
|
-
}
|
|
598
|
-
}
|
|
497
|
+
is_nullable(object: unknown): object is null | undefined {
|
|
498
|
+
return object === undefined || object === null;
|
|
499
|
+
}
|
|
599
500
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
501
|
+
visit_cat(
|
|
502
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
503
|
+
[_type, left, right]: RouteNode<NodeTypes.CAT>,
|
|
504
|
+
parameters: RouteParameters,
|
|
505
|
+
optional: boolean,
|
|
506
|
+
): string {
|
|
507
|
+
const left_part = this.visit(left, parameters, optional);
|
|
508
|
+
let right_part = this.visit(right, parameters, optional);
|
|
509
|
+
if (
|
|
510
|
+
optional &&
|
|
511
|
+
((this.is_optional_node(left[0]) && !left_part) ||
|
|
512
|
+
(this.is_optional_node(right[0]) && !right_part))
|
|
513
|
+
) {
|
|
514
|
+
return "";
|
|
515
|
+
}
|
|
516
|
+
// if left_part ends on '/' and right_part starts on '/'
|
|
517
|
+
if (left_part[left_part.length - 1] === "/" && right_part[0] === "/") {
|
|
518
|
+
// strip slash from right_part
|
|
519
|
+
// to prevent double slash
|
|
520
|
+
right_part = right_part.substring(1);
|
|
521
|
+
}
|
|
522
|
+
return left_part + right_part;
|
|
523
|
+
}
|
|
617
524
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
525
|
+
visit_symbol(
|
|
526
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
527
|
+
[_type, key]: RouteNode<NodeTypes.SYMBOL>,
|
|
528
|
+
parameters: RouteParameters,
|
|
529
|
+
optional: boolean,
|
|
530
|
+
): string {
|
|
531
|
+
const value = this.path_identifier(parameters[key]);
|
|
532
|
+
delete parameters[key];
|
|
533
|
+
if (value.length) {
|
|
534
|
+
return this.encode_segment(value);
|
|
535
|
+
}
|
|
536
|
+
if (optional) {
|
|
537
|
+
return "";
|
|
538
|
+
} else {
|
|
539
|
+
throw new ParametersMissing(key);
|
|
623
540
|
}
|
|
541
|
+
}
|
|
624
542
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
543
|
+
encode_segment(segment: string): string {
|
|
544
|
+
if (segment.match(/^[a-zA-Z0-9-]$/)) {
|
|
545
|
+
// Performance optimization for 99% of cases
|
|
546
|
+
return segment;
|
|
547
|
+
}
|
|
548
|
+
return (segment.match(/./gu) || [])
|
|
549
|
+
.map((ch) => {
|
|
550
|
+
const code = ch.charCodeAt(0);
|
|
551
|
+
if (
|
|
552
|
+
UnescapedRanges.find(
|
|
553
|
+
(range) => code >= range[0] && code <= range[1],
|
|
554
|
+
) ||
|
|
555
|
+
UnescapedSpecials.includes(code)
|
|
556
|
+
) {
|
|
557
|
+
return ch;
|
|
558
|
+
} else {
|
|
559
|
+
return encodeURIComponent(ch);
|
|
642
560
|
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
561
|
+
})
|
|
562
|
+
.join("");
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
is_optional_node(node: NodeTypes): boolean {
|
|
566
|
+
return [NodeTypes.STAR, NodeTypes.SYMBOL, NodeTypes.CAT].includes(node);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
build_path_spec(route: RouteTree, wildcard = false): string {
|
|
570
|
+
let key: string;
|
|
571
|
+
switch (route[0]) {
|
|
572
|
+
case NodeTypes.GROUP:
|
|
573
|
+
return `(${this.build_path_spec(route[1])})`;
|
|
574
|
+
case NodeTypes.CAT:
|
|
575
|
+
return (
|
|
576
|
+
this.build_path_spec(route[1]) + this.build_path_spec(route[2])
|
|
652
577
|
);
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
578
|
+
case NodeTypes.STAR:
|
|
579
|
+
return this.build_path_spec(route[1], true);
|
|
580
|
+
case NodeTypes.SYMBOL:
|
|
581
|
+
key = route[1];
|
|
582
|
+
if (wildcard) {
|
|
583
|
+
return (key.startsWith("*") ? "" : "*") + key;
|
|
584
|
+
} else {
|
|
585
|
+
return ":" + key;
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
case NodeTypes.SLASH:
|
|
589
|
+
case NodeTypes.DOT:
|
|
590
|
+
case NodeTypes.LITERAL:
|
|
591
|
+
return route[1];
|
|
592
|
+
default:
|
|
593
|
+
throw new Error("Unknown Rails node type");
|
|
659
594
|
}
|
|
595
|
+
}
|
|
660
596
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
597
|
+
visit_globbing(
|
|
598
|
+
route: RouteTree,
|
|
599
|
+
parameters: RouteParameters,
|
|
600
|
+
optional: boolean,
|
|
601
|
+
): string {
|
|
602
|
+
const key = route[1] as string;
|
|
603
|
+
let value = parameters[key];
|
|
604
|
+
delete parameters[key];
|
|
605
|
+
if (this.is_nullable(value)) {
|
|
606
|
+
return this.visit(route, parameters, optional);
|
|
607
|
+
}
|
|
608
|
+
if (this.is_array(value)) {
|
|
609
|
+
value = value.join("/");
|
|
610
|
+
}
|
|
611
|
+
const result = this.path_identifier(value as any);
|
|
612
|
+
return encodeURI(result);
|
|
613
|
+
}
|
|
676
614
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
615
|
+
get_prefix(): string {
|
|
616
|
+
const prefix = this.configuration.prefix;
|
|
617
|
+
return prefix.match("/$")
|
|
618
|
+
? prefix.substring(0, prefix.length - 1)
|
|
619
|
+
: prefix;
|
|
620
|
+
}
|
|
680
621
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
622
|
+
route(
|
|
623
|
+
parts_table: PartsTable,
|
|
624
|
+
route_spec: RouteTree,
|
|
625
|
+
absolute = false,
|
|
626
|
+
): RouteHelper {
|
|
627
|
+
const required_params: string[] = [];
|
|
628
|
+
const parts: string[] = [];
|
|
629
|
+
const default_options: RouteParameters = {};
|
|
630
|
+
for (const [part, { r: required, d: value }] of Object.entries(
|
|
631
|
+
parts_table,
|
|
632
|
+
)) {
|
|
633
|
+
parts.push(part);
|
|
634
|
+
if (required) {
|
|
635
|
+
required_params.push(part);
|
|
636
|
+
}
|
|
637
|
+
if (this.is_not_nullable(value)) {
|
|
638
|
+
default_options[part] = value;
|
|
639
|
+
}
|
|
685
640
|
}
|
|
641
|
+
const result = (...args: OptionalRouteParameter[]): string => {
|
|
642
|
+
return this.build_route(
|
|
643
|
+
parts,
|
|
644
|
+
required_params,
|
|
645
|
+
default_options,
|
|
646
|
+
route_spec,
|
|
647
|
+
absolute,
|
|
648
|
+
args,
|
|
649
|
+
);
|
|
650
|
+
};
|
|
651
|
+
result.requiredParams = () => required_params;
|
|
652
|
+
result.toString = () => {
|
|
653
|
+
return this.build_path_spec(route_spec);
|
|
654
|
+
};
|
|
655
|
+
return result as any;
|
|
656
|
+
}
|
|
686
657
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
658
|
+
route_url(route_defaults: KeywordUrlOptions): string {
|
|
659
|
+
const hostname = route_defaults.host || this.current_host();
|
|
660
|
+
if (!hostname) {
|
|
661
|
+
return "";
|
|
662
|
+
}
|
|
663
|
+
const subdomain = route_defaults.subdomain
|
|
664
|
+
? route_defaults.subdomain + "."
|
|
665
|
+
: "";
|
|
666
|
+
const protocol = route_defaults.protocol || this.current_protocol();
|
|
667
|
+
let port =
|
|
668
|
+
route_defaults.port ||
|
|
669
|
+
(!route_defaults.host ? this.current_port() : undefined);
|
|
670
|
+
port = port ? ":" + port : "";
|
|
671
|
+
return protocol + "://" + subdomain + hostname + port;
|
|
672
|
+
}
|
|
690
673
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
Object.prototype.toString.call(value) === "[object Object]"
|
|
695
|
-
);
|
|
696
|
-
}
|
|
674
|
+
current_host(): string {
|
|
675
|
+
return (isBrowser && window?.location?.hostname) || "";
|
|
676
|
+
}
|
|
697
677
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
678
|
+
current_protocol(): string {
|
|
679
|
+
return (
|
|
680
|
+
(isBrowser && window?.location?.protocol?.replace(/:$/, "")) || "http"
|
|
681
|
+
);
|
|
682
|
+
}
|
|
701
683
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
684
|
+
current_port(): string {
|
|
685
|
+
return (isBrowser && window?.location?.port) || "";
|
|
686
|
+
}
|
|
705
687
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
688
|
+
is_object(value: unknown): value is Collection<unknown> {
|
|
689
|
+
return (
|
|
690
|
+
typeof value === "object" &&
|
|
691
|
+
Object.prototype.toString.call(value) === "[object Object]"
|
|
692
|
+
);
|
|
693
|
+
}
|
|
709
694
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
"JsRoutes configuration prefix option is deprecated in favor of default_url_options.script_name."
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
this.configuration = { ...this.configuration, ...new_config };
|
|
717
|
-
return this.configuration;
|
|
718
|
-
}
|
|
695
|
+
is_array<T>(object: unknown | T[]): object is T[] {
|
|
696
|
+
return object instanceof Array;
|
|
697
|
+
}
|
|
719
698
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
699
|
+
is_callable(object: unknown): object is Function {
|
|
700
|
+
return typeof object === "function" && !!object.call;
|
|
701
|
+
}
|
|
723
702
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
703
|
+
is_reserved_option(key: unknown): key is ReservedOption {
|
|
704
|
+
return ReservedOptions.includes(key as any);
|
|
705
|
+
}
|
|
727
706
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
707
|
+
configure(new_config: Partial<Configuration>): Configuration {
|
|
708
|
+
if (new_config.prefix) {
|
|
709
|
+
console.warn(
|
|
710
|
+
"JsRoutes configuration prefix option is deprecated in favor of default_url_options.script_name.",
|
|
711
|
+
);
|
|
732
712
|
}
|
|
713
|
+
this.configuration = { ...this.configuration, ...new_config };
|
|
714
|
+
return this.configuration;
|
|
715
|
+
}
|
|
733
716
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
module: RouterExposedMethods
|
|
737
|
-
): RouterExposedMethods {
|
|
738
|
-
this.ensure_module_supported(name);
|
|
739
|
-
ModuleReferences[name].define(module);
|
|
740
|
-
return module;
|
|
741
|
-
}
|
|
717
|
+
config(): Configuration {
|
|
718
|
+
return { ...this.configuration };
|
|
742
719
|
}
|
|
743
720
|
|
|
744
|
-
|
|
721
|
+
is_module_supported(name: ModuleType): boolean {
|
|
722
|
+
return ModuleReferences[name].isSupported();
|
|
723
|
+
}
|
|
745
724
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
absolute?: boolean
|
|
752
|
-
): RouteHelper {
|
|
753
|
-
return utils.route(parts_table, route_spec, absolute);
|
|
754
|
-
},
|
|
755
|
-
};
|
|
725
|
+
ensure_module_supported(name: ModuleType): void {
|
|
726
|
+
if (!this.is_module_supported(name)) {
|
|
727
|
+
throw new Error(`${name} is not supported by runtime`);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
756
730
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
serialize: (object: Serializable): string => {
|
|
766
|
-
return utils.serialize(object);
|
|
767
|
-
},
|
|
768
|
-
...RubyVariables.ROUTES_OBJECT,
|
|
769
|
-
});
|
|
731
|
+
define_module(
|
|
732
|
+
name: ModuleType,
|
|
733
|
+
module: RouterExposedMethods,
|
|
734
|
+
): RouterExposedMethods {
|
|
735
|
+
this.ensure_module_supported(name);
|
|
736
|
+
ModuleReferences[name].define(module);
|
|
737
|
+
return module;
|
|
738
|
+
}
|
|
770
739
|
}
|
|
771
|
-
|
|
740
|
+
|
|
741
|
+
const utils = new UtilsClass();
|
|
742
|
+
|
|
743
|
+
// We want this helper name to be short
|
|
744
|
+
const __jsr = {
|
|
745
|
+
r: utils.route.bind(utils),
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
return utils.define_module(RubyVariables.MODULE_TYPE, {
|
|
749
|
+
...__jsr,
|
|
750
|
+
configure: utils.configure.bind(utils),
|
|
751
|
+
config: utils.config.bind(utils),
|
|
752
|
+
serialize: utils.serialize.bind(utils),
|
|
753
|
+
...RubyVariables.ROUTES_OBJECT,
|
|
754
|
+
});
|
|
755
|
+
})();
|