js_from_routes 4.1.0 → 5.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f4bdf584f20e9d501c07a584faaf0d9a592794c0ef0b48b7c687a302fc7b41f
4
- data.tar.gz: 440fa7063465c567ac001198be8d7024b85bd726bd614c5b5a7f9b6be4320527
3
+ metadata.gz: 1c5e43fa00d907a28931b13ec417afb2d7ef602fdb1b9c47ccd9e00289b305ec
4
+ data.tar.gz: 4bf4c33ffd5adf831f940f82d51e68af66cc7be9199c5fcbcd957ab4cf2858be
5
5
  SHA512:
6
- metadata.gz: 73b25326be13bf7bd2ad3294a967bcc7f044696ab8870dd601fe3bcb173ffa14f859e5c82488959a2bc25d15f301a775ee0677d727e2a767c10f1e19e2b88bc3
7
- data.tar.gz: 38115d06a1f4dbc8d6cea476418215b379266d0fbae5a7b14234b916f42d2cb6ec625ac98ed7a02b46a98aebcbb73a1f883eb6a3745a57f2f27d373759c5bffc
6
+ metadata.gz: d55050b0f4b4168db33c7db98fa51323fb469e5b8ffd0f484da07860d6834d1a23db1537fcb05414422ec14d344f31fa15f784aac33d283b5365baa45caebda5
7
+ data.tar.gz: fa99b4558aae4ad1e91fc6ab4792b058f928232565f521c98e32344bd304e548fc8cfcac873e31c13ca1be06a170bbc9a3d13db8ddc88cc89eacfcf367e4612d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ # [5.0.0](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@4.1.0...js_from_routes@5.0.0) (2026-10-19)
2
+
3
+
4
+ ### Features
5
+
6
+ * remove cache-key headers and watch route changes ([#72](https://github.com/ElMassimo/js_from_routes/issues/72)) ([2d34fc1](https://github.com/ElMassimo/js_from_routes/commit/2d34fc1015157339ce9c39bd0c30cf30a34abdb1))
7
+
8
+
9
+
10
+ # [5.0.0](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@4.1.0...js_from_routes@5.0.0) (2026-10-17)
11
+
12
+
13
+ ### BREAKING CHANGES
14
+
15
+ * requires Rails 7.2 or newer, which is what CI has been testing against. The development reloader relies on `RoutesReloader#external_routes` and `config.file_watcher`.
16
+
17
+
18
+ ### Features
19
+
20
+ * generated files no longer start with a `// JsFromRoutes CacheKey` header. Files are compared by content and only written when they changed, which removes a frequent source of merge conflicts. Run `bin/rake js_from_routes:generate` once after upgrading to drop the header from existing files.
21
+ * in development, helpers are generated at boot and then only when a routes file or a template changes, instead of on every reload.
22
+ * `JS_FROM_ROUTES_FORCE` is no longer needed and has been removed.
23
+
24
+
1
25
  # [4.1.0](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@4.0.2...js_from_routes@4.1.0) (2026-08-03)
2
26
 
3
27
 
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "digest"
4
3
  require "erubi"
5
4
  require "fileutils"
6
- require "pathname"
7
5
 
8
6
  # Public: Automatically generates JS for Rails routes with { export: true }.
9
7
  # Generates one file per controller, and one function per route.
@@ -24,12 +22,6 @@ module JsFromRoutes
24
22
  }
25
23
  end
26
24
 
27
- # Public: Used to check whether the file should be generated again, changes
28
- # based on the configuration, and route definition.
29
- def cache_key
30
- routes.map(&:inspect).join + [File.read(@config.template_path), @config.helper_mappings.inspect, @config.client_library].join
31
- end
32
-
33
25
  # Public: Exposes the preferred import library to the generator.
34
26
  def client_library
35
27
  @config.client_library
@@ -87,7 +79,7 @@ module JsFromRoutes
87
79
  @mappings.fetch(helper, helper)
88
80
  end
89
81
 
90
- # Internal: Useful as a cache key for the route, and for debugging purposes.
82
+ # Internal: Useful for debugging purposes.
91
83
  def inspect
92
84
  "#{verb} #{helper} #{path}"
93
85
  end
@@ -100,10 +92,10 @@ module JsFromRoutes
100
92
  @compiled_template = Erubi::Engine.new(File.read(template_path), filename: template_path).src
101
93
  end
102
94
 
103
- # Public: Checks if the cache is fresh, or renders the template with the
104
- # specified variables, and writes the updated result to a file.
95
+ # Public: Renders the template with the specified variables, and writes the
96
+ # result to a file unless it already has the same content.
105
97
  def write_if_changed(object)
106
- write_file_if_changed(object.filename, object.cache_key) { render_template(object) }
98
+ write_file_if_changed(object.filename) { render_template(object) }
107
99
  end
108
100
 
109
101
  private
@@ -113,25 +105,16 @@ module JsFromRoutes
113
105
  object.instance_eval(@compiled_template)
114
106
  end
115
107
 
116
- # Internal: Returns true if the cache key has changed since the last codegen.
117
- def stale?(file, cache_key_comment)
118
- ENV["JS_FROM_ROUTES_FORCE"] || file.gets != cache_key_comment
119
- end
120
-
121
- # Internal: Writes if the file does not exist or the cache key has changed.
122
- # The cache strategy consists of a comment on the first line of the file.
108
+ # Internal: Writes the file unless it already has the same content, which
109
+ # keeps mtimes stable so file watchers don't react to no-op generations.
123
110
  #
124
- # Yields to receive the rendered file content when it needs to.
125
- def write_file_if_changed(name, cache_key)
111
+ # Yields to receive the rendered file content.
112
+ def write_file_if_changed(name)
113
+ content = yield
114
+ return if name.exist? && name.read == content
115
+
126
116
  FileUtils.mkdir_p(name.dirname)
127
- cache_key_comment = "// JsFromRoutes CacheKey #{Digest::MD5.hexdigest(cache_key)}\n"
128
- File.open(name, "a+") { |file|
129
- if stale?(file, cache_key_comment)
130
- file.truncate(0)
131
- file.write(cache_key_comment)
132
- file.write(yield)
133
- end
134
- }
117
+ name.write(content)
135
118
  end
136
119
  end
137
120
 
@@ -155,10 +138,9 @@ module JsFromRoutes
155
138
  end
156
139
 
157
140
  class TemplateConfig
158
- attr_reader :cache_key, :filename, :helpers
141
+ attr_reader :filename, :helpers
159
142
 
160
- def initialize(cache_key:, filename:, helpers: nil)
161
- @cache_key = cache_key
143
+ def initialize(filename:, helpers: nil)
162
144
  @filename = filename
163
145
  @helpers = helpers
164
146
  end
@@ -211,12 +193,10 @@ module JsFromRoutes
211
193
  index_file = (config.all_helpers_file == true) ? "index#{preferred_extension}" : config.all_helpers_file
212
194
 
213
195
  Template.new(config.template_all_path).write_if_changed TemplateConfig.new(
214
- cache_key: routes.map(&:import_filename).join + File.read(config.template_all_path),
215
196
  filename: config.output_folder.join("all#{preferred_extension}"),
216
197
  helpers: routes,
217
198
  )
218
199
  Template.new(config.template_index_path).write_if_changed TemplateConfig.new(
219
- cache_key: File.read(config.template_index_path),
220
200
  filename: config.output_folder.join(index_file),
221
201
  )
222
202
  end
@@ -277,9 +257,45 @@ module JsFromRoutes
277
257
  # mixed usage working: adding a named instance never silently disables the
278
258
  # global config that an existing app already relies on.
279
259
  def generate!(app_or_routes = Rails.application)
260
+ runnable_instances.each { |inst| inst.generate!(app_or_routes) }
261
+ end
262
+
263
+ # Public: Generates code on the first call, and afterwards only when a
264
+ # routes file or a template changed, so that reloads unrelated to routes
265
+ # don't pay for iterating every route.
266
+ def generate_if_changed(app = Rails.application)
267
+ watcher(app).execute_if_updated
268
+ end
269
+
270
+ # Public: Watches the files Rails loads routes from, and the templates.
271
+ # Generates once when created, so that helpers are fresh after boot.
272
+ #
273
+ # Registered as a Rails reloader by the railtie, so that editing a
274
+ # template triggers a reload even when no autoloaded file changed.
275
+ def watcher(app = Rails.application)
276
+ @watcher ||= app.config.file_watcher.new(watched_files(app), watched_dirs(app)) { generate!(app) }
277
+ .tap { generate!(app) }
278
+ end
279
+
280
+ private
281
+
282
+ def runnable_instances
280
283
  runnable = instances.values
281
284
  runnable.unshift(Instance.new(:default, config)) if @config_customized || runnable.empty?
282
- runnable.each { |inst| inst.generate!(app_or_routes) }
285
+ runnable
286
+ end
287
+
288
+ # Internal: The files Rails loads routes from, plus the templates, which
289
+ # also determine the generated output.
290
+ def watched_files(app)
291
+ templates = runnable_instances.flat_map { |inst|
292
+ [inst.config.template_path, inst.config.template_all_path, inst.config.template_index_path]
293
+ }
294
+ (app.routes_reloader.paths + templates).map(&:to_s).uniq
295
+ end
296
+
297
+ def watched_dirs(app)
298
+ app.routes_reloader.external_routes.to_h { |dir| [dir.to_s, %w[rb]] }
283
299
  end
284
300
  end
285
301
  end
@@ -10,7 +10,11 @@ class JsFromRoutes::Railtie < Rails::Railtie
10
10
  # Allows to automatically trigger code generation after updating routes.
11
11
  initializer "js_from_routes.reloader" do |app|
12
12
  app.config.to_prepare do
13
- JsFromRoutes.generate!(app)
13
+ JsFromRoutes.generate_if_changed(app)
14
+ end
15
+
16
+ app.config.after_initialize do
17
+ app.reloaders << JsFromRoutes.watcher(app)
14
18
  end
15
19
  end
16
20
  end
@@ -1,4 +1,3 @@
1
- //
2
1
  // DO NOT MODIFY: This file was automatically generated by JsFromRoutes.
3
2
  import { definePathHelper } from '<%= client_library %>'
4
3
 
@@ -1,4 +1,3 @@
1
- //
2
1
  // DO NOT MODIFY: This file was automatically generated by JsFromRoutes.
3
2
  <% helpers.each do |helper| %>
4
3
  export { default as <%= helper.js_name %> } from './<%= helper.import_filename %>'
@@ -1,4 +1,3 @@
1
- //
2
1
  // DO NOT MODIFY: This file was automatically generated by JsFromRoutes.
3
2
  import * as helpers from './all'
4
3
  export * from './all'
@@ -4,5 +4,5 @@
4
4
  # Generates one file per controller, and one function per route.
5
5
  module JsFromRoutes
6
6
  # Public: This library adheres to semantic versioning.
7
- VERSION = "4.1.0"
7
+ VERSION = "5.0.0"
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: js_from_routes
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Máximo Mussini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-03 00:00:00.000000000 Z
11
+ date: 2026-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.1'
19
+ version: '7.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '9'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '5.1'
29
+ version: '7.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '9'
@@ -124,16 +124,16 @@ dependencies:
124
124
  name: simplecov
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
- - - "<"
127
+ - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: '0.18'
129
+ version: 0.17.1
130
130
  type: :development
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
- - - "<"
134
+ - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: '0.18'
136
+ version: 0.17.1
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: standard
139
139
  requirement: !ruby/object:Gem::Requirement