js-routes 2.3.6 → 2.4.0

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