react-manifest-rails 0.2.30 → 0.2.32
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 +17 -0
- data/README.md +4 -1
- data/lib/react_manifest/configuration.rb +8 -3
- data/lib/react_manifest/generator.rb +43 -7
- data/lib/react_manifest/version.rb +1 -1
- data/lib/react_manifest/view_helpers.rb +0 -14
- data/lib/react_manifest.rb +9 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cfebc711293e26ed0feb2d97ba630782e2d2de8592d4e0d4b3392c54ecda6f5a
|
|
4
|
+
data.tar.gz: 4501274e0565b053dbdf110f87a747e631811e8a5b4d9b248e76e28a6b4a54ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 358aa7190d5e0f146b1a9fce2065b41890a23bd8b9b6c42855c895905bf8af6567bb6fec372f8b2bd2363cfd7c68b5a5cd7a535fe8632474be60e5bd8d91b5fa
|
|
7
|
+
data.tar.gz: bb655465c8ce7a9eb63ad3dde98cbb6f354a1d1eaef8efd948a4b11dc0657ef673c943fb2ca8549e0fb672f7c7abec8f9b4c3e312a77c26bdb870ea4807622b5
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.32] - 2026-07-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Generation now removes AUTO-GENERATED manifests whose `ux/app/<controller>/` directory no longer exists (renamed or deleted). Previously these were left behind indefinitely, still `//= require`-ing source files that no longer existed — a landmine for `assets:precompile`. Pinned (non-AUTO-GENERATED) files are never touched, and dry-run mode only logs what would be removed.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `config.stdout_logging` now defaults to `false`. `Rails.logger` always receives ReactManifest's status lines regardless of this flag; `stdout_logging` only controls an *additional* direct print to the terminal. Many development setups already have `Rails.logger` broadcast to stdout (`RAILS_LOG_TO_STDOUT`, Docker, `bin/dev`/Foreman), so the previous default of `true` printed every "File change detected" / "N manifest(s) written" line twice. Set `config.stdout_logging = true` explicitly if your `Rails.logger` does not already surface output in your terminal.
|
|
17
|
+
|
|
18
|
+
## [0.2.31] - 2026-07-01
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Controller manifests no longer pull in an unrelated bundle's files just because a component name collides (e.g. a generic `Show` component defined in two different `ux/app/*` dirs). A bundle now always satisfies a symbol from its own files first, instead of attributing it to whichever bundle happened to define that name first.
|
|
22
|
+
- `react_component` no longer silently stops auto-injecting a component's bundle for the rest of the request after `react_bundle_tag` has rendered once. Previously, calling `react_component` for a component in an unrelated bundle (not the current controller's own bundle, e.g. from a haml/erb view mixing controller-based and component-based rendering) would render with no script tag at all if `react_bundle_tag` had already run earlier in the same request.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
- The gem version is no longer embedded in the `AUTO-GENERATED` manifest header. Previously every manifest's content digest changed on every gem upgrade, forcing a full regeneration of all `ux_*.js` files for no functional reason.
|
|
26
|
+
|
|
10
27
|
## [0.2.28] - 2026-05-11
|
|
11
28
|
|
|
12
29
|
### Fixed
|
data/README.md
CHANGED
|
@@ -108,6 +108,8 @@ Files carry an `AUTO-GENERATED` header. Any file without it is never overwritten
|
|
|
108
108
|
|
|
109
109
|
Writes are atomic (temp file + rename) and idempotent (SHA-256 comparison skips unchanged files).
|
|
110
110
|
|
|
111
|
+
If a `ux/app/<controller>/` directory is renamed or deleted, its manifest is removed automatically on the next generation (dev file watcher, boot, or `rails react_manifest:generate`) — pinned files are never removed this way.
|
|
112
|
+
|
|
111
113
|
## Asset Compilation & Minification
|
|
112
114
|
|
|
113
115
|
The generated files are standard Sprockets manifests — `//= require` directives only. Sprockets processes them identically to `application.js`:
|
|
@@ -133,7 +135,7 @@ ReactManifest.configure do |config|
|
|
|
133
135
|
config.size_threshold_kb = 500 # warn if a bundle exceeds this
|
|
134
136
|
config.dry_run = false # never write; only print what would change
|
|
135
137
|
config.verbose = false # extra diagnostic detail
|
|
136
|
-
config.stdout_logging =
|
|
138
|
+
config.stdout_logging = false # also print status lines directly to stdout (see note below)
|
|
137
139
|
end
|
|
138
140
|
```
|
|
139
141
|
|
|
@@ -144,6 +146,7 @@ end
|
|
|
144
146
|
- **`dry_run`**: also honoured by `DRY_RUN=1` environment variable at runtime.
|
|
145
147
|
- **`extensions`**: add `ts` and `tsx` to enable TypeScript source detection.
|
|
146
148
|
- **`external_roots` / `external_providers`**: do not point these at files already inside shared `ux/` dirs (`components/`, `hooks/`, `lib/`, etc.). The generator now skips those overlaps to prevent duplicate declarations in browser runtime.
|
|
149
|
+
- **`stdout_logging`**: `Rails.logger` always receives status lines regardless of this flag. Turn this on only if your `Rails.logger` does *not* already print to your terminal in development — if it does (common with `RAILS_LOG_TO_STDOUT`, Docker, or `bin/dev`/Foreman setups), enabling this prints every line twice.
|
|
147
150
|
|
|
148
151
|
## Commands
|
|
149
152
|
|
|
@@ -50,8 +50,13 @@ module ReactManifest
|
|
|
50
50
|
# Extra diagnostic logging (summary lines and richer error context).
|
|
51
51
|
attr_accessor :verbose
|
|
52
52
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
53
|
+
# Also print ReactManifest status lines directly to stdout, in addition
|
|
54
|
+
# to Rails.logger (which always receives them regardless of this flag).
|
|
55
|
+
# Off by default: many development setups already have Rails.logger
|
|
56
|
+
# broadcast to the terminal (RAILS_LOG_TO_STDOUT, Docker/Foreman, etc.),
|
|
57
|
+
# and enabling this on top of that prints every line twice. Turn it on
|
|
58
|
+
# only if your Rails.logger does NOT already surface output in your
|
|
59
|
+
# terminal and you want a guaranteed visible line per event.
|
|
55
60
|
attr_accessor :stdout_logging
|
|
56
61
|
|
|
57
62
|
# Explicit symbol-to-require-path mapping for external globals.
|
|
@@ -88,7 +93,7 @@ module ReactManifest
|
|
|
88
93
|
@extensions = %w[js jsx]
|
|
89
94
|
@dry_run = false
|
|
90
95
|
@verbose = false
|
|
91
|
-
@stdout_logging =
|
|
96
|
+
@stdout_logging = false
|
|
92
97
|
@external_providers = {}
|
|
93
98
|
@external_roots = []
|
|
94
99
|
end
|
|
@@ -12,7 +12,8 @@ module ReactManifest
|
|
|
12
12
|
# Returns an array of result hashes:
|
|
13
13
|
# [{path: "/abs/path/ux_shared.js", status: :written}, ...]
|
|
14
14
|
#
|
|
15
|
-
# Possible +status+ values: +:written+, +:unchanged+, +:skipped_pinned+, +:dry_run
|
|
15
|
+
# Possible +status+ values: +:written+, +:unchanged+, +:skipped_pinned+, +:dry_run+,
|
|
16
|
+
# +:removed_orphan+.
|
|
16
17
|
#
|
|
17
18
|
# Generates:
|
|
18
19
|
# ux_shared.js — requires all files from shared dirs (components/, hooks/, lib/, etc.)
|
|
@@ -22,6 +23,9 @@ module ReactManifest
|
|
|
22
23
|
# (skips write if content unchanged). Writes are atomic (temp-file + rename)
|
|
23
24
|
# to avoid partial reads from concurrent processes.
|
|
24
25
|
#
|
|
26
|
+
# Manifests whose ux/app/<controller> dir no longer exists are removed
|
|
27
|
+
# automatically (pinned files are never removed this way).
|
|
28
|
+
#
|
|
25
29
|
# Never touches application.js, application_dev.js, or files in exclude_paths.
|
|
26
30
|
# rubocop:disable Metrics/ClassLength
|
|
27
31
|
class Generator
|
|
@@ -30,7 +34,6 @@ module ReactManifest
|
|
|
30
34
|
|
|
31
35
|
HEADER = <<~JS.freeze
|
|
32
36
|
// AUTO-GENERATED — DO NOT EDIT
|
|
33
|
-
// react-manifest-rails %<version>s
|
|
34
37
|
// Run `rails react_manifest:generate` to regenerate.
|
|
35
38
|
JS
|
|
36
39
|
|
|
@@ -59,6 +62,12 @@ module ReactManifest
|
|
|
59
62
|
# Phase 2: write — each write is atomic (tmp + rename).
|
|
60
63
|
results = manifests.map { |m| write_manifest(m[:filename], m[:content]) }
|
|
61
64
|
|
|
65
|
+
# Phase 3: remove manifests left behind by a controller dir that no
|
|
66
|
+
# longer exists (renamed/deleted ux/app/<controller>). Never touches
|
|
67
|
+
# pinned (non-AUTO-GENERATED) files.
|
|
68
|
+
expected_filenames = manifests.map { |m| m[:filename] }
|
|
69
|
+
results.concat(remove_orphaned_manifests(expected_filenames))
|
|
70
|
+
|
|
62
71
|
print_summary(results) if @config.verbose?
|
|
63
72
|
results
|
|
64
73
|
end
|
|
@@ -130,6 +139,7 @@ module ReactManifest
|
|
|
130
139
|
def build_controller_context(controller_dirs)
|
|
131
140
|
bundle_files = {}
|
|
132
141
|
symbol_to_bundle = {}
|
|
142
|
+
bundle_own_symbols = Hash.new { |h, k| h[k] = Set.new }
|
|
133
143
|
external_symbol_to_require = {}
|
|
134
144
|
dependencies = Hash.new { |h, k| h[k] = Set.new }
|
|
135
145
|
external_requires = Hash.new { |h, k| h[k] = Set.new }
|
|
@@ -145,6 +155,7 @@ module ReactManifest
|
|
|
145
155
|
next unless sym.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
|
|
146
156
|
|
|
147
157
|
symbol_to_bundle[sym] ||= bundle_name
|
|
158
|
+
bundle_own_symbols[bundle_name] << sym
|
|
148
159
|
end
|
|
149
160
|
end
|
|
150
161
|
end
|
|
@@ -170,8 +181,16 @@ module ReactManifest
|
|
|
170
181
|
|
|
171
182
|
# Compute per-bundle cross-app and external dependencies
|
|
172
183
|
bundle_files.each do |bundle_name, files|
|
|
184
|
+
own_symbols = bundle_own_symbols[bundle_name]
|
|
185
|
+
|
|
173
186
|
files.each do |file_path|
|
|
174
187
|
extract_used_component_symbols(file_path).each do |sym|
|
|
188
|
+
# A symbol the bundle defines itself (in any of its own files) is
|
|
189
|
+
# always satisfied locally — never attribute it to another bundle
|
|
190
|
+
# or an external provider just because that symbol name happens
|
|
191
|
+
# to collide with something defined elsewhere.
|
|
192
|
+
next if own_symbols.include?(sym)
|
|
193
|
+
|
|
175
194
|
dep_bundle = symbol_to_bundle[sym]
|
|
176
195
|
dependencies[bundle_name] << dep_bundle if dep_bundle && dep_bundle != bundle_name
|
|
177
196
|
|
|
@@ -313,13 +332,29 @@ module ReactManifest
|
|
|
313
332
|
end
|
|
314
333
|
end
|
|
315
334
|
|
|
335
|
+
# Remove AUTO-GENERATED manifests that no longer correspond to any
|
|
336
|
+
# current shared/controller bundle (e.g. a ux/app/<controller> dir was
|
|
337
|
+
# deleted or renamed). Skips pinned files and, in dry-run mode, only
|
|
338
|
+
# logs what would be removed.
|
|
339
|
+
def remove_orphaned_manifests(expected_filenames)
|
|
340
|
+
Dir.glob(File.join(@config.abs_manifest_dir, "ux_*.js")).filter_map do |file|
|
|
341
|
+
next if expected_filenames.include?(File.basename(file))
|
|
342
|
+
next unless auto_generated?(file)
|
|
343
|
+
|
|
344
|
+
if @config.dry_run?
|
|
345
|
+
log_info "DRY-RUN: would remove orphaned manifest #{file}"
|
|
346
|
+
{ path: file, status: :dry_run }
|
|
347
|
+
else
|
|
348
|
+
File.delete(file)
|
|
349
|
+
{ path: file, status: :removed_orphan }
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
316
354
|
# ----------------------------------------------------------- helpers
|
|
317
355
|
|
|
318
356
|
def header_lines
|
|
319
|
-
[
|
|
320
|
-
format(HEADER, version: ReactManifest::VERSION),
|
|
321
|
-
""
|
|
322
|
-
].flatten
|
|
357
|
+
[HEADER, ""]
|
|
323
358
|
end
|
|
324
359
|
|
|
325
360
|
def js_files_in(dir)
|
|
@@ -427,7 +462,8 @@ module ReactManifest
|
|
|
427
462
|
counts = results.group_by { |r| r[:status] }.transform_values(&:count)
|
|
428
463
|
log_info "Generated: #{counts[:written] || 0} written, " \
|
|
429
464
|
"#{counts[:unchanged] || 0} unchanged, " \
|
|
430
|
-
"#{counts[:skipped_pinned] || 0} skipped (not auto-generated)"
|
|
465
|
+
"#{counts[:skipped_pinned] || 0} skipped (not auto-generated), " \
|
|
466
|
+
"#{counts[:removed_orphan] || 0} orphaned removed"
|
|
431
467
|
end
|
|
432
468
|
end
|
|
433
469
|
# rubocop:enable Metrics/ClassLength
|
|
@@ -26,7 +26,6 @@ module ReactManifest
|
|
|
26
26
|
return "".html_safe if fresh_bundles.empty?
|
|
27
27
|
|
|
28
28
|
fresh_bundles.each { |b| emitted << b }
|
|
29
|
-
mark_bundle_tag_rendered
|
|
30
29
|
|
|
31
30
|
asset_names = fresh_bundles.map { |bundle| "#{bundle}.js" }
|
|
32
31
|
javascript_include_tag(*asset_names, extname: false, **html_options)
|
|
@@ -38,7 +37,6 @@ module ReactManifest
|
|
|
38
37
|
# This avoids strict dependence on controller_path -> bundle naming alignment.
|
|
39
38
|
def react_component(*args, **kwargs, &block)
|
|
40
39
|
html = super
|
|
41
|
-
return html if bundle_tag_rendered?
|
|
42
40
|
|
|
43
41
|
component_name = args.first
|
|
44
42
|
bundles = ReactManifest.resolve_bundles_for_component_direct(component_name)
|
|
@@ -78,17 +76,5 @@ module ReactManifest
|
|
|
78
76
|
@emitted_bundles ||= []
|
|
79
77
|
end
|
|
80
78
|
end
|
|
81
|
-
|
|
82
|
-
def mark_bundle_tag_rendered
|
|
83
|
-
return unless respond_to?(:request, true) && request
|
|
84
|
-
|
|
85
|
-
request.env["react_manifest.bundle_tag_rendered"] = true
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def bundle_tag_rendered?
|
|
89
|
-
return false unless respond_to?(:request, true) && request
|
|
90
|
-
|
|
91
|
-
request.env["react_manifest.bundle_tag_rendered"] == true
|
|
92
|
-
end
|
|
93
79
|
end
|
|
94
80
|
end
|
data/lib/react_manifest.rb
CHANGED
|
@@ -146,6 +146,7 @@ module ReactManifest
|
|
|
146
146
|
|
|
147
147
|
controller_dirs = TreeClassifier.new(config).classify.controller_dirs
|
|
148
148
|
symbol_to_bundle = {}
|
|
149
|
+
bundle_own_symbols = Hash.new { |h, k| h[k] = Set.new }
|
|
149
150
|
bundle_files = Hash.new { |h, k| h[k] = [] }
|
|
150
151
|
bundle_dependencies = Hash.new { |h, k| h[k] = Set.new }
|
|
151
152
|
|
|
@@ -160,13 +161,21 @@ module ReactManifest
|
|
|
160
161
|
|
|
161
162
|
# Keep first writer to ensure deterministic behavior if a symbol is duplicated.
|
|
162
163
|
symbol_to_bundle[symbol] ||= bundle_name
|
|
164
|
+
bundle_own_symbols[bundle_name] << symbol
|
|
163
165
|
end
|
|
164
166
|
end
|
|
165
167
|
end
|
|
166
168
|
|
|
167
169
|
bundle_files.each do |bundle_name, files|
|
|
170
|
+
own_symbols = bundle_own_symbols[bundle_name]
|
|
171
|
+
|
|
168
172
|
files.each do |file_path|
|
|
169
173
|
extract_used_component_symbols(file_path).each do |symbol|
|
|
174
|
+
# A symbol the bundle defines itself is always satisfied locally —
|
|
175
|
+
# never attribute it to another bundle just because that symbol
|
|
176
|
+
# name happens to collide with something defined elsewhere.
|
|
177
|
+
next if own_symbols.include?(symbol)
|
|
178
|
+
|
|
170
179
|
dep_bundle = symbol_to_bundle[symbol]
|
|
171
180
|
next unless dep_bundle && dep_bundle != bundle_name
|
|
172
181
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: react-manifest-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.32
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oliver Noonan
|
|
@@ -160,7 +160,7 @@ licenses:
|
|
|
160
160
|
metadata:
|
|
161
161
|
homepage_uri: https://github.com/olivernoonan/react-manifest-rails
|
|
162
162
|
source_code_uri: https://github.com/olivernoonan/react-manifest-rails
|
|
163
|
-
changelog_uri: https://github.com/olivernoonan/react-manifest-rails/blob/
|
|
163
|
+
changelog_uri: https://github.com/olivernoonan/react-manifest-rails/blob/master/CHANGELOG.md
|
|
164
164
|
bug_tracker_uri: https://github.com/olivernoonan/react-manifest-rails/issues
|
|
165
165
|
rubygems_mfa_required: 'true'
|
|
166
166
|
rdoc_options: []
|