js-routes 2.2.0 → 2.2.3
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 +18 -0
- data/Readme.md +43 -6
- data/lib/js_routes/configuration.rb +111 -0
- data/lib/js_routes/engine.rb +28 -26
- data/lib/js_routes/generators/middleware.rb +29 -4
- data/lib/js_routes/instance.rb +173 -0
- data/lib/js_routes/middleware.rb +1 -1
- data/lib/js_routes/route.rb +172 -0
- data/lib/js_routes/version.rb +2 -2
- data/lib/js_routes.rb +8 -423
- data/lib/routes.d.ts +0 -1
- data/lib/routes.js +9 -25
- data/lib/routes.ts +9 -30
- data/spec/js_routes/module_types/dts/routes.spec.d.ts +0 -1
- data/spec/js_routes/module_types/nil_spec.rb +1 -0
- data/spec/js_routes/options_spec.rb +6 -3
- data/spec/js_routes/rails_routes_compatibility_spec.rb +4 -1
- data/spec/spec_helper.rb +12 -4
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b706c51040d12aaa69182dad9d25304ba947fe1e80cc5171775864a27d6f4ed8
|
4
|
+
data.tar.gz: 1a105e8a4dbc9cfde5a787a0714b6e0bf0758b6291f6945f0a16ca9c1e7a47a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f053d1f48cb8a4f9f7ac7f1e68d37f4816d09e42e0a91660fb84c1e9f4ad36d439019f6f402f1e2fce30467e6b0d5b799fd08bbe70bfc2aa8423c63f053f0bdb
|
7
|
+
data.tar.gz: bdcf72700542350da423e9cf62b4abcc0ba326400c4077c70c5f6174edb75a7e61b6b6a765a0454b4cd1a2fc5ddbf91f3a16294ce995d9ab31d9984153c29b4c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
## master
|
2
2
|
|
3
|
+
## v2.2.4
|
4
|
+
|
5
|
+
* Fix rails engine loading if sprockets is not in Gemfile. Fixes [#294](https://github.com/railsware/js-routes/issues/294)
|
6
|
+
|
7
|
+
## v2.2.3
|
8
|
+
|
9
|
+
* Fixed NIL module type namespace defintion [#297](https://github.com/railsware/js-routes/issues/297).
|
10
|
+
* The patch may cause a problem with nested `namespace` option
|
11
|
+
* Ex. Value like `MyProject.Routes` requires to define `window.MyProject` before importing the routes file
|
12
|
+
|
13
|
+
## v2.2.2.
|
14
|
+
|
15
|
+
* Fix custom file path [#295](https://github.com/railsware/js-routes/issues/295)
|
16
|
+
|
17
|
+
## v2.2.1
|
18
|
+
|
19
|
+
* Improve generator to update route files on `assets:precompile` and add them to `.gitignore by default` [#288](https://github.com/railsware/js-routes/issues/288#issuecomment-1012182815)
|
20
|
+
|
3
21
|
## v2.2.0
|
4
22
|
|
5
23
|
* Use Rack Middleware to automatically update routes file in development [#288](https://github.com/railsware/js-routes/issues/288)
|
data/Readme.md
CHANGED
@@ -16,12 +16,13 @@ gem "js-routes"
|
|
16
16
|
|
17
17
|
## Setup
|
18
18
|
|
19
|
-
There are
|
19
|
+
There are several possible ways to setup JsRoutes:
|
20
20
|
|
21
21
|
* [Quick and easy](#quick-start)
|
22
22
|
* Uses Rack Middleware to automatically update routes locally
|
23
|
+
* Automatically generates routes files on `assets:precompile` in production
|
23
24
|
* Works great for a simple Rails application
|
24
|
-
* [Webpacker](#webpacker)
|
25
|
+
* [Webpacker ERB Loader](#webpacker)
|
25
26
|
* Requires ESM module system (the default)
|
26
27
|
* Doesn't support typescript definitions
|
27
28
|
* [Advanced Setup](#advanced-setup)
|
@@ -33,18 +34,52 @@ There are 3 possible ways to setup JsRoutes:
|
|
33
34
|
|
34
35
|
### Quick Start
|
35
36
|
|
36
|
-
Setup [Rack Middleware](https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack)
|
37
|
+
Setup [Rack Middleware](https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack)
|
38
|
+
to automatically generate and maintain `routes.js` file and corresponding
|
39
|
+
[Typescript definitions](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html) `routes.d.ts`:
|
37
40
|
|
38
|
-
|
41
|
+
#### Use a Generator
|
42
|
+
|
43
|
+
Run a command:
|
39
44
|
|
40
45
|
``` sh
|
41
46
|
rails generate js_routes:middleware
|
42
47
|
```
|
43
48
|
|
49
|
+
#### Setup Manually
|
50
|
+
|
51
|
+
Add the following to `config/environments/development.rb`:
|
52
|
+
|
53
|
+
``` ruby
|
54
|
+
config.middleware.use(JsRoutes::Middleware)
|
55
|
+
```
|
56
|
+
|
57
|
+
Use it in `app/javascript/packs/application.js`:
|
58
|
+
|
59
|
+
``` javascript
|
60
|
+
import * as Routes from '../routes';
|
61
|
+
// window.Routes = Routes;
|
62
|
+
alert(Routes.post_path(1))
|
63
|
+
```
|
64
|
+
|
65
|
+
Upgrade `rake assets:precompile` to update js-routes files:
|
66
|
+
|
67
|
+
``` ruby
|
68
|
+
namespace :assets do
|
69
|
+
task :precompile => "js:routes:typescript"
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
Add js-routes files to `.gitignore`:
|
74
|
+
|
75
|
+
```
|
76
|
+
/app/javascript/routes.js
|
77
|
+
/app/javascript/routes.d.ts
|
78
|
+
```
|
44
79
|
|
45
80
|
<div id='webpacker'></div>
|
46
81
|
|
47
|
-
### Webpacker
|
82
|
+
### Webpacker ERB loader
|
48
83
|
|
49
84
|
**IMPORTANT**: this setup doesn't support IDE autocompletion with [Typescript](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html)
|
50
85
|
|
@@ -111,7 +146,7 @@ window.Routes = Routes;
|
|
111
146
|
|
112
147
|
**IMPORTANT**: that this setup requires the JS routes file to be updates manually
|
113
148
|
|
114
|
-
|
149
|
+
Routes file can be generated with a `rake` task:
|
115
150
|
|
116
151
|
``` sh
|
117
152
|
rake js:routes
|
@@ -122,6 +157,8 @@ rake js:routes:typescript
|
|
122
157
|
In case you need multiple route files for different parts of your application, you have to create the files manually.
|
123
158
|
If your application has an `admin` and an `application` namespace for example:
|
124
159
|
|
160
|
+
**IMPORTANT**: Requires [Webpacker ERB Loader](#webpacker) setup.
|
161
|
+
|
125
162
|
``` erb
|
126
163
|
// app/javascript/admin/routes.js.erb
|
127
164
|
<%= JsRoutes.generate(include: /admin/) %>
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module JsRoutes
|
4
|
+
class Configuration
|
5
|
+
DEFAULTS = {
|
6
|
+
namespace: nil,
|
7
|
+
exclude: [],
|
8
|
+
include: //,
|
9
|
+
file: nil,
|
10
|
+
prefix: -> { Rails.application.config.relative_url_root || "" },
|
11
|
+
url_links: false,
|
12
|
+
camel_case: false,
|
13
|
+
default_url_options: {},
|
14
|
+
compact: false,
|
15
|
+
serializer: nil,
|
16
|
+
special_options_key: "_options",
|
17
|
+
application: -> { Rails.application },
|
18
|
+
module_type: 'ESM',
|
19
|
+
documentation: true,
|
20
|
+
} #:nodoc:
|
21
|
+
|
22
|
+
attr_accessor(*DEFAULTS.keys)
|
23
|
+
|
24
|
+
def initialize(attributes = nil)
|
25
|
+
assign(DEFAULTS)
|
26
|
+
return unless attributes
|
27
|
+
assign(attributes)
|
28
|
+
end
|
29
|
+
|
30
|
+
def assign(attributes = nil, &block)
|
31
|
+
if !attributes && !block
|
32
|
+
raise "Provide attributes or block"
|
33
|
+
end
|
34
|
+
tap(&block) if block
|
35
|
+
if attributes
|
36
|
+
attributes.each do |attribute, value|
|
37
|
+
value = value.call if value.is_a?(Proc)
|
38
|
+
send(:"#{attribute}=", value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
normalize_and_verify
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](attribute)
|
46
|
+
send(attribute)
|
47
|
+
end
|
48
|
+
|
49
|
+
def merge(attributes)
|
50
|
+
clone.assign(attributes)
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_hash
|
54
|
+
Hash[*members.zip(values).flatten(1)].symbolize_keys
|
55
|
+
end
|
56
|
+
|
57
|
+
def esm?
|
58
|
+
module_type === 'ESM'
|
59
|
+
end
|
60
|
+
|
61
|
+
def dts?
|
62
|
+
self.module_type === 'DTS'
|
63
|
+
end
|
64
|
+
|
65
|
+
def modern?
|
66
|
+
esm? || dts?
|
67
|
+
end
|
68
|
+
|
69
|
+
def require_esm
|
70
|
+
raise "ESM module type is required" unless modern?
|
71
|
+
end
|
72
|
+
|
73
|
+
def source_file
|
74
|
+
File.dirname(__FILE__) + "/../" + default_file_name
|
75
|
+
end
|
76
|
+
|
77
|
+
def output_file
|
78
|
+
webpacker_dir = pathname('app', 'javascript')
|
79
|
+
sprockets_dir = pathname('app','assets','javascripts')
|
80
|
+
file_name = file || default_file_name
|
81
|
+
sprockets_file = sprockets_dir.join(file_name)
|
82
|
+
webpacker_file = webpacker_dir.join(file_name)
|
83
|
+
!Dir.exist?(webpacker_dir) && defined?(::Sprockets) ? sprockets_file : webpacker_file
|
84
|
+
end
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
def normalize_and_verify
|
89
|
+
normalize
|
90
|
+
verify
|
91
|
+
end
|
92
|
+
|
93
|
+
def pathname(*parts)
|
94
|
+
Pathname.new(File.join(*parts))
|
95
|
+
end
|
96
|
+
|
97
|
+
def default_file_name
|
98
|
+
dts? ? "routes.d.ts" : "routes.js"
|
99
|
+
end
|
100
|
+
|
101
|
+
def normalize
|
102
|
+
self.module_type = module_type&.upcase || 'NIL'
|
103
|
+
end
|
104
|
+
|
105
|
+
def verify
|
106
|
+
if module_type != 'NIL' && namespace
|
107
|
+
raise "JsRoutes namespace option can only be used if module_type is nil"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/js_routes/engine.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
module JsRoutes
|
2
2
|
class SprocketsExtension
|
3
3
|
def initialize(filename, &block)
|
4
4
|
@filename = filename
|
@@ -29,32 +29,34 @@ end
|
|
29
29
|
|
30
30
|
|
31
31
|
class Engine < ::Rails::Engine
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
{
|
39
|
-
|
40
|
-
{
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
config.
|
52
|
-
|
53
|
-
|
54
|
-
|
32
|
+
if defined?(::Sprockets::Railtie)
|
33
|
+
require 'sprockets/version'
|
34
|
+
v2 = Gem::Dependency.new('', ' ~> 2')
|
35
|
+
vgte3 = Gem::Dependency.new('', ' >= 3')
|
36
|
+
sprockets_version = Gem::Version.new(::Sprockets::VERSION).release
|
37
|
+
initializer_args = case sprockets_version
|
38
|
+
when -> (v) { v2.match?('', v) }
|
39
|
+
{ after: "sprockets.environment" }
|
40
|
+
when -> (v) { vgte3.match?('', v) }
|
41
|
+
{ after: :engines_blank_point, before: :finisher_hook }
|
42
|
+
else
|
43
|
+
raise StandardError, "Sprockets version #{sprockets_version} is not supported"
|
44
|
+
end
|
45
|
+
|
46
|
+
initializer 'js-routes.dependent_on_routes', initializer_args do
|
47
|
+
case sprockets_version
|
48
|
+
when -> (v) { v2.match?('', v) },
|
49
|
+
-> (v) { vgte3.match?('', v) }
|
50
|
+
|
51
|
+
Rails.application.config.assets.configure do |config|
|
52
|
+
config.register_preprocessor(
|
53
|
+
"application/javascript",
|
54
|
+
SprocketsExtension,
|
55
|
+
)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise StandardError, "Sprockets version #{sprockets_version} is not supported"
|
55
59
|
end
|
56
|
-
else
|
57
|
-
raise StandardError, "Sprockets version #{sprockets_version} is not supported"
|
58
60
|
end
|
59
61
|
end
|
60
62
|
end
|
@@ -6,11 +6,12 @@ class JsRoutes::Generators::Middleware < Rails::Generators::Base
|
|
6
6
|
|
7
7
|
def create_middleware
|
8
8
|
copy_file "initializer.rb", "config/initializers/js_routes.rb"
|
9
|
-
# copy_file "erb.js", "config/webpack/loaders/erb.js"
|
10
|
-
# copy_file "routes.js.erb", "app/javascript/routes.js.erb"
|
11
|
-
# inject_into_file "config/webpack/environment.js", loader_content
|
12
9
|
inject_into_file "app/javascript/packs/application.js", pack_content
|
13
10
|
inject_into_file "config/environments/development.rb", middleware_content, before: /^end\n\z/
|
11
|
+
inject_into_file "Rakefile", rakefile_content
|
12
|
+
inject_into_file ".gitignore", gitignore_content
|
13
|
+
JsRoutes.generate!
|
14
|
+
JsRoutes.definitions!
|
14
15
|
end
|
15
16
|
|
16
17
|
protected
|
@@ -25,9 +26,33 @@ window.Routes = Routes;
|
|
25
26
|
def middleware_content
|
26
27
|
<<-RB
|
27
28
|
|
28
|
-
# Automatically update routes
|
29
|
+
# Automatically update js-routes file
|
29
30
|
# when routes.rb is changed
|
30
31
|
config.middleware.use(JsRoutes::Middleware)
|
31
32
|
RB
|
32
33
|
end
|
34
|
+
|
35
|
+
def rakefile_content
|
36
|
+
<<-RB
|
37
|
+
|
38
|
+
# Update js-routes file before assets precompile
|
39
|
+
namespace :assets do
|
40
|
+
task :precompile => "js:routes:typescript"
|
41
|
+
end
|
42
|
+
RB
|
43
|
+
end
|
44
|
+
|
45
|
+
def gitignore_content
|
46
|
+
banner = <<-TXT
|
47
|
+
|
48
|
+
# Ignore automatically generated js-routes files.
|
49
|
+
TXT
|
50
|
+
|
51
|
+
banner + [
|
52
|
+
{},
|
53
|
+
{module_type: 'DTS'}
|
54
|
+
].map do |config|
|
55
|
+
File.join('/', JsRoutes.new(config).configuration.output_file) + "\n"
|
56
|
+
end.join
|
57
|
+
end
|
33
58
|
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require "js_routes/configuration"
|
2
|
+
require "js_routes/route"
|
3
|
+
|
4
|
+
module JsRoutes
|
5
|
+
class Instance
|
6
|
+
|
7
|
+
attr_reader :configuration
|
8
|
+
#
|
9
|
+
# Implementation
|
10
|
+
#
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
@configuration = JsRoutes.configuration.merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate
|
17
|
+
# Ensure routes are loaded. If they're not, load them.
|
18
|
+
if named_routes.empty? && application.respond_to?(:reload_routes!)
|
19
|
+
application.reload_routes!
|
20
|
+
end
|
21
|
+
content = File.read(@configuration.source_file)
|
22
|
+
|
23
|
+
if !@configuration.dts?
|
24
|
+
content = js_variables.inject(content) do |js, (key, value)|
|
25
|
+
js.gsub!("RubyVariables.#{key}", value.to_s) ||
|
26
|
+
raise("Missing key #{key} in JS template")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
content + routes_export + prevent_types_export
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate!
|
33
|
+
# Some libraries like Devise did not load their routes yet
|
34
|
+
# so we will wait until initialization process finishes
|
35
|
+
# https://github.com/railsware/js-routes/issues/7
|
36
|
+
Rails.configuration.after_initialize do
|
37
|
+
file_path = Rails.root.join(@configuration.output_file)
|
38
|
+
source_code = generate
|
39
|
+
|
40
|
+
# We don't need to rewrite file if it already exist and have same content.
|
41
|
+
# It helps asset pipeline or webpack understand that file wasn't changed.
|
42
|
+
next if File.exist?(file_path) && File.read(file_path) == source_code
|
43
|
+
|
44
|
+
File.open(file_path, 'w') do |f|
|
45
|
+
f.write source_code
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def js_variables
|
53
|
+
{
|
54
|
+
'GEM_VERSION' => JsRoutes::VERSION,
|
55
|
+
'ROUTES_OBJECT' => routes_object,
|
56
|
+
'RAILS_VERSION' => ActionPack.version,
|
57
|
+
'DEPRECATED_GLOBBING_BEHAVIOR' => ActionPack::VERSION::MAJOR == 4 && ActionPack::VERSION::MINOR == 0,
|
58
|
+
|
59
|
+
'APP_CLASS' => application.class.to_s,
|
60
|
+
'DEFAULT_URL_OPTIONS' => json(@configuration.default_url_options),
|
61
|
+
'PREFIX' => json(@configuration.prefix),
|
62
|
+
'SPECIAL_OPTIONS_KEY' => json(@configuration.special_options_key),
|
63
|
+
'SERIALIZER' => @configuration.serializer || json(nil),
|
64
|
+
'MODULE_TYPE' => json(@configuration.module_type),
|
65
|
+
'WRAPPER' => wrapper_variable,
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def wrapper_variable
|
70
|
+
case @configuration.module_type
|
71
|
+
when 'ESM'
|
72
|
+
'const __jsr = '
|
73
|
+
when 'NIL'
|
74
|
+
namespace = @configuration.namespace
|
75
|
+
if namespace
|
76
|
+
if namespace.include?('.')
|
77
|
+
"#{namespace} = "
|
78
|
+
else
|
79
|
+
"(typeof window !== 'undefined' ? window : this).#{namespace} = "
|
80
|
+
end
|
81
|
+
else
|
82
|
+
''
|
83
|
+
end
|
84
|
+
else
|
85
|
+
''
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def application
|
90
|
+
@configuration.application
|
91
|
+
end
|
92
|
+
|
93
|
+
def json(string)
|
94
|
+
JsRoutes.json(string)
|
95
|
+
end
|
96
|
+
|
97
|
+
def named_routes
|
98
|
+
application.routes.named_routes.to_a
|
99
|
+
end
|
100
|
+
|
101
|
+
def routes_object
|
102
|
+
return json({}) if @configuration.modern?
|
103
|
+
properties = routes_list.map do |comment, name, body|
|
104
|
+
"#{comment}#{name}: #{body}".indent(2)
|
105
|
+
end
|
106
|
+
"{\n" + properties.join(",\n\n") + "}\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
def static_exports
|
110
|
+
[:configure, :config, :serialize].map do |name|
|
111
|
+
[
|
112
|
+
"", name,
|
113
|
+
@configuration.dts? ?
|
114
|
+
"RouterExposedMethods['#{name}']" :
|
115
|
+
"__jsr.#{name}"
|
116
|
+
]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def routes_export
|
121
|
+
return "" unless @configuration.modern?
|
122
|
+
[*static_exports, *routes_list].map do |comment, name, body|
|
123
|
+
"#{comment}export const #{name}#{export_separator}#{body};\n\n"
|
124
|
+
end.join
|
125
|
+
end
|
126
|
+
|
127
|
+
def prevent_types_export
|
128
|
+
return "" unless @configuration.dts?
|
129
|
+
<<-JS
|
130
|
+
// By some reason this line prevents all types in a file
|
131
|
+
// from being automatically exported
|
132
|
+
export {};
|
133
|
+
JS
|
134
|
+
end
|
135
|
+
|
136
|
+
def export_separator
|
137
|
+
@configuration.dts? ? ': ' : ' = '
|
138
|
+
end
|
139
|
+
|
140
|
+
def routes_list
|
141
|
+
named_routes.sort_by(&:first).flat_map do |_, route|
|
142
|
+
route_helpers_if_match(route) + mounted_app_routes(route)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def mounted_app_routes(route)
|
147
|
+
rails_engine_app = app_from_route(route)
|
148
|
+
if rails_engine_app.respond_to?(:superclass) &&
|
149
|
+
rails_engine_app.superclass == Rails::Engine && !route.path.anchored
|
150
|
+
rails_engine_app.routes.named_routes.flat_map do |_, engine_route|
|
151
|
+
route_helpers_if_match(engine_route, route)
|
152
|
+
end
|
153
|
+
else
|
154
|
+
[]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def app_from_route(route)
|
159
|
+
app = route.app
|
160
|
+
# rails engine in Rails 4.2 use additional
|
161
|
+
# ActionDispatch::Routing::Mapper::Constraints, which contain app
|
162
|
+
if app.respond_to?(:app) && app.respond_to?(:constraints)
|
163
|
+
app.app
|
164
|
+
else
|
165
|
+
app
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def route_helpers_if_match(route, parent_route = nil)
|
170
|
+
Route.new(@configuration, route, parent_route).helpers
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/lib/js_routes/middleware.rb
CHANGED