js-routes 2.2.6 → 2.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Readme.md +24 -11
- data/lib/js_routes/configuration.rb +1 -1
- data/lib/js_routes/generators/base.rb +1 -1
- data/lib/js_routes/generators/webpacker.rb +1 -1
- data/lib/js_routes/route.rb +9 -2
- data/lib/js_routes/version.rb +1 -1
- data/lib/routes.d.ts +2 -2
- data/lib/routes.js +25 -3
- data/lib/routes.ts +34 -11
- data/spec/js_routes/default_serializer_spec.rb +1 -1
- data/spec/js_routes/module_types/dts/routes.spec.d.ts +2 -2
- data/spec/js_routes/module_types/dts_spec.rb +1 -1
- data/spec/js_routes/module_types/esm_spec.rb +2 -2
- data/spec/js_routes/module_types/umd_spec.rb +1 -1
- data/spec/js_routes/rails_routes_compatibility_spec.rb +17 -12
- data/spec/js_routes/route_specification_spec.rb +1 -4
- data/spec/js_routes/{zzz_last_post_rails_init_spec.rb → zzz_sprockets_spec.rb} +1 -1
- data/spec/spec_helper.rb +4 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6dfe30c594c24cf044d0f19342e17cd6b23857da7ec68a0bf68d8f8be9f33def
|
4
|
+
data.tar.gz: 3aefa8fc04221f07996ada7c57f35b22d2ae519575a6df0e20c6d860fe6203ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 652fa60215a5c812e3a0cb0284870bfa63c6eeb1d249940388ade48a3f90a7209559d7cf66750e697ffc317bfcd1bec87e57fed44cad375795ead64fb1bcd254
|
7
|
+
data.tar.gz: a68bdb295fb704a5ed753d0576e700668aecfe30d5ec6d425ed458d029eb084de66ff26e0789c808e655574146973db02226a1a48f2cd81fa6ebda3379b7bc8c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## master
|
2
2
|
|
3
|
+
## v2.2.8
|
4
|
+
|
5
|
+
* Leave emoji symbols intact when encoding URI fragment [#312](https://github.com/railsware/js-routes/issues/312)
|
6
|
+
* Use webpacker config variable instead of hardcode [#309](https://github.com/railsware/js-routes/issues/309)
|
7
|
+
* Use `File.exist?` to be compatible with all versions of ruby [#310](https://github.com/railsware/js-routes/issues/310)
|
8
|
+
|
9
|
+
## v2.2.7
|
10
|
+
|
11
|
+
* Fix ESM Tree Shaking [#306](https://github.com/railsware/js-routes/issues/306)
|
12
|
+
|
3
13
|
## v2.2.6
|
4
14
|
|
5
15
|
* Prefer to extend `javascript:build` instead of `assets:precompile`. [#305](https://github.com/railsware/js-routes/issues/305)
|
data/Readme.md
CHANGED
@@ -16,16 +16,17 @@ gem "js-routes"
|
|
16
16
|
|
17
17
|
There are several possible ways to setup JsRoutes:
|
18
18
|
|
19
|
-
* [Quick and easy](#quick-start)
|
19
|
+
* [Quick and easy](#quick-start) - Recommended
|
20
20
|
* Uses Rack Middleware to automatically update routes locally
|
21
|
-
* Automatically generates routes files on
|
21
|
+
* Automatically generates routes files on javascript build
|
22
22
|
* Works great for a simple Rails application
|
23
|
-
* [Webpacker ERB Loader](#webpacker)
|
24
|
-
* Requires ESM module system (the default)
|
25
|
-
* Doesn't support typescript definitions
|
26
23
|
* [Advanced Setup](#advanced-setup)
|
27
24
|
* Allows very custom setups
|
28
|
-
*
|
25
|
+
* Automatic updates need to be customized
|
26
|
+
* [Webpacker ERB Loader](#webpacker) - Legacy
|
27
|
+
* Requires ESM module system (the default)
|
28
|
+
* Doesn't support typescript definitions
|
29
|
+
* [Sprockets](#sprockets) - Legacy
|
29
30
|
* Deprecated and not recommended for modern apps
|
30
31
|
|
31
32
|
<div id='quick-start'></div>
|
@@ -80,7 +81,7 @@ Add js-routes files to `.gitignore`:
|
|
80
81
|
|
81
82
|
### Webpacker ERB loader
|
82
83
|
|
83
|
-
**IMPORTANT**:
|
84
|
+
**IMPORTANT**: the setup doesn't support IDE autocompletion with [Typescript](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html)
|
84
85
|
|
85
86
|
|
86
87
|
#### Use a Generator
|
@@ -414,9 +415,14 @@ JsRoutes itself does not have security holes.
|
|
414
415
|
It makes URLs without access protection more reachable by potential attacker.
|
415
416
|
If that is an issue for you, you may use one of the following solutions:
|
416
417
|
|
417
|
-
###
|
418
|
+
### ESM Tree shaking
|
419
|
+
|
420
|
+
Make sure `module_type` is set to `ESM` (the default). Modern JS bundlers like
|
421
|
+
[Webpack](https://webpack.js.org) can statically determine which ESM exports are used, and remove
|
422
|
+
the unused exports to reduce bundle size. This is known as [Tree
|
423
|
+
Shaking](https://webpack.js.org/guides/tree-shaking/).
|
418
424
|
|
419
|
-
|
425
|
+
JS files can use named imports to import only required routes into the file, like:
|
420
426
|
|
421
427
|
``` javascript
|
422
428
|
import {
|
@@ -428,8 +434,15 @@ import {
|
|
428
434
|
} from '../routes'
|
429
435
|
```
|
430
436
|
|
431
|
-
|
432
|
-
|
437
|
+
JS files can also use star imports (`import * as`) for tree shaking, as long as only explicit property accesses are used.
|
438
|
+
|
439
|
+
``` javascript
|
440
|
+
import * as routes from '../routes';
|
441
|
+
|
442
|
+
console.log(routes.inbox_path); // OK, only `inbox_path` is included in the bundle
|
443
|
+
|
444
|
+
console.log(Object.keys(routes)); // forces bundler to include all exports, breaking tree shaking
|
445
|
+
```
|
433
446
|
|
434
447
|
### Exclude option
|
435
448
|
|
@@ -75,7 +75,7 @@ module JsRoutes
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def output_file
|
78
|
-
webpacker_dir = pathname('app', 'javascript')
|
78
|
+
webpacker_dir = defined?(Webpacker) ? Webpacker.config.source_path : pathname('app', 'javascript')
|
79
79
|
sprockets_dir = pathname('app','assets','javascripts')
|
80
80
|
file_name = file || default_file_name
|
81
81
|
sprockets_file = sprockets_dir.join(file_name)
|
@@ -10,7 +10,7 @@ class JsRoutes::Generators::Base < Rails::Generators::Base
|
|
10
10
|
"app/javascript/packs/application.js",
|
11
11
|
"app/javascript/controllers/application.js",
|
12
12
|
].find do |path|
|
13
|
-
File.
|
13
|
+
File.exist?(Rails.root.join(path))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -7,7 +7,7 @@ class JsRoutes::Generators::Webpacker < Rails::Generators::Base
|
|
7
7
|
def create_webpack
|
8
8
|
copy_file "initializer.rb", "config/initializers/js_routes.rb"
|
9
9
|
copy_file "erb.js", "config/webpack/loaders/erb.js"
|
10
|
-
copy_file "routes.js.erb", "
|
10
|
+
copy_file "routes.js.erb", "#{Webpacker.config.source_path}/routes.js.erb"
|
11
11
|
inject_into_file "config/webpack/environment.js", loader_content
|
12
12
|
if path = application_js_path
|
13
13
|
inject_into_file path, pack_content
|
data/lib/js_routes/route.rb
CHANGED
@@ -33,8 +33,15 @@ module JsRoutes
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def body(absolute)
|
36
|
-
@configuration.dts?
|
37
|
-
definition_body
|
36
|
+
if @configuration.dts?
|
37
|
+
definition_body
|
38
|
+
else
|
39
|
+
# For tree-shaking ESM, add a #__PURE__ comment informing Webpack/minifiers that the call to `__jsr.r`
|
40
|
+
# has no side-effects (e.g. modifying global variables) and is safe to remove when unused.
|
41
|
+
# https://webpack.js.org/guides/tree-shaking/#clarifying-tree-shaking-and-sideeffects
|
42
|
+
pure_comment = @configuration.esm? ? '/*#__PURE__*/ ' : ''
|
43
|
+
"#{pure_comment}__jsr.r(#{arguments(absolute).map{|a| json(a)}.join(', ')})"
|
44
|
+
end
|
38
45
|
end
|
39
46
|
|
40
47
|
def definition_body
|
data/lib/js_routes/version.rb
CHANGED
data/lib/routes.d.ts
CHANGED
@@ -32,8 +32,8 @@ declare type RequiredParameters<T extends number> = T extends 1 ? [RequiredRoute
|
|
32
32
|
RequiredRouteParameter,
|
33
33
|
RequiredRouteParameter
|
34
34
|
] : RequiredRouteParameter[];
|
35
|
-
declare type RouteHelperOptions
|
36
|
-
declare type RouteHelper<T extends number = number
|
35
|
+
declare type RouteHelperOptions = RouteOptions & Record<string, OptionalRouteParameter>;
|
36
|
+
declare type RouteHelper<T extends number = number> = ((...args: [...RequiredParameters<T>, RouteHelperOptions]) => string) & RouteHelperExtras;
|
37
37
|
declare type RouteHelpers = Record<string, RouteHelper>;
|
38
38
|
declare type Configuration = {
|
39
39
|
prefix: string;
|
data/lib/routes.js
CHANGED
@@ -19,6 +19,14 @@ RubyVariables.WRAPPER(
|
|
19
19
|
NodeTypes[NodeTypes["DOT"] = 8] = "DOT";
|
20
20
|
})(NodeTypes || (NodeTypes = {}));
|
21
21
|
const isBrowser = typeof window !== "undefined";
|
22
|
+
const UnescapedSpecials = "-._~!$&'()*+,;=:@"
|
23
|
+
.split("")
|
24
|
+
.map((s) => s.charCodeAt(0));
|
25
|
+
const UnescapedRanges = [
|
26
|
+
["a", "z"],
|
27
|
+
["A", "Z"],
|
28
|
+
["0", "9"],
|
29
|
+
].map((range) => range.map((s) => s.charCodeAt(0)));
|
22
30
|
const ModuleReferences = {
|
23
31
|
CJS: {
|
24
32
|
define(routes) {
|
@@ -97,7 +105,6 @@ RubyVariables.WRAPPER(
|
|
97
105
|
this.name = ParametersMissing.name;
|
98
106
|
}
|
99
107
|
}
|
100
|
-
const UriEncoderSegmentRegex = /[^a-zA-Z0-9\-._~!$&'()*+,;=:@]/g;
|
101
108
|
const ReservedOptions = [
|
102
109
|
"anchor",
|
103
110
|
"trailing_slash",
|
@@ -349,7 +356,22 @@ RubyVariables.WRAPPER(
|
|
349
356
|
}
|
350
357
|
}
|
351
358
|
encode_segment(segment) {
|
352
|
-
|
359
|
+
if (segment.match(/^[a-zA-Z0-9-]$/)) {
|
360
|
+
// Performance optimization for 99% of cases
|
361
|
+
return segment;
|
362
|
+
}
|
363
|
+
return (segment.match(/./gu) || [])
|
364
|
+
.map((ch) => {
|
365
|
+
const code = ch.charCodeAt(0);
|
366
|
+
if (UnescapedRanges.find((range) => code >= range[0] && code <= range[1]) ||
|
367
|
+
UnescapedSpecials.includes(code)) {
|
368
|
+
return ch;
|
369
|
+
}
|
370
|
+
else {
|
371
|
+
return encodeURIComponent(ch);
|
372
|
+
}
|
373
|
+
})
|
374
|
+
.join("");
|
353
375
|
}
|
354
376
|
is_optional_node(node) {
|
355
377
|
return [NodeTypes.STAR, NodeTypes.SYMBOL, NodeTypes.CAT].includes(node);
|
@@ -358,7 +380,7 @@ RubyVariables.WRAPPER(
|
|
358
380
|
let key;
|
359
381
|
switch (route[0]) {
|
360
382
|
case NodeTypes.GROUP:
|
361
|
-
return
|
383
|
+
return `(${this.build_path_spec(route[1])})`;
|
362
384
|
case NodeTypes.CAT:
|
363
385
|
return (this.build_path_spec(route[1]) + this.build_path_spec(route[2]));
|
364
386
|
case NodeTypes.STAR:
|
data/lib/routes.ts
CHANGED
@@ -40,11 +40,10 @@ type RequiredParameters<T extends number> = T extends 1
|
|
40
40
|
]
|
41
41
|
: RequiredRouteParameter[];
|
42
42
|
|
43
|
-
type RouteHelperOptions
|
44
|
-
Optional<Record<T, OptionalRouteParameter>>;
|
43
|
+
type RouteHelperOptions = RouteOptions & Record<string, OptionalRouteParameter>;
|
45
44
|
|
46
|
-
type RouteHelper<T extends number = number
|
47
|
-
...args: [...RequiredParameters<T>, RouteHelperOptions
|
45
|
+
type RouteHelper<T extends number = number> = ((
|
46
|
+
...args: [...RequiredParameters<T>, RouteHelperOptions]
|
48
47
|
) => string) &
|
49
48
|
RouteHelperExtras;
|
50
49
|
|
@@ -101,7 +100,7 @@ declare const module: { exports: any } | undefined;
|
|
101
100
|
RubyVariables.WRAPPER(
|
102
101
|
// eslint-disable-next-line
|
103
102
|
(): RouterExposedMethods => {
|
104
|
-
const hasProp = (value: unknown, key: string) =>
|
103
|
+
const hasProp = (value: unknown, key: string): boolean =>
|
105
104
|
Object.prototype.hasOwnProperty.call(value, key);
|
106
105
|
enum NodeTypes {
|
107
106
|
GROUP = 1,
|
@@ -136,6 +135,16 @@ RubyVariables.WRAPPER(
|
|
136
135
|
define: (routes: RouterExposedMethods) => void;
|
137
136
|
isSupported: () => boolean;
|
138
137
|
};
|
138
|
+
|
139
|
+
const UnescapedSpecials = "-._~!$&'()*+,;=:@"
|
140
|
+
.split("")
|
141
|
+
.map((s) => s.charCodeAt(0));
|
142
|
+
const UnescapedRanges = [
|
143
|
+
["a", "z"],
|
144
|
+
["A", "Z"],
|
145
|
+
["0", "9"],
|
146
|
+
].map((range) => range.map((s) => s.charCodeAt(0)));
|
147
|
+
|
139
148
|
const ModuleReferences: Record<ModuleType, ModuleDefinition> = {
|
140
149
|
CJS: {
|
141
150
|
define(routes) {
|
@@ -216,8 +225,6 @@ RubyVariables.WRAPPER(
|
|
216
225
|
}
|
217
226
|
}
|
218
227
|
|
219
|
-
const UriEncoderSegmentRegex = /[^a-zA-Z0-9\-._~!$&'()*+,;=:@]/g;
|
220
|
-
|
221
228
|
const ReservedOptions = [
|
222
229
|
"anchor",
|
223
230
|
"trailing_slash",
|
@@ -526,9 +533,25 @@ RubyVariables.WRAPPER(
|
|
526
533
|
}
|
527
534
|
|
528
535
|
encode_segment(segment: string): string {
|
529
|
-
|
530
|
-
|
531
|
-
|
536
|
+
if (segment.match(/^[a-zA-Z0-9-]$/)) {
|
537
|
+
// Performance optimization for 99% of cases
|
538
|
+
return segment;
|
539
|
+
}
|
540
|
+
return (segment.match(/./gu) || [])
|
541
|
+
.map((ch) => {
|
542
|
+
const code = ch.charCodeAt(0);
|
543
|
+
if (
|
544
|
+
UnescapedRanges.find(
|
545
|
+
(range) => code >= range[0] && code <= range[1]
|
546
|
+
) ||
|
547
|
+
UnescapedSpecials.includes(code)
|
548
|
+
) {
|
549
|
+
return ch;
|
550
|
+
} else {
|
551
|
+
return encodeURIComponent(ch);
|
552
|
+
}
|
553
|
+
})
|
554
|
+
.join("");
|
532
555
|
}
|
533
556
|
|
534
557
|
is_optional_node(node: NodeTypes): boolean {
|
@@ -539,7 +562,7 @@ RubyVariables.WRAPPER(
|
|
539
562
|
let key: string;
|
540
563
|
switch (route[0]) {
|
541
564
|
case NodeTypes.GROUP:
|
542
|
-
return
|
565
|
+
return `(${this.build_path_spec(route[1])})`;
|
543
566
|
case NodeTypes.CAT:
|
544
567
|
return (
|
545
568
|
this.build_path_spec(route[1]) + this.build_path_spec(route[2])
|
@@ -32,8 +32,8 @@ declare type RequiredParameters<T extends number> = T extends 1 ? [RequiredRoute
|
|
32
32
|
RequiredRouteParameter,
|
33
33
|
RequiredRouteParameter
|
34
34
|
] : RequiredRouteParameter[];
|
35
|
-
declare type RouteHelperOptions
|
36
|
-
declare type RouteHelper<T extends number = number
|
35
|
+
declare type RouteHelperOptions = RouteOptions & Record<string, OptionalRouteParameter>;
|
36
|
+
declare type RouteHelper<T extends number = number> = ((...args: [...RequiredParameters<T>, RouteHelperOptions]) => string) & RouteHelperExtras;
|
37
37
|
declare type RouteHelpers = Record<string, RouteHelper>;
|
38
38
|
declare type Configuration = {
|
39
39
|
prefix: string;
|
@@ -105,7 +105,7 @@ DOC
|
|
105
105
|
describe "compiled javascript asset" do
|
106
106
|
subject { ERB.new(File.read("app/assets/javascripts/js-routes.js.erb")).result(binding) }
|
107
107
|
it "should have js routes code" do
|
108
|
-
is_expected.to include("export const inbox_message_path = __jsr.r(")
|
108
|
+
is_expected.to include("export const inbox_message_path = /*#__PURE__*/ __jsr.r(")
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -24,7 +24,7 @@ describe JsRoutes, "compatibility with ESM" do
|
|
24
24
|
* @param {object | undefined} options
|
25
25
|
* @returns {string} route path
|
26
26
|
*/
|
27
|
-
export const inboxes_path = __jsr.r
|
27
|
+
export const inboxes_path = /*#__PURE__*/ __jsr.r(
|
28
28
|
DOC
|
29
29
|
end
|
30
30
|
|
@@ -39,7 +39,7 @@ DOC
|
|
39
39
|
describe "compiled javascript asset" do
|
40
40
|
subject { ERB.new(File.read("app/assets/javascripts/js-routes.js.erb")).result(binding) }
|
41
41
|
it "should have js routes code" do
|
42
|
-
is_expected.to include("export const inbox_message_path = __jsr.r(")
|
42
|
+
is_expected.to include("export const inbox_message_path = /*#__PURE__*/ __jsr.r(")
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -2,11 +2,8 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe JsRoutes, "compatibility with Rails" do
|
4
4
|
|
5
|
-
let(:generated_js) do
|
6
|
-
JsRoutes.generate(module_type: nil, namespace: 'Routes')
|
7
|
-
end
|
8
5
|
before(:each) do
|
9
|
-
|
6
|
+
evallib(module_type: nil, namespace: 'Routes')
|
10
7
|
end
|
11
8
|
|
12
9
|
it "should generate collection routing" do
|
@@ -87,14 +84,22 @@ describe JsRoutes, "compatibility with Rails" do
|
|
87
84
|
expectjs("Routes.budgie_descendents_path(1)").to eq(test_routes.budgie_descendents_path(1))
|
88
85
|
end
|
89
86
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
87
|
+
describe "url parameters encoding" do
|
88
|
+
|
89
|
+
it "should support route with parameters containing symbols that need URI-encoding", :aggregate_failures do
|
90
|
+
expectjs("Routes.inbox_path('#hello')").to eq(test_routes.inbox_path('#hello'))
|
91
|
+
expectjs("Routes.inbox_path('some param')").to eq(test_routes.inbox_path('some param'))
|
92
|
+
expectjs("Routes.inbox_path('some param with more & more encode symbols')").to eq(test_routes.inbox_path('some param with more & more encode symbols'))
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should support route with parameters containing symbols not need URI-encoding", :aggregate_failures do
|
96
|
+
expectjs("Routes.inbox_path(':some_id')").to eq(test_routes.inbox_path(':some_id'))
|
97
|
+
expectjs("Routes.inbox_path('.+')").to eq(test_routes.inbox_path('.+'))
|
98
|
+
end
|
99
|
+
|
100
|
+
it "supports emoji characters", :aggregate_failures do
|
101
|
+
expectjs("Routes.inbox_path('💗')").to eq(test_routes.inbox_path('💗'))
|
102
|
+
end
|
98
103
|
end
|
99
104
|
|
100
105
|
describe "when route has defaults" do
|
@@ -3,11 +3,8 @@ require "spec_helper"
|
|
3
3
|
|
4
4
|
describe JsRoutes, "compatibility with Rails" do
|
5
5
|
|
6
|
-
let(:generated_js) do
|
7
|
-
JsRoutes.generate(module_type: nil, namespace: 'Routes')
|
8
|
-
end
|
9
6
|
before(:each) do
|
10
|
-
|
7
|
+
evallib(module_type: nil, namespace: 'Routes')
|
11
8
|
end
|
12
9
|
|
13
10
|
context "when specs" do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: js-routes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bogdan Gusiev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -197,7 +197,7 @@ files:
|
|
197
197
|
- spec/js_routes/options_spec.rb
|
198
198
|
- spec/js_routes/rails_routes_compatibility_spec.rb
|
199
199
|
- spec/js_routes/route_specification_spec.rb
|
200
|
-
- spec/js_routes/
|
200
|
+
- spec/js_routes/zzz_sprockets_spec.rb
|
201
201
|
- spec/spec_helper.rb
|
202
202
|
- spec/support/routes.rb
|
203
203
|
- spec/tsconfig.json
|
@@ -222,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
222
|
- !ruby/object:Gem::Version
|
223
223
|
version: '0'
|
224
224
|
requirements: []
|
225
|
-
rubygems_version: 3.
|
225
|
+
rubygems_version: 3.4.15
|
226
226
|
signing_key:
|
227
227
|
specification_version: 4
|
228
228
|
summary: Brings Rails named routes to javascript
|