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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 315ddd617bc6630cf5858afc5ba38d2c204f6167a7984ccdaf7171a50404e418
4
- data.tar.gz: 676d2d36107ee524c07517143499173c0658731cc06d1c52753e3ab2f4e6cb81
3
+ metadata.gz: b706c51040d12aaa69182dad9d25304ba947fe1e80cc5171775864a27d6f4ed8
4
+ data.tar.gz: 1a105e8a4dbc9cfde5a787a0714b6e0bf0758b6291f6945f0a16ca9c1e7a47a9
5
5
  SHA512:
6
- metadata.gz: 6c7f715e9afd02d1daebb882381e328595beca59a6a6d8c10a3a1518801522090e986c6b362174aaa9c51faf91508ee8e1cc6fff8ba118a08c32eb26c7205ba1
7
- data.tar.gz: 7f70bfa95a1fb42092f20118bb3f22f07e459ccc6f95b8d3b0651faf0457266152465873da6a4dfddc4d52df006d6f5b991e3608a083c1eb524fef5ed77ff0c8
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 3 possible ways to setup JsRoutes:
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) automatic updates
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) to automatically generate and maintain `routes.js` file and corresponding [Typescript definitions](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html) `routes.d.ts`:
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
- Run:
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 + automatic updates - Typescript
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
- You can run every time you want to generate a routes file:
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
@@ -1,4 +1,4 @@
1
- class JsRoutes
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
- require 'sprockets/version'
33
- v2 = Gem::Dependency.new('', ' ~> 2')
34
- vgte3 = Gem::Dependency.new('', ' >= 3')
35
- sprockets_version = Gem::Version.new(::Sprockets::VERSION).release
36
- initializer_args = case sprockets_version
37
- when -> (v) { v2.match?('', v) }
38
- { after: "sprockets.environment" }
39
- when -> (v) { vgte3.match?('', v) }
40
- { after: :engines_blank_point, before: :finisher_hook }
41
- else
42
- raise StandardError, "Sprockets version #{sprockets_version} is not supported"
43
- end
44
-
45
- initializer 'js-routes.dependent_on_routes', initializer_args do
46
- case sprockets_version
47
- when -> (v) { v2.match?('', v) },
48
- -> (v) { vgte3.match?('', v) }
49
-
50
- Rails.application.config.assets.configure do |config|
51
- config.register_preprocessor(
52
- "application/javascript",
53
- SprocketsExtension,
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.js file
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
@@ -1,4 +1,4 @@
1
- class JsRoutes
1
+ module JsRoutes
2
2
  # A Rack middleware that automatically updates routes file
3
3
  # whenever routes.rb is modified
4
4
  #