js-routes 2.0.7 → 2.1.2

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
@@ -3,15 +3,51 @@
3
3
  * Based on Rails RubyVariables.RAILS_VERSION routes of RubyVariables.APP_CLASS
4
4
  */
5
5
 
6
- type RouteParameter = unknown;
7
- type RouteParameters = Record<string, RouteParameter>;
8
- type Serializer = (value: unknown) => string;
9
- type RouteHelper = {
10
- (...args: RouteParameter[]): string;
6
+ type Optional<T> = { [P in keyof T]?: T[P] | null };
7
+ type BaseRouteParameter = string | boolean | Date | number;
8
+ type MethodRouteParameter = BaseRouteParameter | (() => BaseRouteParameter);
9
+ type ModelRouteParameter =
10
+ | { id: MethodRouteParameter }
11
+ | { to_param: MethodRouteParameter }
12
+ | { toParam: MethodRouteParameter };
13
+ type RequiredRouteParameter = BaseRouteParameter | ModelRouteParameter;
14
+ type OptionalRouteParameter = undefined | null | RequiredRouteParameter;
15
+ type QueryRouteParameter =
16
+ | OptionalRouteParameter
17
+ | QueryRouteParameter[]
18
+ | { [k: string]: QueryRouteParameter };
19
+ type RouteParameters = Record<string, QueryRouteParameter>;
20
+
21
+ type Serializable = Record<string, unknown>;
22
+ type Serializer = (value: Serializable) => string;
23
+ type RouteHelperExtras = {
11
24
  requiredParams(): string[];
12
25
  toString(): string;
13
26
  };
14
27
 
28
+ type RequiredParameters<T extends number> = T extends 1
29
+ ? [RequiredRouteParameter]
30
+ : T extends 2
31
+ ? [RequiredRouteParameter, RequiredRouteParameter]
32
+ : T extends 3
33
+ ? [RequiredRouteParameter, RequiredRouteParameter, RequiredRouteParameter]
34
+ : T extends 4
35
+ ? [
36
+ RequiredRouteParameter,
37
+ RequiredRouteParameter,
38
+ RequiredRouteParameter,
39
+ RequiredRouteParameter
40
+ ]
41
+ : RequiredRouteParameter[];
42
+
43
+ type RouteHelperOptions<T extends string> = RouteOptions &
44
+ Optional<Record<T, OptionalRouteParameter>>;
45
+
46
+ type RouteHelper<T extends number = number, U extends string = string> = ((
47
+ ...args: [...RequiredParameters<T>, RouteHelperOptions<U>]
48
+ ) => string) &
49
+ RouteHelperExtras;
50
+
15
51
  type RouteHelpers = Record<string, RouteHelper>;
16
52
 
17
53
  type Configuration = {
@@ -21,7 +57,6 @@ type Configuration = {
21
57
  serializer: Serializer;
22
58
  };
23
59
 
24
- type Optional<T> = { [P in keyof T]?: T[P] | null };
25
60
  interface RouterExposedMethods {
26
61
  config(): Configuration;
27
62
  configure(arg: Partial<Configuration>): Configuration;
@@ -32,14 +67,16 @@ type KeywordUrlOptions = Optional<{
32
67
  host: string;
33
68
  protocol: string;
34
69
  subdomain: string;
35
- port: string;
70
+ port: string | number;
36
71
  anchor: string;
37
72
  trailing_slash: boolean;
38
73
  }>;
39
74
 
40
- type PartsTable = Record<string, { r?: boolean; d?: unknown }>;
75
+ type RouteOptions = KeywordUrlOptions & RouteParameters;
41
76
 
42
- type ModuleType = "CJS" | "AMD" | "UMD" | "ESM";
77
+ type PartsTable = Record<string, { r?: boolean; d?: OptionalRouteParameter }>;
78
+
79
+ type ModuleType = "CJS" | "AMD" | "UMD" | "ESM" | "DTS" | "NIL";
43
80
 
44
81
  declare const RubyVariables: {
45
82
  PREFIX: string;
@@ -49,7 +86,7 @@ declare const RubyVariables: {
49
86
  SERIALIZER: Serializer;
50
87
  NAMESPACE: string;
51
88
  ROUTES_OBJECT: RouteHelpers;
52
- MODULE_TYPE: ModuleType | null;
89
+ MODULE_TYPE: ModuleType;
53
90
  WRAPPER: <T>(callback: T) => T;
54
91
  };
55
92
 
@@ -92,30 +129,38 @@ RubyVariables.WRAPPER(
92
129
  }[keyof RouteNodes];
93
130
 
94
131
  const Root = that;
132
+ const isBroswer = typeof window !== "undefined";
95
133
  type ModuleDefinition = {
96
- define: (routes: unknown) => void;
97
- support: () => boolean;
134
+ define: (routes: RouterExposedMethods) => void;
135
+ isSupported: () => boolean;
98
136
  };
99
137
  const ModuleReferences: Record<ModuleType, ModuleDefinition> = {
100
138
  CJS: {
101
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
102
- define: (routes) => (module!.exports = routes),
103
- support: () => typeof module === "object",
139
+ define(routes) {
140
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
141
+ module!.exports = routes;
142
+ },
143
+ isSupported() {
144
+ return typeof module === "object";
145
+ },
104
146
  },
105
147
  AMD: {
106
- define: (routes) =>
148
+ define(routes) {
107
149
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
108
150
  define!([], function () {
109
151
  return routes;
110
- }),
111
- support: () => typeof define === "function" && !!define.amd,
152
+ });
153
+ },
154
+ isSupported() {
155
+ return typeof define === "function" && !!define.amd;
156
+ },
112
157
  },
113
158
  UMD: {
114
- define: (routes) => {
115
- if (ModuleReferences.AMD.support()) {
159
+ define(routes) {
160
+ if (ModuleReferences.AMD.isSupported()) {
116
161
  ModuleReferences.AMD.define(routes);
117
162
  } else {
118
- if (ModuleReferences.CJS.support()) {
163
+ if (ModuleReferences.CJS.isSupported()) {
119
164
  try {
120
165
  ModuleReferences.CJS.define(routes);
121
166
  } catch (error) {
@@ -124,12 +169,38 @@ RubyVariables.WRAPPER(
124
169
  }
125
170
  }
126
171
  },
127
- support: () =>
128
- ModuleReferences.AMD.support() || ModuleReferences.CJS.support(),
172
+ isSupported() {
173
+ return (
174
+ ModuleReferences.AMD.isSupported() ||
175
+ ModuleReferences.CJS.isSupported()
176
+ );
177
+ },
129
178
  },
130
179
  ESM: {
131
- define: () => null,
132
- support: () => true,
180
+ define() {
181
+ // Module can only be defined using ruby code generation
182
+ },
183
+ isSupported() {
184
+ // Its impossible to check if "export" keyword is supported
185
+ return true;
186
+ },
187
+ },
188
+ NIL: {
189
+ define(routes) {
190
+ Utils.namespace(Root, RubyVariables.NAMESPACE, routes);
191
+ },
192
+ isSupported() {
193
+ return !!Root;
194
+ },
195
+ },
196
+ DTS: {
197
+ // Acts the same as ESM
198
+ define(routes) {
199
+ ModuleReferences.ESM.define(routes);
200
+ },
201
+ isSupported() {
202
+ return ModuleReferences.ESM.isSupported();
203
+ },
133
204
  },
134
205
  };
135
206
 
@@ -143,9 +214,6 @@ RubyVariables.WRAPPER(
143
214
  }
144
215
  }
145
216
 
146
- const DeprecatedGlobbingBehavior =
147
- RubyVariables.DEPRECATED_GLOBBING_BEHAVIOR;
148
-
149
217
  const UriEncoderSegmentRegex = /[^a-zA-Z0-9\-._~!$&'()*+,;=:@]/g;
150
218
 
151
219
  const ReservedOptions = [
@@ -204,16 +272,16 @@ RubyVariables.WRAPPER(
204
272
  return result.join("&");
205
273
  }
206
274
 
207
- serialize(object: unknown): string {
275
+ serialize(object: Serializable): string {
208
276
  return this.configuration.serializer(object);
209
277
  }
210
278
 
211
279
  extract_options(
212
280
  number_of_params: number,
213
- args: RouteParameter[]
281
+ args: OptionalRouteParameter[]
214
282
  ): {
215
- args: RouteParameter[];
216
- options: KeywordUrlOptions & RouteParameters;
283
+ args: OptionalRouteParameter[];
284
+ options: RouteOptions;
217
285
  } {
218
286
  const last_el = args[args.length - 1];
219
287
  if (
@@ -226,32 +294,27 @@ RubyVariables.WRAPPER(
226
294
  }
227
295
  return {
228
296
  args: args.slice(0, args.length - 1),
229
- options: last_el as KeywordUrlOptions & RouteParameters,
297
+ options: (last_el as any) as RouteOptions,
230
298
  };
231
299
  } else {
232
300
  return { args, options: {} };
233
301
  }
234
302
  }
235
303
 
236
- looks_like_serialized_model(
237
- object: any
238
- ): object is
239
- | { id: unknown }
240
- | { to_param: unknown }
241
- | { toParam: unknown } {
304
+ looks_like_serialized_model(object: any): object is ModelRouteParameter {
242
305
  return (
243
306
  this.is_object(object) &&
244
- !object[this.configuration.special_options_key] &&
307
+ !(this.configuration.special_options_key in object) &&
245
308
  ("id" in object || "to_param" in object || "toParam" in object)
246
309
  );
247
310
  }
248
311
 
249
- path_identifier(object: unknown): string {
312
+ path_identifier(object: QueryRouteParameter): string {
250
313
  const result = this.unwrap_path_identifier(object);
251
314
  return this.is_nullable(result) || result === false ? "" : "" + result;
252
315
  }
253
316
 
254
- unwrap_path_identifier(object: any): unknown {
317
+ unwrap_path_identifier(object: QueryRouteParameter): unknown {
255
318
  let result: any = object;
256
319
  if (!this.is_object(object)) {
257
320
  return object;
@@ -272,7 +335,7 @@ RubyVariables.WRAPPER(
272
335
  parts: string[],
273
336
  required_params: string[],
274
337
  default_options: RouteParameters,
275
- call_arguments: RouteParameter[]
338
+ call_arguments: OptionalRouteParameter[]
276
339
  ): {
277
340
  keyword_parameters: KeywordUrlOptions;
278
341
  query_parameters: RouteParameters;
@@ -336,7 +399,7 @@ RubyVariables.WRAPPER(
336
399
  default_options: RouteParameters,
337
400
  route: RouteTree,
338
401
  absolute: boolean,
339
- args: RouteParameter[]
402
+ args: OptionalRouteParameter[]
340
403
  ): string {
341
404
  const {
342
405
  keyword_parameters,
@@ -446,9 +509,9 @@ RubyVariables.WRAPPER(
446
509
  }
447
510
 
448
511
  encode_segment(segment: string): string {
449
- return segment.replace(UriEncoderSegmentRegex, function (str) {
450
- return encodeURIComponent(str);
451
- });
512
+ return segment.replace(UriEncoderSegmentRegex, (str) =>
513
+ encodeURIComponent(str)
514
+ );
452
515
  }
453
516
 
454
517
  is_optional_node(node: NodeTypes): boolean {
@@ -498,7 +561,9 @@ RubyVariables.WRAPPER(
498
561
  value = value.join("/");
499
562
  }
500
563
  const result = this.path_identifier(value as any);
501
- return DeprecatedGlobbingBehavior ? result : encodeURI(result);
564
+ return RubyVariables.DEPRECATED_GLOBBING_BEHAVIOR
565
+ ? result
566
+ : encodeURI(result);
502
567
  }
503
568
 
504
569
  get_prefix(): string {
@@ -527,7 +592,7 @@ RubyVariables.WRAPPER(
527
592
  default_options[part] = value;
528
593
  }
529
594
  }
530
- const result = (...args: RouteParameter[]): string => {
595
+ const result = (...args: OptionalRouteParameter[]): string => {
531
596
  return this.build_route(
532
597
  parts,
533
598
  required_params,
@@ -541,7 +606,7 @@ RubyVariables.WRAPPER(
541
606
  result.toString = () => {
542
607
  return this.build_path_spec(route_spec);
543
608
  };
544
- return result;
609
+ return result as any;
545
610
  }
546
611
 
547
612
  route_url(route_defaults: KeywordUrlOptions): string {
@@ -560,31 +625,18 @@ RubyVariables.WRAPPER(
560
625
  return protocol + "://" + subdomain + hostname + port;
561
626
  }
562
627
 
563
- has_location(): boolean {
564
- return this.is_not_nullable(window) && !!window.location;
565
- }
566
-
567
- current_host(): string | null {
568
- if (this.has_location()) {
569
- return window.location.hostname;
570
- } else {
571
- return null;
572
- }
628
+ current_host(): string {
629
+ return (isBroswer && window?.location?.hostname) || "";
573
630
  }
574
631
 
575
632
  current_protocol(): string {
576
- if (this.has_location() && window.location.protocol !== "") {
577
- return window.location.protocol.replace(/:$/, "");
578
- } else {
579
- return "http";
580
- }
633
+ return (
634
+ (isBroswer && window?.location?.protocol?.replace(/:$/, "")) || "http"
635
+ );
581
636
  }
637
+
582
638
  current_port(): string {
583
- if (this.has_location() && window.location.port !== "") {
584
- return window.location.port;
585
- } else {
586
- return "";
587
- }
639
+ return (isBroswer && window?.location?.port) || "";
588
640
  }
589
641
 
590
642
  is_object(value: unknown): value is Record<string, unknown> {
@@ -635,7 +687,7 @@ RubyVariables.WRAPPER(
635
687
  }
636
688
 
637
689
  is_module_supported(name: ModuleType): boolean {
638
- return ModuleReferences[name].support();
690
+ return ModuleReferences[name].isSupported();
639
691
  }
640
692
 
641
693
  ensure_module_supported(name: ModuleType): void {
@@ -644,13 +696,7 @@ RubyVariables.WRAPPER(
644
696
  }
645
697
  }
646
698
 
647
- define_module(
648
- name: ModuleType | null,
649
- module: RouterExposedMethods
650
- ): void {
651
- if (!name) {
652
- return;
653
- }
699
+ define_module(name: ModuleType, module: RouterExposedMethods): void {
654
700
  this.ensure_module_supported(name);
655
701
  ModuleReferences[name].define(module);
656
702
  }
@@ -677,17 +723,13 @@ RubyVariables.WRAPPER(
677
723
  config: (): Configuration => {
678
724
  return Utils.config();
679
725
  },
680
- serialize: (object: unknown): string => {
726
+ serialize: (object: Serializable): string => {
681
727
  return Utils.serialize(object);
682
728
  },
683
729
  ...RubyVariables.ROUTES_OBJECT,
684
730
  };
685
731
 
686
- Utils.namespace(Root, RubyVariables.NAMESPACE, result);
687
-
688
- if (RubyVariables.MODULE_TYPE) {
689
- Utils.define_module(RubyVariables.MODULE_TYPE, result);
690
- }
732
+ Utils.define_module(RubyVariables.MODULE_TYPE, result);
691
733
  return result;
692
734
  }
693
735
  )(this);
@@ -1,8 +1,14 @@
1
1
  namespace :js do
2
- desc "Make a js file that will have functions that will return restful routes/urls."
2
+ desc "Make a js file with all rails route URL helpers"
3
3
  task routes: :environment do
4
4
  require "js-routes"
5
-
6
5
  JsRoutes.generate!
7
6
  end
7
+
8
+ namespace :routes do
9
+ desc "Make a js file with all rails route URL helpers and typescript definitions for them"
10
+ task typescript: "js:routes" do
11
+ JsRoutes.definitions!
12
+ end
13
+ end
8
14
  end
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ module: {
3
+ rules: [
4
+ {
5
+ test: /\.erb$/,
6
+ enforce: 'pre',
7
+ loader: 'rails-erb-loader'
8
+ },
9
+ ]
10
+ }
11
+ };
@@ -0,0 +1,5 @@
1
+ JsRoutes.setup do |c|
2
+ # Setup your JS module system:
3
+ # ESM, CJS, AMD, UMD or nil
4
+ # c.module_type = "ESM"
5
+ end
@@ -0,0 +1 @@
1
+ <%= JsRoutes.generate %>
@@ -0,0 +1,114 @@
1
+ /**
2
+ * File generated by js-routes RubyVariables.GEM_VERSION
3
+ * Based on Rails RubyVariables.RAILS_VERSION routes of RubyVariables.APP_CLASS
4
+ */
5
+ declare type Optional<T> = {
6
+ [P in keyof T]?: T[P] | null;
7
+ };
8
+ declare type BaseRouteParameter = string | boolean | Date | number;
9
+ declare type MethodRouteParameter = BaseRouteParameter | (() => BaseRouteParameter);
10
+ declare type ModelRouteParameter = {
11
+ id: MethodRouteParameter;
12
+ } | {
13
+ to_param: MethodRouteParameter;
14
+ } | {
15
+ toParam: MethodRouteParameter;
16
+ };
17
+ declare type RequiredRouteParameter = BaseRouteParameter | ModelRouteParameter;
18
+ declare type OptionalRouteParameter = undefined | null | RequiredRouteParameter;
19
+ declare type QueryRouteParameter = OptionalRouteParameter | QueryRouteParameter[] | {
20
+ [k: string]: QueryRouteParameter;
21
+ };
22
+ declare type RouteParameters = Record<string, QueryRouteParameter>;
23
+ declare type Serializable = Record<string, unknown>;
24
+ declare type Serializer = (value: Serializable) => string;
25
+ declare type RouteHelperExtras = {
26
+ requiredParams(): string[];
27
+ toString(): string;
28
+ };
29
+ declare type RequiredParameters<T extends number> = T extends 1 ? [RequiredRouteParameter] : T extends 2 ? [RequiredRouteParameter, RequiredRouteParameter] : T extends 3 ? [RequiredRouteParameter, RequiredRouteParameter, RequiredRouteParameter] : T extends 4 ? [
30
+ RequiredRouteParameter,
31
+ RequiredRouteParameter,
32
+ RequiredRouteParameter,
33
+ RequiredRouteParameter
34
+ ] : RequiredRouteParameter[];
35
+ declare type RouteHelperOptions<T extends string> = RouteOptions & Optional<Record<T, OptionalRouteParameter>>;
36
+ declare type RouteHelper<T extends number = number, U extends string = string> = ((...args: [...RequiredParameters<T>, RouteHelperOptions<U>]) => string) & RouteHelperExtras;
37
+ declare type RouteHelpers = Record<string, RouteHelper>;
38
+ declare type Configuration = {
39
+ prefix: string;
40
+ default_url_options: RouteParameters;
41
+ special_options_key: string;
42
+ serializer: Serializer;
43
+ };
44
+ interface RouterExposedMethods {
45
+ config(): Configuration;
46
+ configure(arg: Partial<Configuration>): Configuration;
47
+ serialize: Serializer;
48
+ }
49
+ declare type KeywordUrlOptions = Optional<{
50
+ host: string;
51
+ protocol: string;
52
+ subdomain: string;
53
+ port: string | number;
54
+ anchor: string;
55
+ trailing_slash: boolean;
56
+ }>;
57
+ declare type RouteOptions = KeywordUrlOptions & RouteParameters;
58
+ declare type PartsTable = Record<string, {
59
+ r?: boolean;
60
+ d?: OptionalRouteParameter;
61
+ }>;
62
+ declare type ModuleType = "CJS" | "AMD" | "UMD" | "ESM" | "DTS" | "NIL";
63
+ declare const RubyVariables: {
64
+ PREFIX: string;
65
+ DEPRECATED_GLOBBING_BEHAVIOR: boolean;
66
+ SPECIAL_OPTIONS_KEY: string;
67
+ DEFAULT_URL_OPTIONS: RouteParameters;
68
+ SERIALIZER: Serializer;
69
+ NAMESPACE: string;
70
+ ROUTES_OBJECT: RouteHelpers;
71
+ MODULE_TYPE: ModuleType;
72
+ WRAPPER: <T>(callback: T) => T;
73
+ };
74
+ declare const define: undefined | (((arg: unknown[], callback: () => unknown) => void) & {
75
+ amd?: unknown;
76
+ });
77
+ declare const module: {
78
+ exports: any;
79
+ } | undefined;
80
+ export const configure: RouterExposedMethods['configure'];
81
+
82
+ export const config: RouterExposedMethods['config'];
83
+
84
+ export const serialize: RouterExposedMethods['serialize'];
85
+
86
+ /**
87
+ * Generates rails route to
88
+ * /inboxes/:inbox_id/messages/:message_id/attachments/:id(.:format)
89
+ * @param {any} inbox_id
90
+ * @param {any} message_id
91
+ * @param {any} id
92
+ * @param {object | undefined} options
93
+ * @returns {string} route path
94
+ */
95
+ export const inbox_message_attachment_path: ((
96
+ inbox_id: RequiredRouteParameter,
97
+ message_id: RequiredRouteParameter,
98
+ id: RequiredRouteParameter,
99
+ options?: {format?: OptionalRouteParameter} & RouteOptions
100
+ ) => string) & RouteHelperExtras;
101
+
102
+ /**
103
+ * Generates rails route to
104
+ * /inboxes(.:format)
105
+ * @param {object | undefined} options
106
+ * @returns {string} route path
107
+ */
108
+ export const inboxes_path: ((
109
+ options?: {format?: OptionalRouteParameter} & RouteOptions
110
+ ) => string) & RouteHelperExtras;
111
+
112
+ // By some reason this line prevents all types in a file
113
+ // from being automatically exported
114
+ export {};
@@ -0,0 +1,56 @@
1
+ import {
2
+ inbox_message_attachment_path,
3
+ inboxes_path,
4
+ serialize,
5
+ configure,
6
+ config,
7
+ } from "./routes.spec";
8
+
9
+ // Route Helpers
10
+ inboxes_path();
11
+ inboxes_path({
12
+ locale: "en",
13
+ search: {
14
+ q: "ukraine",
15
+ page: 3,
16
+ keywords: ["large", "small", { advanced: true }],
17
+ },
18
+ });
19
+
20
+ inbox_message_attachment_path(1, "2", true);
21
+ inbox_message_attachment_path(
22
+ { id: 1 },
23
+ { to_param: () => "2" },
24
+ { toParam: () => true }
25
+ );
26
+ inbox_message_attachment_path(1, "2", true, { format: "json" });
27
+ inboxes_path.toString();
28
+ inboxes_path.requiredParams();
29
+
30
+ // serialize test
31
+ const SerializerArgument = {
32
+ locale: "en",
33
+ search: {
34
+ q: "ukraine",
35
+ page: 3,
36
+ keywords: ["large", "small", { advanced: true }],
37
+ },
38
+ };
39
+ serialize(SerializerArgument);
40
+ config().serializer(SerializerArgument);
41
+
42
+ // configure test
43
+ configure({
44
+ default_url_options: { port: 1, host: null },
45
+ prefix: "",
46
+ special_options_key: "_options",
47
+ serializer: (value) => JSON.stringify(value),
48
+ });
49
+
50
+ // config tests
51
+ const Config = config();
52
+ console.log(
53
+ Config.prefix,
54
+ Config.default_url_options,
55
+ Config.special_options_key
56
+ );