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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1331513cb5939f583960098fa86106a9901a0b3ef126b64f5ac9ae545a51f930
4
- data.tar.gz: f89ef2eff49327f1e4a4a3aed0657167d5dacabd0bae3735a1a4744c0ca45332
3
+ metadata.gz: cfebc711293e26ed0feb2d97ba630782e2d2de8592d4e0d4b3392c54ecda6f5a
4
+ data.tar.gz: 4501274e0565b053dbdf110f87a747e631811e8a5b4d9b248e76e28a6b4a54ea
5
5
  SHA512:
6
- metadata.gz: 6fe469fdebbd0bb563f46c59e01c3931fbcf67485e8d0141cd9628f95969912716f42622454055f758c98e0a4e9482183f79a4a1c025c9e8a9b86a6690dd8f0c
7
- data.tar.gz: 36fcaa4b86a562227966018d98355894526ada5f6744427749938dd64b21ed6e5cd4f390c08c309f4500201f220647fe868a7f223ede977fbbafc15960d5ed51
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 = true # print status lines to terminal
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
- # Emit ReactManifest status lines to stdout in development.
54
- # Independent from Rails.logger output.
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 = true
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
@@ -1,3 +1,3 @@
1
1
  module ReactManifest
2
- VERSION = "0.2.30".freeze
2
+ VERSION = "0.2.32".freeze
3
3
  end
@@ -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
@@ -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.30
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/main/CHANGELOG.md
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: []