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.js CHANGED
@@ -1,534 +1,126 @@
1
- // eslint-disable-next-line
2
- RubyVariables.WRAPPER(
3
- // eslint-disable-next-line
4
- () => {
5
- const hasProp = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
6
- let NodeTypes;
7
- (function (NodeTypes) {
8
- NodeTypes[NodeTypes["GROUP"] = 1] = "GROUP";
9
- NodeTypes[NodeTypes["CAT"] = 2] = "CAT";
10
- NodeTypes[NodeTypes["SYMBOL"] = 3] = "SYMBOL";
11
- NodeTypes[NodeTypes["OR"] = 4] = "OR";
12
- NodeTypes[NodeTypes["STAR"] = 5] = "STAR";
13
- NodeTypes[NodeTypes["LITERAL"] = 6] = "LITERAL";
14
- NodeTypes[NodeTypes["SLASH"] = 7] = "SLASH";
15
- NodeTypes[NodeTypes["DOT"] = 8] = "DOT";
16
- })(NodeTypes || (NodeTypes = {}));
17
- const isBrowser = typeof window !== "undefined";
18
- const UnescapedSpecials = "-._~!$&'()*+,;=:@"
19
- .split("")
20
- .map((s) => s.charCodeAt(0));
21
- const UnescapedRanges = [
22
- ["a", "z"],
23
- ["A", "Z"],
24
- ["0", "9"],
25
- ].map((range) => range.map((s) => s.charCodeAt(0)));
26
- const ModuleReferences = {
27
- CJS: {
28
- define(routes) {
29
- if (module) {
30
- module.exports = routes;
31
- }
32
- },
33
- isSupported() {
34
- return typeof module === "object";
35
- },
36
- },
37
- AMD: {
38
- define(routes) {
39
- if (define) {
40
- define([], function () {
41
- return routes;
42
- });
43
- }
1
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
2
+ RubyVariables.IMPORT_ROUTER;
3
+ RubyVariables.WRAPPER(() => {
4
+ var _a;
5
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
6
+ RubyVariables.EMBED_ROUTER;
7
+ const Modules = {
8
+ references: {
9
+ CJS: {
10
+ define(routes) {
11
+ if (module) {
12
+ // Some javascript processors (like vite/rolldown)
13
+ // warn on using module dot exports in an ESM module.
14
+ // This just obfuscates that assignment a little so
15
+ // users don't get a warning they can't fix.
16
+ const _mod = module;
17
+ _mod.exports = routes;
18
+ }
19
+ },
20
+ isSupported() {
21
+ return typeof module === "object";
22
+ },
44
23
  },
45
- isSupported() {
46
- return typeof define === "function" && !!define.amd;
24
+ AMD: {
25
+ define(routes) {
26
+ if (define) {
27
+ define([], function () {
28
+ return routes;
29
+ });
30
+ }
31
+ },
32
+ isSupported() {
33
+ return typeof define === "function" && !!define.amd;
34
+ },
47
35
  },
48
- },
49
- UMD: {
50
- define(routes) {
51
- if (ModuleReferences.AMD.isSupported()) {
52
- ModuleReferences.AMD.define(routes);
53
- }
54
- else {
55
- if (ModuleReferences.CJS.isSupported()) {
56
- try {
57
- ModuleReferences.CJS.define(routes);
58
- }
59
- catch (error) {
60
- if (error.name !== "TypeError")
61
- throw error;
36
+ UMD: {
37
+ define(routes) {
38
+ if (Modules.references.AMD.isSupported()) {
39
+ Modules.references.AMD.define(routes);
40
+ }
41
+ else {
42
+ if (Modules.references.CJS.isSupported()) {
43
+ try {
44
+ Modules.references.CJS.define(routes);
45
+ }
46
+ catch (error) {
47
+ if (error.name !== "TypeError")
48
+ throw error;
49
+ }
62
50
  }
63
51
  }
64
- }
52
+ },
53
+ isSupported() {
54
+ return (Modules.references.AMD.isSupported() ||
55
+ Modules.references.CJS.isSupported());
56
+ },
65
57
  },
66
- isSupported() {
67
- return (ModuleReferences.AMD.isSupported() ||
68
- ModuleReferences.CJS.isSupported());
58
+ ESM: {
59
+ define() {
60
+ // Module can only be defined using ruby code generation
61
+ },
62
+ isSupported() {
63
+ // Its impossible to check if "export" keyword is supported
64
+ return true;
65
+ },
69
66
  },
70
- },
71
- ESM: {
72
- define() {
73
- // Module can only be defined using ruby code generation
74
- },
75
- isSupported() {
76
- // Its impossible to check if "export" keyword is supported
77
- return true;
78
- },
79
- },
80
- NIL: {
81
- define() {
82
- // Defined using RubyVariables.WRAPPER
67
+ NIL: {
68
+ define() {
69
+ // Defined using RubyVariables . WRAPPER
70
+ },
71
+ isSupported() {
72
+ return true;
73
+ },
83
74
  },
84
- isSupported() {
85
- return true;
75
+ DTS: {
76
+ // Acts the same as ESM
77
+ define(routes) {
78
+ Modules.references.ESM.define(routes);
79
+ },
80
+ isSupported() {
81
+ return Modules.references.ESM.isSupported();
82
+ },
86
83
  },
87
- },
88
- DTS: {
89
- // Acts the same as ESM
90
- define(routes) {
91
- ModuleReferences.ESM.define(routes);
92
- },
93
- isSupported() {
94
- return ModuleReferences.ESM.isSupported();
84
+ PKG: {
85
+ // Acts the same as ESM
86
+ define() {
87
+ Modules.references.ESM.define(new Router());
88
+ },
89
+ isSupported() {
90
+ return Modules.references.ESM.isSupported();
91
+ },
95
92
  },
96
93
  },
97
- };
98
- class ParametersMissing extends Error {
99
- constructor(...keys) {
100
- super(`Route missing required keys: ${keys.join(", ")}`);
101
- this.keys = keys;
102
- Object.setPrototypeOf(this, Object.getPrototypeOf(this));
103
- this.name = ParametersMissing.name;
104
- }
105
- }
106
- const ReservedOptions = [
107
- "anchor",
108
- "trailing_slash",
109
- "subdomain",
110
- "host",
111
- "port",
112
- "protocol",
113
- "script_name",
114
- ];
115
- class UtilsClass {
116
- constructor() {
117
- this.configuration = {
118
- prefix: RubyVariables.PREFIX,
119
- default_url_options: RubyVariables.DEFAULT_URL_OPTIONS,
120
- special_options_key: RubyVariables.SPECIAL_OPTIONS_KEY,
121
- serializer: RubyVariables.SERIALIZER || this.default_serializer.bind(this),
122
- };
123
- }
124
- default_serializer(value, prefix) {
125
- if (!prefix && !this.is_object(value)) {
126
- throw new Error("Url parameters should be a javascript hash");
127
- }
128
- prefix = prefix || "";
129
- const result = [];
130
- if (this.is_array(value)) {
131
- for (const element of value) {
132
- result.push(this.default_serializer(element, prefix + "[]"));
133
- }
134
- }
135
- else if (this.is_object(value)) {
136
- for (let key in value) {
137
- if (!hasProp(value, key))
138
- continue;
139
- let prop = value[key];
140
- if (prefix) {
141
- key = prefix + "[" + key + "]";
142
- }
143
- const subvalue = this.default_serializer(prop, key);
144
- if (subvalue.length) {
145
- result.push(subvalue);
146
- }
147
- }
148
- }
149
- else {
150
- result.push(this.is_not_nullable(value) ||
151
- RubyVariables.DEPRECATED_NIL_QUERY_PARAMETER_BEHAVIOR
152
- ? encodeURIComponent(prefix) +
153
- "=" +
154
- encodeURIComponent("" + (value !== null && value !== void 0 ? value : ""))
155
- : encodeURIComponent(prefix));
156
- }
157
- return result.join("&");
158
- }
159
- serialize(object) {
160
- return this.configuration.serializer(object);
161
- }
162
- extract_options(number_of_params, args) {
163
- const last_el = args[args.length - 1];
164
- if ((args.length > number_of_params && last_el === 0) ||
165
- (this.is_object(last_el) &&
166
- !this.looks_like_serialized_model(last_el))) {
167
- if (this.is_object(last_el)) {
168
- delete last_el[this.configuration.special_options_key];
169
- }
170
- return {
171
- args: args.slice(0, args.length - 1),
172
- options: last_el,
173
- };
174
- }
175
- else {
176
- return { args, options: {} };
177
- }
178
- }
179
- looks_like_serialized_model(object) {
180
- return (this.is_object(object) &&
181
- !(this.configuration.special_options_key in object) &&
182
- ("id" in object || "to_param" in object || "toParam" in object));
183
- }
184
- path_identifier(object) {
185
- const result = this.unwrap_path_identifier(object);
186
- return this.is_nullable(result) ||
187
- (RubyVariables.DEPRECATED_FALSE_PARAMETER_BEHAVIOR &&
188
- result === false)
189
- ? ""
190
- : "" + result;
191
- }
192
- unwrap_path_identifier(object) {
193
- let result = object;
194
- if (!this.is_object(object)) {
195
- return object;
196
- }
197
- if ("to_param" in object) {
198
- result = object.to_param;
199
- }
200
- else if ("toParam" in object) {
201
- result = object.toParam;
202
- }
203
- else if ("id" in object) {
204
- result = object.id;
205
- }
206
- else {
207
- result = object;
208
- }
209
- return this.is_callable(result) ? result.call(object) : result;
210
- }
211
- partition_parameters(parts, required_params, default_options, call_arguments) {
212
- // eslint-disable-next-line prefer-const
213
- let { args, options } = this.extract_options(parts.length, call_arguments);
214
- if (args.length > parts.length) {
215
- throw new Error("Too many parameters provided for path");
216
- }
217
- let use_all_parts = args.length > required_params.length;
218
- const parts_options = {
219
- ...this.configuration.default_url_options,
220
- };
221
- for (const key in options) {
222
- const value = options[key];
223
- if (!hasProp(options, key))
224
- continue;
225
- use_all_parts = true;
226
- if (parts.includes(key)) {
227
- parts_options[key] = value;
228
- }
229
- }
230
- options = {
231
- ...this.configuration.default_url_options,
232
- ...default_options,
233
- ...options,
234
- };
235
- const keyword_parameters = {};
236
- let query_parameters = {};
237
- for (const key in options) {
238
- if (!hasProp(options, key))
239
- continue;
240
- const value = options[key];
241
- if (key === "params") {
242
- if (this.is_object(value)) {
243
- query_parameters = {
244
- ...query_parameters,
245
- ...value,
246
- };
247
- }
248
- else {
249
- throw new Error("params value should always be an object");
250
- }
251
- }
252
- else if (this.is_reserved_option(key)) {
253
- keyword_parameters[key] = value;
254
- }
255
- else {
256
- if (!this.is_nullable(value) &&
257
- (value !== default_options[key] || required_params.includes(key))) {
258
- query_parameters[key] = value;
259
- }
260
- }
261
- }
262
- const route_parts = use_all_parts ? parts : required_params;
263
- let i = 0;
264
- for (const part of route_parts) {
265
- if (i < args.length) {
266
- const value = args[i];
267
- if (!hasProp(parts_options, part)) {
268
- query_parameters[part] = value;
269
- ++i;
270
- }
271
- }
272
- }
273
- return { keyword_parameters, query_parameters };
274
- }
275
- build_route(parts, required_params, default_options, route, absolute, args) {
276
- const { keyword_parameters, query_parameters } = this.partition_parameters(parts, required_params, default_options, args);
277
- let { trailing_slash, anchor, script_name } = keyword_parameters;
278
- const missing_params = required_params.filter((param) => !hasProp(query_parameters, param) ||
279
- this.is_nullable(query_parameters[param]));
280
- if (missing_params.length) {
281
- throw new ParametersMissing(...missing_params);
282
- }
283
- let result = this.get_prefix() + this.visit(route, query_parameters);
284
- if (trailing_slash) {
285
- result = result.replace(/(.*?)[/]?$/, "$1/");
286
- }
287
- const url_params = this.serialize(query_parameters);
288
- if (url_params.length) {
289
- result += "?" + url_params;
290
- }
291
- if (anchor) {
292
- result += "#" + anchor;
293
- }
294
- if (script_name) {
295
- const last_index = script_name.length - 1;
296
- if (script_name[last_index] == "/" && result[0] == "/") {
297
- script_name = script_name.slice(0, last_index);
298
- }
299
- result = script_name + result;
300
- }
301
- if (absolute) {
302
- result = this.route_url(keyword_parameters) + result;
303
- }
304
- return result;
305
- }
306
- visit(route, parameters, optional = false) {
307
- switch (route[0]) {
308
- case NodeTypes.GROUP:
309
- return this.visit(route[1], parameters, true);
310
- case NodeTypes.CAT:
311
- return this.visit_cat(route, parameters, optional);
312
- case NodeTypes.SYMBOL:
313
- return this.visit_symbol(route, parameters, optional);
314
- case NodeTypes.STAR:
315
- return this.visit_globbing(route[1], parameters, true);
316
- case NodeTypes.LITERAL:
317
- case NodeTypes.SLASH:
318
- case NodeTypes.DOT:
319
- return route[1];
320
- default:
321
- throw new Error("Unknown Rails node type");
322
- }
323
- }
324
- is_not_nullable(object) {
325
- return !this.is_nullable(object);
326
- }
327
- is_nullable(object) {
328
- return object === undefined || object === null;
329
- }
330
- visit_cat(
331
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
332
- [_type, left, right], parameters, optional) {
333
- const left_part = this.visit(left, parameters, optional);
334
- let right_part = this.visit(right, parameters, optional);
335
- if (optional &&
336
- ((this.is_optional_node(left[0]) && !left_part) ||
337
- (this.is_optional_node(right[0]) && !right_part))) {
338
- return "";
339
- }
340
- // if left_part ends on '/' and right_part starts on '/'
341
- if (left_part[left_part.length - 1] === "/" && right_part[0] === "/") {
342
- // strip slash from right_part
343
- // to prevent double slash
344
- right_part = right_part.substring(1);
345
- }
346
- return left_part + right_part;
347
- }
348
- visit_symbol(
349
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
350
- [_type, key], parameters, optional) {
351
- const value = this.path_identifier(parameters[key]);
352
- delete parameters[key];
353
- if (value.length) {
354
- return this.encode_segment(value);
355
- }
356
- if (optional) {
357
- return "";
358
- }
359
- else {
360
- throw new ParametersMissing(key);
361
- }
362
- }
363
- encode_segment(segment) {
364
- if (segment.match(/^[a-zA-Z0-9-]$/)) {
365
- // Performance optimization for 99% of cases
366
- return segment;
367
- }
368
- return (segment.match(/./gu) || [])
369
- .map((ch) => {
370
- const code = ch.charCodeAt(0);
371
- if (UnescapedRanges.find((range) => code >= range[0] && code <= range[1]) ||
372
- UnescapedSpecials.includes(code)) {
373
- return ch;
374
- }
375
- else {
376
- return encodeURIComponent(ch);
377
- }
378
- })
379
- .join("");
380
- }
381
- is_optional_node(node) {
382
- return [NodeTypes.STAR, NodeTypes.SYMBOL, NodeTypes.CAT].includes(node);
383
- }
384
- build_path_spec(route, wildcard = false) {
385
- let key;
386
- switch (route[0]) {
387
- case NodeTypes.GROUP:
388
- return `(${this.build_path_spec(route[1])})`;
389
- case NodeTypes.CAT:
390
- return (this.build_path_spec(route[1]) + this.build_path_spec(route[2]));
391
- case NodeTypes.STAR:
392
- return this.build_path_spec(route[1], true);
393
- case NodeTypes.SYMBOL:
394
- key = route[1];
395
- if (wildcard) {
396
- return (key.startsWith("*") ? "" : "*") + key;
397
- }
398
- else {
399
- return ":" + key;
400
- }
401
- break;
402
- case NodeTypes.SLASH:
403
- case NodeTypes.DOT:
404
- case NodeTypes.LITERAL:
405
- return route[1];
406
- default:
407
- throw new Error("Unknown Rails node type");
408
- }
409
- }
410
- visit_globbing(route, parameters, optional) {
411
- const key = route[1];
412
- let value = parameters[key];
413
- delete parameters[key];
414
- if (this.is_nullable(value)) {
415
- return this.visit(route, parameters, optional);
416
- }
417
- if (this.is_array(value)) {
418
- value = value.join("/");
419
- }
420
- const result = this.path_identifier(value);
421
- return encodeURI(result);
422
- }
423
- get_prefix() {
424
- const prefix = this.configuration.prefix;
425
- return prefix.match("/$")
426
- ? prefix.substring(0, prefix.length - 1)
427
- : prefix;
428
- }
429
- route(parts_table, route_spec, absolute = false) {
430
- const required_params = [];
431
- const parts = [];
432
- const default_options = {};
433
- for (const [part, { r: required, d: value }] of Object.entries(parts_table)) {
434
- parts.push(part);
435
- if (required) {
436
- required_params.push(part);
437
- }
438
- if (this.is_not_nullable(value)) {
439
- default_options[part] = value;
440
- }
441
- }
442
- const result = (...args) => {
443
- return this.build_route(parts, required_params, default_options, route_spec, absolute, args);
444
- };
445
- result.requiredParams = () => required_params;
446
- result.toString = () => {
447
- return this.build_path_spec(route_spec);
448
- };
449
- return result;
450
- }
451
- route_url(route_defaults) {
452
- const hostname = route_defaults.host || this.current_host();
453
- if (!hostname) {
454
- return "";
455
- }
456
- const subdomain = route_defaults.subdomain
457
- ? route_defaults.subdomain + "."
458
- : "";
459
- const protocol = route_defaults.protocol || this.current_protocol();
460
- let port = route_defaults.port ||
461
- (!route_defaults.host ? this.current_port() : undefined);
462
- port = port ? ":" + port : "";
463
- return protocol + "://" + subdomain + hostname + port;
464
- }
465
- current_host() {
466
- var _a;
467
- return (isBrowser && ((_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.hostname)) || "";
468
- }
469
- current_protocol() {
470
- var _a, _b;
471
- return ((isBrowser && ((_b = (_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.protocol) === null || _b === void 0 ? void 0 : _b.replace(/:$/, ""))) || "http");
472
- }
473
- current_port() {
474
- var _a;
475
- return (isBrowser && ((_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.port)) || "";
476
- }
477
- is_object(value) {
478
- return (typeof value === "object" &&
479
- Object.prototype.toString.call(value) === "[object Object]");
480
- }
481
- is_array(object) {
482
- return object instanceof Array;
483
- }
484
- is_callable(object) {
485
- return typeof object === "function" && !!object.call;
486
- }
487
- is_reserved_option(key) {
488
- return ReservedOptions.includes(key);
489
- }
490
- configure(new_config) {
491
- if (new_config.prefix) {
492
- console.warn("JsRoutes configuration prefix option is deprecated in favor of default_url_options.script_name.");
493
- }
494
- this.configuration = { ...this.configuration, ...new_config };
495
- return this.configuration;
496
- }
497
- config() {
498
- return { ...this.configuration };
499
- }
500
94
  is_module_supported(name) {
501
- return ModuleReferences[name].isSupported();
502
- }
95
+ return this.references[name].isSupported();
96
+ },
503
97
  ensure_module_supported(name) {
504
98
  if (!this.is_module_supported(name)) {
505
99
  throw new Error(`${name} is not supported by runtime`);
506
100
  }
507
- }
101
+ },
508
102
  define_module(name, module) {
509
103
  this.ensure_module_supported(name);
510
- ModuleReferences[name].define(module);
104
+ this.references[name].define(module);
511
105
  return module;
512
- }
513
- }
514
- const utils = new UtilsClass();
515
- // We want this helper name to be short
516
- const __jsr = {
517
- r(parts_table, route_spec, absolute) {
518
- return utils.route(parts_table, route_spec, absolute);
519
106
  },
520
107
  };
521
- return utils.define_module(RubyVariables.MODULE_TYPE, {
522
- ...__jsr,
523
- configure: (config) => {
524
- return utils.configure(config);
525
- },
526
- config: () => {
527
- return utils.config();
528
- },
529
- serialize: (object) => {
530
- return utils.serialize(object);
531
- },
108
+ const router = new Router({
109
+ prefix: RubyVariables.PREFIX,
110
+ default_url_options: RubyVariables.DEFAULT_URL_OPTIONS,
111
+ special_options_key: RubyVariables.SPECIAL_OPTIONS_KEY,
112
+ serializer: (_a = RubyVariables.SERIALIZER) !== null && _a !== void 0 ? _a : undefined,
113
+ deprecated_false_parameter_behavior: RubyVariables.DEPRECATED_FALSE_PARAMETER_BEHAVIOR,
114
+ deprecated_nil_query_parameter_behavior: RubyVariables.DEPRECATED_NIL_QUERY_PARAMETER_BEHAVIOR,
115
+ include_undefined_query_parameters: RubyVariables.INCLUDE_UNDEFINED_QUERY_PARAMETERS,
116
+ });
117
+ const __route = router.__route.bind(router);
118
+ return Modules.define_module(RubyVariables.MODULE_TYPE, {
119
+ __route,
120
+ configure: router.configure.bind(router),
121
+ config: router.config.bind(router),
122
+ serialize: router.serialize.bind(router),
532
123
  ...RubyVariables.ROUTES_OBJECT,
533
124
  });
534
125
  })();
126
+ export {};