js_from_routes 4.0.2 → 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 +4 -4
- data/CHANGELOG.md +33 -0
- data/lib/js_from_routes/generator.rb +118 -41
- data/lib/js_from_routes/railtie.rb +5 -1
- data/lib/js_from_routes/template.js.erb +0 -1
- data/lib/js_from_routes/template_all.js.erb +0 -1
- data/lib/js_from_routes/template_index.js.erb +0 -1
- data/lib/js_from_routes/version.rb +1 -1
- metadata +30 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c5e43fa00d907a28931b13ec417afb2d7ef602fdb1b9c47ccd9e00289b305ec
|
|
4
|
+
data.tar.gz: 4bf4c33ffd5adf831f940f82d51e68af66cc7be9199c5fcbcd957ab4cf2858be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d55050b0f4b4168db33c7db98fa51323fb469e5b8ffd0f484da07860d6834d1a23db1537fcb05414422ec14d344f31fa15f784aac33d283b5365baa45caebda5
|
|
7
|
+
data.tar.gz: fa99b4558aae4ad1e91fc6ab4792b058f928232565f521c98e32344bd304e548fc8cfcac873e31c13ca1be06a170bbc9a3d13db8ddc88cc89eacfcf367e4612d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
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
|
+
|
|
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)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* support multiple generator instances for sub-app routing ([#70](https://github.com/ElMassimo/js_from_routes/issues/70)) ([49d944b](https://github.com/ElMassimo/js_from_routes/commit/49d944b7fcde885f1e8b26312738450a4a3f9136))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
1
34
|
## [4.0.2](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@4.0.1...js_from_routes@4.0.2) (2025-01-15)
|
|
2
35
|
|
|
3
36
|
|
|
@@ -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
|
|
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:
|
|
104
|
-
#
|
|
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
|
|
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:
|
|
117
|
-
|
|
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
|
|
125
|
-
def write_file_if_changed(name
|
|
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
|
-
|
|
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,28 +138,40 @@ module JsFromRoutes
|
|
|
155
138
|
end
|
|
156
139
|
|
|
157
140
|
class TemplateConfig
|
|
158
|
-
attr_reader :
|
|
141
|
+
attr_reader :filename, :helpers
|
|
159
142
|
|
|
160
|
-
def initialize(
|
|
161
|
-
@cache_key = cache_key
|
|
143
|
+
def initialize(filename:, helpers: nil)
|
|
162
144
|
@filename = filename
|
|
163
145
|
@helpers = helpers
|
|
164
146
|
end
|
|
165
147
|
end
|
|
166
148
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
149
|
+
# Public: A generator instance with its own configuration. Allows generating
|
|
150
|
+
# routes separately for different sub-apps in a Rails monolith.
|
|
151
|
+
#
|
|
152
|
+
# Example:
|
|
153
|
+
# JsFromRoutes.config(:admin) do |config|
|
|
154
|
+
# config.output_folder = Rails.root.join("app/javascript/admin/api")
|
|
155
|
+
# config.export_if = ->(route) { route.defaults[:export] == :admin }
|
|
156
|
+
# end
|
|
157
|
+
#
|
|
158
|
+
# JsFromRoutes.config(:public) do |config|
|
|
159
|
+
# config.output_folder = Rails.root.join("app/javascript/public/api")
|
|
160
|
+
# config.export_if = ->(route) { route.defaults[:export] == :public }
|
|
161
|
+
# end
|
|
162
|
+
class Instance
|
|
163
|
+
attr_reader :name, :config
|
|
164
|
+
|
|
165
|
+
def initialize(name, config)
|
|
166
|
+
@name = name
|
|
167
|
+
@config = config
|
|
173
168
|
end
|
|
174
169
|
|
|
175
170
|
# Public: Generates code for the specified routes with { export: true }.
|
|
176
171
|
def generate!(app_or_routes = Rails.application)
|
|
177
172
|
raise ArgumentError, "A Rails app must be defined, or you must specify a custom `output_folder`" if config.output_folder.to_s.blank?
|
|
178
173
|
rails_routes = app_or_routes.is_a?(::Rails::Engine) ? app_or_routes.routes.routes : app_or_routes
|
|
179
|
-
generate_files
|
|
174
|
+
generate_files(exported_routes_by_controller(rails_routes))
|
|
180
175
|
end
|
|
181
176
|
|
|
182
177
|
private
|
|
@@ -198,12 +193,10 @@ module JsFromRoutes
|
|
|
198
193
|
index_file = (config.all_helpers_file == true) ? "index#{preferred_extension}" : config.all_helpers_file
|
|
199
194
|
|
|
200
195
|
Template.new(config.template_all_path).write_if_changed TemplateConfig.new(
|
|
201
|
-
cache_key: routes.map(&:import_filename).join + File.read(config.template_all_path),
|
|
202
196
|
filename: config.output_folder.join("all#{preferred_extension}"),
|
|
203
197
|
helpers: routes,
|
|
204
198
|
)
|
|
205
199
|
Template.new(config.template_index_path).write_if_changed TemplateConfig.new(
|
|
206
|
-
cache_key: File.read(config.template_index_path),
|
|
207
200
|
filename: config.output_folder.join(index_file),
|
|
208
201
|
)
|
|
209
202
|
end
|
|
@@ -221,4 +214,88 @@ module JsFromRoutes
|
|
|
221
214
|
.group_by { |route| namespace_for_route(route)&.to_s }
|
|
222
215
|
end
|
|
223
216
|
end
|
|
217
|
+
|
|
218
|
+
class << self
|
|
219
|
+
# Public: Configuration of the code generator.
|
|
220
|
+
#
|
|
221
|
+
# Without a name, returns/yields the default global configuration:
|
|
222
|
+
# JsFromRoutes.config do |config|
|
|
223
|
+
# config.file_suffix = "Api.ts"
|
|
224
|
+
# end
|
|
225
|
+
#
|
|
226
|
+
# With a name, defines a named generator instance with its own configuration,
|
|
227
|
+
# allowing separate route generation for different sub-apps:
|
|
228
|
+
# JsFromRoutes.config(:admin) do |config|
|
|
229
|
+
# config.output_folder = Rails.root.join("app/javascript/admin/api")
|
|
230
|
+
# config.export_if = ->(route) { route.defaults[:export] == :admin }
|
|
231
|
+
# end
|
|
232
|
+
def config(name = nil)
|
|
233
|
+
if name
|
|
234
|
+
root = ::Rails.root || Pathname.new(Dir.pwd)
|
|
235
|
+
new_config = Configuration.new(root)
|
|
236
|
+
yield(new_config) if block_given?
|
|
237
|
+
instances[name] = Instance.new(name, new_config)
|
|
238
|
+
else
|
|
239
|
+
@config ||= Configuration.new(::Rails.root || Pathname.new(Dir.pwd))
|
|
240
|
+
if block_given?
|
|
241
|
+
@config_customized = true
|
|
242
|
+
yield(@config)
|
|
243
|
+
end
|
|
244
|
+
@config
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Public: Returns all registered named instances.
|
|
249
|
+
def instances
|
|
250
|
+
@instances ||= {}
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Public: Generates code for the specified routes with { export: true }.
|
|
254
|
+
#
|
|
255
|
+
# Runs every named instance, plus the default global config whenever it was
|
|
256
|
+
# explicitly configured (or when no named instances exist at all). This keeps
|
|
257
|
+
# mixed usage working: adding a named instance never silently disables the
|
|
258
|
+
# global config that an existing app already relies on.
|
|
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
|
|
283
|
+
runnable = instances.values
|
|
284
|
+
runnable.unshift(Instance.new(:default, config)) if @config_customized || runnable.empty?
|
|
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]] }
|
|
299
|
+
end
|
|
300
|
+
end
|
|
224
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.
|
|
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
|
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
|
+
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:
|
|
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: '
|
|
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: '
|
|
29
|
+
version: '7.2'
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '9'
|
|
@@ -34,16 +34,22 @@ dependencies:
|
|
|
34
34
|
name: bundler
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '2'
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '5'
|
|
40
43
|
type: :development
|
|
41
44
|
prerelease: false
|
|
42
45
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
46
|
requirements:
|
|
44
|
-
- - "
|
|
47
|
+
- - ">="
|
|
45
48
|
- !ruby/object:Gem::Version
|
|
46
49
|
version: '2'
|
|
50
|
+
- - "<"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '5'
|
|
47
53
|
- !ruby/object:Gem::Dependency
|
|
48
54
|
name: listen
|
|
49
55
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,6 +78,20 @@ dependencies:
|
|
|
72
78
|
- - "~>"
|
|
73
79
|
- !ruby/object:Gem::Version
|
|
74
80
|
version: '3.9'
|
|
81
|
+
- !ruby/object:Gem::Dependency
|
|
82
|
+
name: readline
|
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
type: :development
|
|
89
|
+
prerelease: false
|
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
75
95
|
- !ruby/object:Gem::Dependency
|
|
76
96
|
name: rake
|
|
77
97
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -104,16 +124,16 @@ dependencies:
|
|
|
104
124
|
name: simplecov
|
|
105
125
|
requirement: !ruby/object:Gem::Requirement
|
|
106
126
|
requirements:
|
|
107
|
-
- - "
|
|
127
|
+
- - "~>"
|
|
108
128
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
129
|
+
version: 0.17.1
|
|
110
130
|
type: :development
|
|
111
131
|
prerelease: false
|
|
112
132
|
version_requirements: !ruby/object:Gem::Requirement
|
|
113
133
|
requirements:
|
|
114
|
-
- - "
|
|
134
|
+
- - "~>"
|
|
115
135
|
- !ruby/object:Gem::Version
|
|
116
|
-
version:
|
|
136
|
+
version: 0.17.1
|
|
117
137
|
- !ruby/object:Gem::Dependency
|
|
118
138
|
name: standard
|
|
119
139
|
requirement: !ruby/object:Gem::Requirement
|