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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +95 -0
- data/Readme.md +51 -1
- data/lib/js_routes/configuration.rb +62 -5
- data/lib/js_routes/generators/base.rb +5 -4
- data/lib/js_routes/instance.rb +96 -25
- data/lib/js_routes/route.rb +40 -8
- data/lib/js_routes/utils.rb +6 -1
- data/lib/js_routes/version.rb +1 -1
- data/lib/js_routes.rb +11 -1
- data/lib/router.d.ts +63 -0
- data/lib/router.js +449 -0
- data/lib/router.ts +665 -0
- data/lib/routes.d.ts +1 -78
- data/lib/routes.js +103 -502
- data/lib/routes.ts +125 -703
- data/lib/templates/initializer.rb +12 -2
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40c089f66c3042418a4b05690dd145fa20d0c61094d5242eb818b54cf8908e0a
|
|
4
|
+
data.tar.gz: 228996f6fe1b66fd475aecc4372da6d03ff25d06e23c426fdd44b3c608ef16ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 72aaae2f2221a6db8e96d10f3bc410f82f6c9f5f9ff9c03c193bdac42bb332113acf972a9331afde0800dde6cb11128aa94adecee8e365af29cf647fff4c5b06
|
|
7
|
+
data.tar.gz: 87a8a03ff9d7929d9fc46c8c46fc913c43a6efa1a55b04b3b52476c75533349a6b538eff3fe2a1e306ce4cf198652578b33c0ca2382679f0dbaf5b39b8d25660
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,101 @@
|
|
|
2
2
|
|
|
3
3
|
## Pending
|
|
4
4
|
|
|
5
|
+
## [2.4.0]
|
|
6
|
+
|
|
7
|
+
### Package mode
|
|
8
|
+
|
|
9
|
+
Add `package` option and `JsRoutes.package` / `JsRoutes.package!` API for sharing a single Router runtime across multiple route files.
|
|
10
|
+
|
|
11
|
+
**Why:** When an app generates several ESM route files (e.g. one per domain or engine), each file previously embedded the full js-routes runtime (~10 KB minified). With `package`, the runtime is extracted into one `router.js` and every route file imports it — the runtime is downloaded and parsed only once.
|
|
12
|
+
|
|
13
|
+
Generate the shared router package (no route definitions, just the runtime):
|
|
14
|
+
|
|
15
|
+
``` ruby
|
|
16
|
+
# config/initializers/js_routes.rb
|
|
17
|
+
JsRoutes.package! # writes app/javascript/router.js
|
|
18
|
+
# or with a custom path:
|
|
19
|
+
JsRoutes.package!("shared/router.js")
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Generate consumer route files that import from it:
|
|
23
|
+
|
|
24
|
+
``` ruby
|
|
25
|
+
JsRoutes.generate!(
|
|
26
|
+
"app/javascript/admin_routes.js",
|
|
27
|
+
typed: true,
|
|
28
|
+
package: "./router.js", # or package: true for the default path
|
|
29
|
+
include: /\Aadmin_/
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
JsRoutes.generate!(
|
|
33
|
+
"app/javascript/api_routes.js",
|
|
34
|
+
typed: true,
|
|
35
|
+
package: "./router.js",
|
|
36
|
+
include: /\Aapi_/
|
|
37
|
+
)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or share one configuration block:
|
|
41
|
+
|
|
42
|
+
``` ruby
|
|
43
|
+
JsRoutes.setup do |c|
|
|
44
|
+
c.module_type = "ESM"
|
|
45
|
+
c.package = "./router.js" # all generated files import the same runtime
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
JsRoutes.package! # router.js — runtime only
|
|
49
|
+
JsRoutes.generate! # routes.js — route helpers only
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`package: true` is a shorthand for `package: "./router.js"`.
|
|
53
|
+
|
|
54
|
+
### `include_undefined_query_parameters` option
|
|
55
|
+
|
|
56
|
+
Add `include_undefined_query_parameters` configuration option. Fixes [#345](https://github.com/railsware/js-routes/issues/345).
|
|
57
|
+
|
|
58
|
+
JavaScript uses `undefined` to mean "not provided," but js-routes previously treated `undefined` query object values the same as `null`. With Rails 8.1's nil parameter handling, that causes `undefined` values to appear as bare keys like `?foo`.
|
|
59
|
+
|
|
60
|
+
* Set it to `false` to omit object properties whose value is `undefined`, matching typical JavaScript semantics. This is the recommended value for new and upgrading apps.
|
|
61
|
+
* Set it to `true` only when the application depends on serializing `undefined` as Rails `nil` (legacy behavior).
|
|
62
|
+
* Explicit `null` values continue to serialize as Rails `nil` in all cases.
|
|
63
|
+
* When unset, the legacy behavior is preserved and a deprecation warning is emitted so apps can opt in deliberately.
|
|
64
|
+
|
|
65
|
+
### JavaScript reserved word escaping
|
|
66
|
+
|
|
67
|
+
Escape JavaScript reserved words in route helper names and TypeScript parameter names. Fixes broken `.d.ts` output when a route segment name collides with a JS keyword.
|
|
68
|
+
|
|
69
|
+
Given a route like:
|
|
70
|
+
|
|
71
|
+
``` ruby
|
|
72
|
+
scope "/returns/:return" do
|
|
73
|
+
resources :objects, only: [:show]
|
|
74
|
+
end
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The generated `.d.ts` now uses `return_` instead of the invalid `return`:
|
|
78
|
+
|
|
79
|
+
``` typescript
|
|
80
|
+
export const object_path: ((
|
|
81
|
+
return_: RequiredRouteParameter,
|
|
82
|
+
id: RequiredRouteParameter,
|
|
83
|
+
options?: RouteOptions
|
|
84
|
+
) => string) & RouteHelperExtras;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
In `compact` mode, if the short helper name would itself be a reserved word, the `_path` suffix is kept as a fallback:
|
|
88
|
+
|
|
89
|
+
``` ruby
|
|
90
|
+
# route named :return with compact: true
|
|
91
|
+
return_path(…) # kept — `return` alone would be invalid
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Bug Fixes
|
|
95
|
+
|
|
96
|
+
* Fix `generate!` with `typed: true` raising when `package:` option is set. The `package:` option is now stripped before being forwarded to `definitions!`, which only generates type declarations and has no use for it.
|
|
97
|
+
* Support `config.javascript_path` Rails configuration. Fixes [#344](https://github.com/railsware/js-routes/issues/344).
|
|
98
|
+
* Do not emit a deprecation warning when `prefix` is set to an empty string. Fixes [#340](https://github.com/railsware/js-routes/issues/340).
|
|
99
|
+
|
|
5
100
|
## [2.3.7]
|
|
6
101
|
|
|
7
102
|
* Obfuscate assignment to module.exports in order to prevent warnings in javascript bundlers, like Vite. Fixes [#337](https://github.com/railsware/js-routes/issues/337).
|
data/Readme.md
CHANGED
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
<img src="/logo.webp" alt="Logo" width="200" height="200">
|
|
6
6
|
|
|
7
|
-
Generates javascript file that defines all Rails named routes as javascript
|
|
7
|
+
Generates javascript file that defines all Rails named routes as javascript functions:
|
|
8
|
+
|
|
9
|
+
``` ruby
|
|
10
|
+
Rails.application.routes.draw do
|
|
11
|
+
root "home#index"
|
|
12
|
+
namespace :api do
|
|
13
|
+
resources :users
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
```
|
|
8
17
|
|
|
9
18
|
``` js
|
|
10
19
|
import { root_path, api_user_path } from './routes';
|
|
@@ -15,6 +24,12 @@ api_user_path(25, include_profile: true, format: 'json') // => /api/users/25.jso
|
|
|
15
24
|
|
|
16
25
|
[More Examples](#usage)
|
|
17
26
|
|
|
27
|
+
## Philosophy
|
|
28
|
+
|
|
29
|
+
1. Move fast, break nothing.
|
|
30
|
+
2. Minimum invention, maximum compatiblity with Rails.
|
|
31
|
+
3. Ready for your advanced architecture.
|
|
32
|
+
|
|
18
33
|
## Intallation
|
|
19
34
|
|
|
20
35
|
Your Rails Gemfile:
|
|
@@ -240,6 +255,31 @@ which will cause any `JsRoutes` instance to generate defintions instead of route
|
|
|
240
255
|
|
|
241
256
|
<div id="sprockets"></div>
|
|
242
257
|
|
|
258
|
+
<div id="package"></div>
|
|
259
|
+
|
|
260
|
+
#### Using shared package
|
|
261
|
+
|
|
262
|
+
Some setups may benefit from splitting routes into multiple files with the core js-routes utils shared between these files. This can be helpful for codebases that have a large number of routes and can improve tree shaking. This is only available when the `module_type` is set to `ESM`.
|
|
263
|
+
|
|
264
|
+
```ruby
|
|
265
|
+
class AdvancedJsRoutesMiddleware < JsRoutes::Middleware
|
|
266
|
+
def regenerate
|
|
267
|
+
JsRoutes.package!
|
|
268
|
+
|
|
269
|
+
JsRoutes.generate!(
|
|
270
|
+
"admin_routes.js",
|
|
271
|
+
include: /^admin_/,
|
|
272
|
+
package: './router.js'
|
|
273
|
+
)
|
|
274
|
+
JsRoutes.generate!(
|
|
275
|
+
"api_routes.js",
|
|
276
|
+
include: /^api_/,
|
|
277
|
+
package: './router.js'
|
|
278
|
+
)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
```
|
|
282
|
+
|
|
243
283
|
### Sprockets (Deprecated)
|
|
244
284
|
|
|
245
285
|
If you are using [Sprockets](https://github.com/rails/sprockets-rails) you may configure js-routes in the following way.
|
|
@@ -328,6 +368,10 @@ Options to configure JavaScript file generator. These options are only available
|
|
|
328
368
|
* Default: `-> { Rails.application }`
|
|
329
369
|
* `file` - a file location where generated routes are stored
|
|
330
370
|
* Default: `app/javascript/routes.js` if setup with Webpacker, otherwise `app/assets/javascripts/routes.js` if setup with Sprockets.
|
|
371
|
+
* `package` - specify where the shared package will be imported from. e.g. `'./router.js'`.
|
|
372
|
+
* Generate the shared package with `package!`.
|
|
373
|
+
* See [Using shared package](#package).
|
|
374
|
+
* Default: `nil`
|
|
331
375
|
* `optional_definition_params` - make all route paramters in definition optional
|
|
332
376
|
* See [related compatibility issue](#optional-definition-params)
|
|
333
377
|
* Default: `false`
|
|
@@ -359,6 +403,12 @@ Options to configure routes formatting. These options are available both in Ruby
|
|
|
359
403
|
* Default: `nil`. Uses built-in serializer compatible with Rails
|
|
360
404
|
* Example: `jQuery.param` - use jQuery's serializer algorithm. You can attach serialize function from your favorite AJAX framework.
|
|
361
405
|
* Example: `function (object) { ... }` - use completely custom serializer of your application.
|
|
406
|
+
* `include_undefined_query_parameters` - when using the built-in serializer, include query object properties whose value is `undefined` as Rails `nil` instead of omitting them.
|
|
407
|
+
* Default: `nil`. Preserves legacy serialization and emits a warning until this option is set explicitly.
|
|
408
|
+
* New generated initializers set this option to `false`.
|
|
409
|
+
* Set this option to `false` to omit `undefined` query object properties.
|
|
410
|
+
* Compatible with Rails nil query serialization: explicit `null` still serializes as Rails `nil`, including the Rails 8.1 bare-key behavior.
|
|
411
|
+
* Custom `serializer` functions remain responsible for their own `undefined` handling.
|
|
362
412
|
* `special_options_key` - a special key that helps JsRoutes to destinguish serialized model from options hash
|
|
363
413
|
* This option exists because JS doesn't provide a difference between an object and a hash
|
|
364
414
|
* Default: `_options`
|
|
@@ -18,6 +18,12 @@ module JsRoutes
|
|
|
18
18
|
attr_accessor :include
|
|
19
19
|
sig { returns(FileName) }
|
|
20
20
|
attr_accessor :file
|
|
21
|
+
sig { returns(T.nilable(String)) }
|
|
22
|
+
attr_reader :package
|
|
23
|
+
|
|
24
|
+
def package=(value)
|
|
25
|
+
@package = value == true ? "./router.js" : (value == false ? nil : value)
|
|
26
|
+
end
|
|
21
27
|
sig { returns(Prefix) }
|
|
22
28
|
attr_reader :prefix
|
|
23
29
|
sig { returns(T::Boolean) }
|
|
@@ -26,6 +32,8 @@ module JsRoutes
|
|
|
26
32
|
attr_accessor :camel_case
|
|
27
33
|
sig { returns(Options) }
|
|
28
34
|
attr_accessor :default_url_options
|
|
35
|
+
sig { returns(T.nilable(T::Boolean)) }
|
|
36
|
+
attr_accessor :include_undefined_query_parameters
|
|
29
37
|
sig { returns(T::Boolean) }
|
|
30
38
|
attr_accessor :compact
|
|
31
39
|
sig { returns(T.nilable(String)) }
|
|
@@ -42,6 +50,10 @@ module JsRoutes
|
|
|
42
50
|
attr_accessor :optional_definition_params
|
|
43
51
|
sig { returns(BannerCaller) }
|
|
44
52
|
attr_accessor :banner
|
|
53
|
+
sig { returns(T::Boolean) }
|
|
54
|
+
attr_accessor :deprecated_false_parameter_behavior
|
|
55
|
+
sig { returns(T::Boolean) }
|
|
56
|
+
attr_accessor :deprecated_nil_query_parameter_behavior
|
|
45
57
|
|
|
46
58
|
sig {params(attributes: T.nilable(Options)).void }
|
|
47
59
|
def initialize(attributes = nil)
|
|
@@ -53,6 +65,7 @@ module JsRoutes
|
|
|
53
65
|
@url_links = T.let(false, T::Boolean)
|
|
54
66
|
@camel_case = T.let(false, T::Boolean)
|
|
55
67
|
@default_url_options = T.let(T.unsafe({}), Options)
|
|
68
|
+
@include_undefined_query_parameters = T.let(nil, T.nilable(T::Boolean))
|
|
56
69
|
@compact = T.let(false, T::Boolean)
|
|
57
70
|
@serializer = T.let(nil, T.nilable(String))
|
|
58
71
|
@special_options_key = T.let("_options", Literal)
|
|
@@ -61,6 +74,15 @@ module JsRoutes
|
|
|
61
74
|
@documentation = T.let(true, T::Boolean)
|
|
62
75
|
@optional_definition_params = T.let(false, T::Boolean)
|
|
63
76
|
@banner = T.let(default_banner, BannerCaller)
|
|
77
|
+
@package = T.let(nil, T.nilable(String))
|
|
78
|
+
@deprecated_false_parameter_behavior = T.let(
|
|
79
|
+
defined?(Rails) ? JsRoutes::Utils.rails_version < Gem::Version.new('7.0.0') : false,
|
|
80
|
+
T::Boolean
|
|
81
|
+
)
|
|
82
|
+
@deprecated_nil_query_parameter_behavior = T.let(
|
|
83
|
+
defined?(Rails) ? JsRoutes::Utils.rails_version < Gem::Version.new('8.1.0') : false,
|
|
84
|
+
T::Boolean
|
|
85
|
+
)
|
|
64
86
|
|
|
65
87
|
return unless attributes
|
|
66
88
|
assign(attributes)
|
|
@@ -92,7 +114,7 @@ module JsRoutes
|
|
|
92
114
|
end
|
|
93
115
|
|
|
94
116
|
def prefix=(value)
|
|
95
|
-
JsRoutes::Utils.deprecator.warn("JsRoutes configuration prefix is deprecated in favor of default_url_options.script_name.")
|
|
117
|
+
JsRoutes::Utils.deprecator.warn("JsRoutes configuration prefix is deprecated in favor of default_url_options.script_name.") unless value.blank?
|
|
96
118
|
@prefix = value
|
|
97
119
|
end
|
|
98
120
|
|
|
@@ -111,11 +133,20 @@ module JsRoutes
|
|
|
111
133
|
self.module_type === 'DTS'
|
|
112
134
|
end
|
|
113
135
|
|
|
136
|
+
sig {returns(T::Boolean)}
|
|
137
|
+
def pkg?
|
|
138
|
+
module_type === 'PKG'
|
|
139
|
+
end
|
|
140
|
+
|
|
114
141
|
sig {returns(T::Boolean)}
|
|
115
142
|
def modern?
|
|
116
143
|
esm? || dts?
|
|
117
144
|
end
|
|
118
145
|
|
|
146
|
+
def use_package?
|
|
147
|
+
esm? && package
|
|
148
|
+
end
|
|
149
|
+
|
|
119
150
|
sig { void }
|
|
120
151
|
def require_esm
|
|
121
152
|
raise "ESM module type is required" unless modern?
|
|
@@ -123,21 +154,44 @@ module JsRoutes
|
|
|
123
154
|
|
|
124
155
|
sig { returns(String) }
|
|
125
156
|
def source_file
|
|
126
|
-
|
|
157
|
+
template = dts? ? "routes.d.ts" : "routes.js"
|
|
158
|
+
File.dirname(__FILE__) + "/../" + template
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
sig { returns(String) }
|
|
162
|
+
def router_source_file
|
|
163
|
+
template = dts? ? "router.d.ts" : "router.js"
|
|
164
|
+
File.dirname(__FILE__) + "/../" + template
|
|
127
165
|
end
|
|
128
166
|
|
|
129
167
|
sig { returns(Pathname) }
|
|
130
168
|
def output_file
|
|
169
|
+
output_file_path(file || default_file_name)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
protected
|
|
173
|
+
|
|
174
|
+
sig { params(file_name: FileName).returns(Pathname) }
|
|
175
|
+
def output_file_path(file_name)
|
|
131
176
|
shakapacker = JsRoutes::Utils.shakapacker
|
|
132
177
|
shakapacker_dir = shakapacker ?
|
|
133
|
-
shakapacker.config.source_path : pathname(
|
|
178
|
+
shakapacker.config.source_path : pathname(self.class.rails_javascript_path)
|
|
134
179
|
sprockets_dir = pathname('app','assets','javascripts')
|
|
135
|
-
file_name = file || default_file_name
|
|
136
180
|
sprockets_file = sprockets_dir.join(file_name)
|
|
137
181
|
webpacker_file = shakapacker_dir.join(file_name)
|
|
138
182
|
!Dir.exist?(shakapacker_dir) && defined?(::Sprockets) ? sprockets_file : webpacker_file
|
|
139
183
|
end
|
|
140
184
|
|
|
185
|
+
sig { returns(String) }
|
|
186
|
+
def self.rails_javascript_path
|
|
187
|
+
js_dir = if defined?(Rails) && Rails.application&.config&.respond_to?(:javascript_path)
|
|
188
|
+
Rails.application.config.javascript_path
|
|
189
|
+
else
|
|
190
|
+
"javascript"
|
|
191
|
+
end
|
|
192
|
+
"app/#{js_dir}"
|
|
193
|
+
end
|
|
194
|
+
|
|
141
195
|
protected
|
|
142
196
|
|
|
143
197
|
sig { void }
|
|
@@ -153,7 +207,7 @@ module JsRoutes
|
|
|
153
207
|
|
|
154
208
|
sig { returns(String) }
|
|
155
209
|
def default_file_name
|
|
156
|
-
dts? ? "routes.d.ts" : "routes.js"
|
|
210
|
+
dts? ? "routes.d.ts" : pkg? ? "router.js" : "routes.js"
|
|
157
211
|
end
|
|
158
212
|
|
|
159
213
|
sig {void}
|
|
@@ -166,6 +220,9 @@ module JsRoutes
|
|
|
166
220
|
if module_type != 'NIL' && namespace
|
|
167
221
|
raise "JsRoutes namespace option can only be used if module_type is nil"
|
|
168
222
|
end
|
|
223
|
+
if package && !esm?
|
|
224
|
+
raise "JsRoutes package option can only be used with ESM module type"
|
|
225
|
+
end
|
|
169
226
|
end
|
|
170
227
|
|
|
171
228
|
sig { returns(T.proc.returns(String)) }
|
|
@@ -11,11 +11,12 @@ class JsRoutes::Generators::Base < Rails::Generators::Base
|
|
|
11
11
|
protected
|
|
12
12
|
|
|
13
13
|
def application_js_path
|
|
14
|
+
js_dir = JsRoutes::Configuration.rails_javascript_path
|
|
14
15
|
[
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
16
|
+
"#{js_dir}/packs/application.ts",
|
|
17
|
+
"#{js_dir}/packs/application.js",
|
|
18
|
+
"#{js_dir}/controllers/application.ts",
|
|
19
|
+
"#{js_dir}/controllers/application.js",
|
|
19
20
|
].find do |path|
|
|
20
21
|
File.exist?(Rails.root.join(path))
|
|
21
22
|
end
|
data/lib/js_routes/instance.rb
CHANGED
|
@@ -28,29 +28,30 @@ module JsRoutes
|
|
|
28
28
|
application = T.unsafe(self.application)
|
|
29
29
|
if routes_from(application).empty?
|
|
30
30
|
if application.is_a?(Rails::Application)
|
|
31
|
-
if
|
|
31
|
+
if JsRoutes::Utils.rails_version >= Gem::Version.new("8.0.0")
|
|
32
32
|
T.unsafe(application).reload_routes_unless_loaded
|
|
33
33
|
else
|
|
34
34
|
T.unsafe(application).reload_routes!
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
|
-
content = File.read(@configuration.source_file)
|
|
39
38
|
|
|
40
|
-
unless @configuration.dts?
|
|
41
|
-
content = js_variables.inject(content) do |js, (key, value)|
|
|
42
|
-
js.gsub!("RubyVariables.#{key}", value.to_s) ||
|
|
43
|
-
raise("Missing key #{key} in JS template")
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
39
|
unless @configuration.module_type == "NIL"
|
|
47
|
-
banner +
|
|
40
|
+
banner + jsr + routes_export
|
|
48
41
|
else
|
|
49
|
-
|
|
42
|
+
# Strip the empty IMPORT_ROUTER statement (comment + semicolon) left after substitution
|
|
43
|
+
jsr.sub(/\A(\/\/[^\n]+\n)*;\n/, "")
|
|
50
44
|
end
|
|
51
45
|
|
|
52
46
|
end
|
|
53
47
|
|
|
48
|
+
sig {returns(String)}
|
|
49
|
+
def package
|
|
50
|
+
raise "Package generation requires module_type: 'PKG'" unless @configuration.pkg?
|
|
51
|
+
|
|
52
|
+
jsr
|
|
53
|
+
end
|
|
54
|
+
|
|
54
55
|
sig { returns(String) }
|
|
55
56
|
def banner
|
|
56
57
|
banner = @configuration.banner
|
|
@@ -83,6 +84,22 @@ module JsRoutes
|
|
|
83
84
|
end
|
|
84
85
|
end
|
|
85
86
|
|
|
87
|
+
sig { void }
|
|
88
|
+
def package!
|
|
89
|
+
raise "Package generation requires module_type: 'PKG'" unless @configuration.pkg?
|
|
90
|
+
|
|
91
|
+
file_path = Rails.root.join(@configuration.output_file)
|
|
92
|
+
source_code = package
|
|
93
|
+
|
|
94
|
+
# We don't need to rewrite file if it already exist and have same content.
|
|
95
|
+
# It helps asset pipeline or webpack understand that file wasn't changed.
|
|
96
|
+
return if File.exist?(file_path) && File.read(file_path) == source_code
|
|
97
|
+
|
|
98
|
+
File.open(file_path, 'w') do |f|
|
|
99
|
+
f.write source_code
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
86
103
|
sig { void }
|
|
87
104
|
def remove!
|
|
88
105
|
path = Rails.root.join(@configuration.output_file)
|
|
@@ -92,27 +109,91 @@ module JsRoutes
|
|
|
92
109
|
|
|
93
110
|
protected
|
|
94
111
|
|
|
112
|
+
ESM_MODULE_MARKER = /export \{\};\n?\z/
|
|
113
|
+
|
|
114
|
+
def read_js(path)
|
|
115
|
+
File.read(path).sub(ESM_MODULE_MARKER, "")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
sig { returns(String) }
|
|
119
|
+
def jsr
|
|
120
|
+
return pkg_jsr if @configuration.pkg?
|
|
121
|
+
|
|
122
|
+
if @configuration.dts?
|
|
123
|
+
return File.read(@configuration.router_source_file)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
content = read_js(@configuration.source_file)
|
|
127
|
+
|
|
128
|
+
js_variables.inject(content) do |js, (key, value)|
|
|
129
|
+
js.gsub!("RubyVariables.#{key}", value.to_s) ||
|
|
130
|
+
raise("Missing key #{key} in JS template")
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
sig { returns(String) }
|
|
135
|
+
def pkg_jsr
|
|
136
|
+
read_js(@configuration.router_source_file) + "export default Router;\n"
|
|
137
|
+
end
|
|
138
|
+
|
|
95
139
|
sig { returns(T::Hash[String, String]) }
|
|
96
140
|
def js_variables
|
|
141
|
+
warn_on_implicit_undefined_query_parameter_behavior
|
|
142
|
+
|
|
97
143
|
prefix = @configuration.prefix
|
|
98
144
|
prefix = prefix.call if prefix.is_a?(Proc)
|
|
99
145
|
{
|
|
100
146
|
'ROUTES_OBJECT' => routes_object,
|
|
101
|
-
'DEPRECATED_FALSE_PARAMETER_BEHAVIOR' =>
|
|
102
|
-
'DEPRECATED_NIL_QUERY_PARAMETER_BEHAVIOR' =>
|
|
147
|
+
'DEPRECATED_FALSE_PARAMETER_BEHAVIOR' => @configuration.deprecated_false_parameter_behavior,
|
|
148
|
+
'DEPRECATED_NIL_QUERY_PARAMETER_BEHAVIOR' => @configuration.deprecated_nil_query_parameter_behavior,
|
|
149
|
+
'INCLUDE_UNDEFINED_QUERY_PARAMETERS' => json(@configuration.include_undefined_query_parameters != false),
|
|
103
150
|
'DEFAULT_URL_OPTIONS' => json(@configuration.default_url_options),
|
|
104
151
|
'PREFIX' => json(prefix),
|
|
105
152
|
'SPECIAL_OPTIONS_KEY' => json(@configuration.special_options_key),
|
|
106
153
|
'SERIALIZER' => @configuration.serializer || json(nil),
|
|
107
154
|
'MODULE_TYPE' => json(@configuration.module_type),
|
|
108
155
|
'WRAPPER' => wrapper_variable,
|
|
156
|
+
"IMPORT_ROUTER" => import_router_variable,
|
|
157
|
+
"EMBED_ROUTER" => embed_router_variable,
|
|
109
158
|
}
|
|
110
159
|
end
|
|
111
160
|
|
|
161
|
+
sig { void }
|
|
162
|
+
def warn_on_implicit_undefined_query_parameter_behavior
|
|
163
|
+
return unless @configuration.include_undefined_query_parameters.nil?
|
|
164
|
+
|
|
165
|
+
JsRoutes::Utils.deprecator.warn(
|
|
166
|
+
"JsRoutes include_undefined_query_parameters is not configured. " \
|
|
167
|
+
"Set JsRoutes.setup { |c| c.include_undefined_query_parameters = false } " \
|
|
168
|
+
"to omit undefined query parameters, or set it to true to keep legacy nil serialization. " \
|
|
169
|
+
"The default will change to false in a future release."
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
sig { returns(String) }
|
|
174
|
+
def embed_router_variable
|
|
175
|
+
unless @configuration.use_package? || @configuration.modern?
|
|
176
|
+
read_js(@configuration.router_source_file)
|
|
177
|
+
else
|
|
178
|
+
""
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
sig { returns(String) }
|
|
183
|
+
def import_router_variable
|
|
184
|
+
if @configuration.use_package?
|
|
185
|
+
"import Router from '#{@configuration.package}'"
|
|
186
|
+
elsif @configuration.modern?
|
|
187
|
+
read_js(@configuration.router_source_file)
|
|
188
|
+
else
|
|
189
|
+
""
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
112
193
|
sig { returns(String) }
|
|
113
194
|
def wrapper_variable
|
|
114
195
|
case @configuration.module_type
|
|
115
|
-
when 'ESM'
|
|
196
|
+
when 'ESM', 'PKG'
|
|
116
197
|
'const __jsr = '
|
|
117
198
|
when 'NIL'
|
|
118
199
|
namespace = @configuration.namespace
|
|
@@ -148,7 +229,7 @@ module JsRoutes
|
|
|
148
229
|
|
|
149
230
|
sig { returns(String) }
|
|
150
231
|
def routes_object
|
|
151
|
-
return json({}) if @configuration.modern?
|
|
232
|
+
return json({}) if @configuration.modern? || @configuration.pkg?
|
|
152
233
|
properties = routes_list.map do |comment, name, body|
|
|
153
234
|
"#{comment}#{name}: #{body}".indent(2)
|
|
154
235
|
end
|
|
@@ -157,7 +238,7 @@ module JsRoutes
|
|
|
157
238
|
|
|
158
239
|
sig { returns(T::Array[StringArray]) }
|
|
159
240
|
def static_exports
|
|
160
|
-
[:configure, :config, :serialize].map do |name|
|
|
241
|
+
[:configure, :config, :serialize, :__route].map do |name|
|
|
161
242
|
[
|
|
162
243
|
"", name.to_s,
|
|
163
244
|
@configuration.dts? ?
|
|
@@ -175,16 +256,6 @@ module JsRoutes
|
|
|
175
256
|
end.join
|
|
176
257
|
end
|
|
177
258
|
|
|
178
|
-
sig { returns(String) }
|
|
179
|
-
def prevent_types_export
|
|
180
|
-
return "" unless @configuration.dts?
|
|
181
|
-
<<-JS
|
|
182
|
-
// By some reason this line prevents all types in a file
|
|
183
|
-
// from being automatically exported
|
|
184
|
-
export {};
|
|
185
|
-
JS
|
|
186
|
-
end
|
|
187
|
-
|
|
188
259
|
sig { returns(String) }
|
|
189
260
|
def export_separator
|
|
190
261
|
@configuration.dts? ? ': ' : ' = '
|
data/lib/js_routes/route.rb
CHANGED
|
@@ -10,6 +10,21 @@ module JsRoutes
|
|
|
10
10
|
|
|
11
11
|
FILTERED_DEFAULT_PARTS = T.let([:controller, :action].freeze, SymbolArray)
|
|
12
12
|
URL_OPTIONS = T.let([:protocol, :domain, :host, :port, :subdomain].freeze, SymbolArray)
|
|
13
|
+
|
|
14
|
+
# JavaScript reserved words that are invalid as function parameter names.
|
|
15
|
+
# @see https://www.w3schools.com/js/js_reserved.asp
|
|
16
|
+
JS_RESERVED_WORDS = T.let(%w[
|
|
17
|
+
abstract arguments async await boolean break byte case
|
|
18
|
+
catch char class const continue debugger default delete
|
|
19
|
+
do double else enum eval export extends false
|
|
20
|
+
final finally float for function goto if implements
|
|
21
|
+
import in instanceof int interface let long native
|
|
22
|
+
new null package private protected public return short
|
|
23
|
+
static super switch synchronized this throw throws transient
|
|
24
|
+
true try typeof using var void volatile while
|
|
25
|
+
with yield
|
|
26
|
+
].freeze, T::Array[String])
|
|
27
|
+
|
|
13
28
|
NODE_TYPES = T.let({
|
|
14
29
|
GROUP: 1,
|
|
15
30
|
CAT: 2,
|
|
@@ -55,11 +70,12 @@ module JsRoutes
|
|
|
55
70
|
if @configuration.dts?
|
|
56
71
|
definition_body
|
|
57
72
|
else
|
|
58
|
-
# For tree-shaking ESM, add a #__PURE__ comment informing js bundlers that the call
|
|
59
|
-
#
|
|
73
|
+
# For tree-shaking ESM, add a #__PURE__ comment informing js bundlers that the call has
|
|
74
|
+
# no side-effects (e.g. modifying global variables) and is safe to remove when unused.
|
|
60
75
|
# https://webpack.js.org/guides/tree-shaking/#clarifying-tree-shaking-and-sidyeeffects
|
|
61
76
|
pure_comment = @configuration.esm? ? '/*#__PURE__*/ ' : ''
|
|
62
|
-
|
|
77
|
+
call = '__route'
|
|
78
|
+
"#{pure_comment}#{call}(#{arguments(absolute).map{|a| json(a)}.join(', ')})"
|
|
63
79
|
end
|
|
64
80
|
end
|
|
65
81
|
|
|
@@ -67,7 +83,7 @@ module JsRoutes
|
|
|
67
83
|
def definition_body
|
|
68
84
|
options_type = optional_parts_type ? "#{optional_parts_type} & RouteOptions" : "RouteOptions"
|
|
69
85
|
predicate = @configuration.optional_definition_params ? '?' : ''
|
|
70
|
-
args = required_parts.map{|p| "#{
|
|
86
|
+
args = required_parts.map{|p| "#{format_param(p)}#{predicate}: RequiredRouteParameter"}
|
|
71
87
|
args << "options?: #{options_type}"
|
|
72
88
|
"((\n#{args.join(",\n").indent(2)}\n) => string) & RouteHelperExtras"
|
|
73
89
|
end
|
|
@@ -76,7 +92,7 @@ module JsRoutes
|
|
|
76
92
|
def optional_parts_type
|
|
77
93
|
return nil if optional_parts.empty?
|
|
78
94
|
@optional_parts_type ||= T.let(
|
|
79
|
-
"{" + optional_parts.map {|p| "#{p}?: OptionalRouteParameter"}.join(', ') + "}",
|
|
95
|
+
"{" + optional_parts.map {|p| "#{format_param(p)}?: OptionalRouteParameter"}.join(', ') + "}",
|
|
80
96
|
T.nilable(String)
|
|
81
97
|
)
|
|
82
98
|
end
|
|
@@ -117,8 +133,14 @@ module JsRoutes
|
|
|
117
133
|
|
|
118
134
|
sig { params(absolute: T::Boolean).returns(String) }
|
|
119
135
|
def helper_name(absolute)
|
|
120
|
-
|
|
121
|
-
|
|
136
|
+
apply_case(base_name, helper_suffix(absolute))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
sig { params(absolute: T::Boolean).returns(T.nilable(Symbol)) }
|
|
140
|
+
def helper_suffix(absolute)
|
|
141
|
+
return :url if absolute
|
|
142
|
+
return :path unless @configuration.compact
|
|
143
|
+
JS_RESERVED_WORDS.include?(apply_case(base_name)) ? :path : nil
|
|
122
144
|
end
|
|
123
145
|
|
|
124
146
|
sig { returns(String) }
|
|
@@ -173,7 +195,7 @@ JS
|
|
|
173
195
|
sig { returns(String) }
|
|
174
196
|
def documentation_params
|
|
175
197
|
required_parts.map do |param|
|
|
176
|
-
"\n * @param {any} #{
|
|
198
|
+
"\n * @param {any} #{format_param(param)}"
|
|
177
199
|
end.join
|
|
178
200
|
end
|
|
179
201
|
|
|
@@ -188,6 +210,16 @@ JS
|
|
|
188
210
|
@configuration.camel_case ? value.camelize(:lower) : value
|
|
189
211
|
end
|
|
190
212
|
|
|
213
|
+
sig { params(part: T.nilable(Literal)).returns(String) }
|
|
214
|
+
def format_param(part)
|
|
215
|
+
sanitize_param(apply_case(part))
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
sig { params(name: String).returns(String) }
|
|
219
|
+
def sanitize_param(name)
|
|
220
|
+
JS_RESERVED_WORDS.include?(name) ? "#{name}_" : name
|
|
221
|
+
end
|
|
222
|
+
|
|
191
223
|
# This function serializes Journey route into JSON structure
|
|
192
224
|
# We do not use Hash for human readable serialization
|
|
193
225
|
# And preffer Array serialization because it is shorter.
|
data/lib/js_routes/utils.rb
CHANGED
|
@@ -16,12 +16,17 @@ module JsRoutes
|
|
|
16
16
|
|
|
17
17
|
sig { returns(T.untyped) }
|
|
18
18
|
def self.deprecator
|
|
19
|
-
if defined?(Rails) && Rails.
|
|
19
|
+
if defined?(Rails) && Rails.respond_to?(:deprecator)
|
|
20
20
|
Rails.deprecator
|
|
21
21
|
else
|
|
22
22
|
ActiveSupport::Deprecation
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
sig { returns(Gem::Version) }
|
|
27
|
+
def self.rails_version
|
|
28
|
+
Gem::Version.new(Rails.version)
|
|
29
|
+
end
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
end
|
data/lib/js_routes/version.rb
CHANGED