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