react-manifest-rails 0.2.22 → 0.2.23
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 +4 -0
- data/lib/react_manifest/generator.rb +71 -29
- data/lib/react_manifest/scanner.rb +23 -28
- data/lib/react_manifest/version.rb +1 -1
- data/lib/react_manifest/view_helpers.rb +35 -9
- data/tasks/react_manifest.rake +0 -6
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 907e5e7bb8b3e6a8554009c4e8a87a39379d8bd09c4455fd9c02b082a014c18b
|
|
4
|
+
data.tar.gz: 11d4e6ca8833973107937eee1b82881f620f3b2e74442ab0883521ea5803d58b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 950abc3b34f1ffe0e8eb6fcd4ae7798469b60746531d0bb0171de0975881cb2572f35182e07785255fcf932ce14ccaac369a1238e2d5e2fe3b8689d323861d06
|
|
7
|
+
data.tar.gz: dc6fcbfb087a8aa9f95ff109147ee0345b2aa32fc1a0281118ffb60d12313735d659ecb55200832eb940bf6def8675ab711e64208fcc0ec17514e71e0ea57f42
|
data/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.23] - 2026-04-21
|
|
11
|
+
|
|
10
12
|
### Fixed
|
|
11
13
|
- `react_component` now emits only `ux_shared` plus the direct owning bundle for the requested component symbol, instead of also emitting transitive controller manifests as separate script tags. This prevents unnecessary network requests for additional `ux_*.js` manifests while preserving dependency loading through generated manifest `require` directives.
|
|
12
14
|
- Generator now skips `external_roots` and `external_providers` entries that resolve to files already included in `ux_shared`, preventing duplicate runtime declarations from overlapping shared/external includes.
|
|
13
15
|
- View helper bundle deduplication now canonicalizes bundle names (e.g. `ux_shared` vs `ux_manifests/ux_shared`) to avoid re-emitting equivalent script tags.
|
|
16
|
+
- Controller manifests now include scanner-detected shared dependencies after `ux_shared` removal, including transitive shared dependencies needed by shared components (for example `DataTable` -> `SortHeader`).
|
|
17
|
+
- Shared `ux/lib` utility files are now included in controller manifests, restoring runtime availability for global helper functions such as `formatDate` and `formatCurrency`.
|
|
14
18
|
|
|
15
19
|
## [0.2.10] - 2026-04-16
|
|
16
20
|
|
|
@@ -42,11 +42,11 @@ module ReactManifest
|
|
|
42
42
|
# written and others stale/missing.
|
|
43
43
|
def run!
|
|
44
44
|
classification = @classifier.classify
|
|
45
|
-
|
|
45
|
+
scan_result = Scanner.new(@config).scan(classification)
|
|
46
|
+
controller_context = build_controller_context(classification.controller_dirs, classification.shared_dirs, scan_result)
|
|
46
47
|
|
|
47
48
|
# Phase 1: build all content in memory — no I/O.
|
|
48
49
|
manifests = []
|
|
49
|
-
manifests << build_shared(classification.shared_dirs)
|
|
50
50
|
classification.controller_dirs.each { |ctrl| manifests << build_controller(ctrl, controller_context) }
|
|
51
51
|
|
|
52
52
|
migrate_legacy_manifests!
|
|
@@ -62,33 +62,20 @@ module ReactManifest
|
|
|
62
62
|
|
|
63
63
|
# ------------------------------------------------------------------ shared
|
|
64
64
|
|
|
65
|
-
def build_shared(shared_dirs)
|
|
66
|
-
lines = header_lines
|
|
67
|
-
any_files = false
|
|
68
65
|
|
|
69
|
-
shared_dirs.each do |shared_dir|
|
|
70
|
-
files = js_files_in(shared_dir[:path])
|
|
71
|
-
next if files.empty?
|
|
72
|
-
|
|
73
|
-
any_files = true
|
|
74
|
-
files.each { |f| lines << "//= require #{relative_require_path(f)}" }
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
lines << "// (no shared files found)" unless any_files
|
|
78
|
-
|
|
79
|
-
{ filename: "#{@config.shared_bundle}.js", content: "#{lines.join("\n")}\n" }
|
|
80
|
-
end
|
|
81
66
|
|
|
82
67
|
# --------------------------------------------------------------- controller
|
|
83
68
|
|
|
84
69
|
def build_controller(ctrl, controller_context)
|
|
85
70
|
lines = header_lines
|
|
86
71
|
dep_requires = controller_dependency_requires(ctrl[:bundle_name], controller_context)
|
|
72
|
+
lib_reqs = controller_context[:shared_lib_requires]
|
|
73
|
+
shared_reqs = controller_context[:shared_requires].fetch(ctrl[:bundle_name], Set.new).to_a.sort
|
|
87
74
|
ext_reqs = controller_context[:external_requires].fetch(ctrl[:bundle_name], Set.new).to_a.sort
|
|
88
75
|
|
|
89
76
|
files = js_files_in(ctrl[:path])
|
|
90
77
|
own_requires = files.map { |f| relative_require_path(f) }
|
|
91
|
-
all_requires = (dep_requires + ext_reqs + own_requires).uniq
|
|
78
|
+
all_requires = (dep_requires + lib_reqs + shared_reqs + ext_reqs + own_requires).uniq
|
|
92
79
|
|
|
93
80
|
if all_requires.empty?
|
|
94
81
|
lines << "// (no JSX files found in #{ctrl[:name]}/)"
|
|
@@ -99,13 +86,24 @@ module ReactManifest
|
|
|
99
86
|
{ filename: "#{ctrl[:bundle_name]}.js", content: "#{lines.join("\n")}\n" }
|
|
100
87
|
end
|
|
101
88
|
|
|
102
|
-
def build_controller_context(controller_dirs, shared_dirs)
|
|
89
|
+
def build_controller_context(controller_dirs, shared_dirs, scan_result)
|
|
103
90
|
bundle_files = {}
|
|
104
91
|
symbol_to_bundle = {}
|
|
105
92
|
external_symbol_to_require = {}
|
|
106
93
|
dependencies = Hash.new { |h, k| h[k] = Set.new }
|
|
107
94
|
external_requires = Hash.new { |h, k| h[k] = Set.new }
|
|
108
95
|
shared_require_paths = shared_require_path_set(shared_dirs)
|
|
96
|
+
shared_requires = Hash.new { |h, k| h[k] = Set.new }
|
|
97
|
+
shared_dependency_map = build_shared_dependency_map(shared_dirs, shared_require_paths, scan_result)
|
|
98
|
+
shared_lib_requires = shared_lib_require_paths(shared_dirs)
|
|
99
|
+
|
|
100
|
+
controller_dirs.each do |ctrl|
|
|
101
|
+
scan_result.controller_usages.fetch(ctrl[:name], []).each do |req_path|
|
|
102
|
+
shared_requires[ctrl[:bundle_name]] << req_path
|
|
103
|
+
end
|
|
104
|
+
shared_requires[ctrl[:bundle_name]] = expand_shared_requires(shared_requires[ctrl[:bundle_name]],
|
|
105
|
+
shared_dependency_map)
|
|
106
|
+
end
|
|
109
107
|
|
|
110
108
|
# Index controller-defined symbols for cross-app detection
|
|
111
109
|
controller_dirs.each do |ctrl|
|
|
@@ -127,10 +125,6 @@ module ReactManifest
|
|
|
127
125
|
abs_root = abs_external_root(root_path)
|
|
128
126
|
external_js_files_in(abs_root).each do |file_path|
|
|
129
127
|
req_path = relative_require_path(file_path)
|
|
130
|
-
if shared_require_paths.include?(normalize_require_path(req_path))
|
|
131
|
-
warn "[ReactManifest] Skipping external_roots file already provided by shared bundle: #{req_path}"
|
|
132
|
-
next
|
|
133
|
-
end
|
|
134
128
|
|
|
135
129
|
warn_on_external_controller_references(file_path, symbol_to_bundle)
|
|
136
130
|
|
|
@@ -142,12 +136,6 @@ module ReactManifest
|
|
|
142
136
|
|
|
143
137
|
# Explicit external_providers win over scanned roots on symbol conflicts
|
|
144
138
|
@config.external_providers.each do |sym, req_path|
|
|
145
|
-
if shared_require_paths.include?(normalize_require_path(req_path))
|
|
146
|
-
warn "[ReactManifest] Skipping external provider '#{sym}' because it is already " \
|
|
147
|
-
"provided by shared bundle: #{req_path}"
|
|
148
|
-
next
|
|
149
|
-
end
|
|
150
|
-
|
|
151
139
|
external_symbol_to_require[sym] = req_path
|
|
152
140
|
end
|
|
153
141
|
|
|
@@ -167,6 +155,8 @@ module ReactManifest
|
|
|
167
155
|
{
|
|
168
156
|
bundle_files: bundle_files,
|
|
169
157
|
dependencies: dependencies,
|
|
158
|
+
shared_lib_requires: shared_lib_requires,
|
|
159
|
+
shared_requires: shared_requires,
|
|
170
160
|
external_requires: external_requires
|
|
171
161
|
}
|
|
172
162
|
end
|
|
@@ -370,6 +360,58 @@ module ReactManifest
|
|
|
370
360
|
end
|
|
371
361
|
end
|
|
372
362
|
|
|
363
|
+
def shared_lib_require_paths(shared_dirs)
|
|
364
|
+
shared_dirs.each_with_object([]) do |shared_dir, paths|
|
|
365
|
+
next unless File.basename(shared_dir[:path]) == "lib"
|
|
366
|
+
|
|
367
|
+
js_files_in(shared_dir[:path]).each do |file_path|
|
|
368
|
+
paths << normalize_require_path(relative_require_path(file_path))
|
|
369
|
+
end
|
|
370
|
+
end.sort.uniq
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def build_shared_dependency_map(shared_dirs, shared_require_paths, scan_result)
|
|
374
|
+
dependency_map = Hash.new { |h, k| h[k] = Set.new }
|
|
375
|
+
|
|
376
|
+
shared_symbol_index = scan_result.symbol_index.each_with_object({}) do |(sym, req_path), index|
|
|
377
|
+
normalized = normalize_require_path(req_path)
|
|
378
|
+
next unless shared_require_paths.include?(normalized)
|
|
379
|
+
|
|
380
|
+
index[sym] = normalized
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
shared_dirs.each do |shared_dir|
|
|
384
|
+
js_files_in(shared_dir[:path]).each do |file_path|
|
|
385
|
+
from_req = normalize_require_path(relative_require_path(file_path))
|
|
386
|
+
extract_used_component_symbols(file_path).each do |sym|
|
|
387
|
+
to_req = shared_symbol_index[sym]
|
|
388
|
+
next if to_req.nil? || to_req == from_req
|
|
389
|
+
|
|
390
|
+
dependency_map[from_req] << to_req
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
dependency_map
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def expand_shared_requires(initial_requires, dependency_map)
|
|
399
|
+
expanded = Set.new(initial_requires)
|
|
400
|
+
queue = initial_requires.to_a
|
|
401
|
+
|
|
402
|
+
until queue.empty?
|
|
403
|
+
req = queue.shift
|
|
404
|
+
dependency_map.fetch(req, Set.new).each do |dep_req|
|
|
405
|
+
next if expanded.include?(dep_req)
|
|
406
|
+
|
|
407
|
+
expanded << dep_req
|
|
408
|
+
queue << dep_req
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
expanded
|
|
413
|
+
end
|
|
414
|
+
|
|
373
415
|
def normalize_require_path(path)
|
|
374
416
|
path.to_s.sub(/\.js\.jsx$/, "").sub(/\.jsx$/, "").sub(/\.js$/, "")
|
|
375
417
|
end
|
|
@@ -9,7 +9,6 @@ module ReactManifest
|
|
|
9
9
|
# Phase 1 — builds a symbol index from shared dirs:
|
|
10
10
|
# "PrimaryButton" => "ux/components/buttons/primary_button"
|
|
11
11
|
# "useFetch" => "ux/hooks/use_fetch"
|
|
12
|
-
# "formatDate" => "ux/lib/format_date"
|
|
13
12
|
#
|
|
14
13
|
# Phase 2 — scans controller files for usage of those symbols
|
|
15
14
|
# and produces per-controller lists of referenced shared files.
|
|
@@ -21,22 +20,19 @@ module ReactManifest
|
|
|
21
20
|
DEFINITION_PATTERNS = [
|
|
22
21
|
# CommonJS / variable-assignment style
|
|
23
22
|
/(?:const|let|var)\s+([A-Z][A-Za-z0-9_]*)\s*=/, # const FooBar =
|
|
24
|
-
/function\s+([A-Z][A-Za-z0-9_]*)\s*\(/,
|
|
25
|
-
/class\s+([A-Z][A-Za-z0-9_]*)\s*(?:extends|\{)/,
|
|
23
|
+
/function\s+([A-Z][A-Za-z0-9_]*)\s*\(/, # function FooBar(
|
|
24
|
+
/class\s+([A-Z][A-Za-z0-9_]*)\s*(?:extends|\{)/, # class FooBar
|
|
26
25
|
/(?:const|let|var)\s+(use[A-Z][A-Za-z0-9_]*)\s*=/, # const useFoo = (hooks)
|
|
27
|
-
/function\s+(use[A-Z][A-Za-z0-9_]*)\s*\(/,
|
|
28
|
-
/(?:const|let|var)\s+([a-z][A-Za-z0-9_]{2,})\s*=\s*(?:function|\()/, # const formatDate = function/arrow
|
|
29
|
-
/^function\s+([a-z][A-Za-z0-9_]{2,})\s*\(/, # function formatDate( at line start
|
|
26
|
+
/function\s+(use[A-Z][A-Za-z0-9_]*)\s*\(/, # function useFoo(
|
|
30
27
|
|
|
31
28
|
# ES module style (export default / named exports)
|
|
32
|
-
/^export\s+default\s+(?:function|class)\s+([A-Z][A-Za-z0-9_]*)/,
|
|
33
|
-
/^export\s+default\s+(?:function|class)\s+(use[A-Z][A-Za-z0-9_]*)/,
|
|
34
|
-
/^export\s+(?:const|let|var)\s+([A-Z][A-Za-z0-9_]*)\s*=/,
|
|
35
|
-
/^export\s+(?:const|let|var)\s+(use[A-Z][A-Za-z0-9_]*)\s*=/,
|
|
36
|
-
/^export\s+
|
|
37
|
-
/^export\s+function\s+([A-Z][A-Za-z0-9_]*)\s*\(/,
|
|
38
|
-
/^export\s+
|
|
39
|
-
/^export\s+class\s+([A-Z][A-Za-z0-9_]*)\s*(?:extends|\{)/ # export class Foo
|
|
29
|
+
/^export\s+default\s+(?:function|class)\s+([A-Z][A-Za-z0-9_]*)/,
|
|
30
|
+
/^export\s+default\s+(?:function|class)\s+(use[A-Z][A-Za-z0-9_]*)/,
|
|
31
|
+
/^export\s+(?:const|let|var)\s+([A-Z][A-Za-z0-9_]*)\s*=/,
|
|
32
|
+
/^export\s+(?:const|let|var)\s+(use[A-Z][A-Za-z0-9_]*)\s*=/,
|
|
33
|
+
/^export\s+function\s+([A-Z][A-Za-z0-9_]*)\s*\(/,
|
|
34
|
+
/^export\s+function\s+(use[A-Z][A-Za-z0-9_]*)\s*\(/,
|
|
35
|
+
/^export\s+class\s+([A-Z][A-Za-z0-9_]*)\s*(?:extends|\{)/
|
|
40
36
|
].freeze
|
|
41
37
|
|
|
42
38
|
# Patterns to detect usage in controller files.
|
|
@@ -66,7 +62,7 @@ module ReactManifest
|
|
|
66
62
|
|
|
67
63
|
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/PerceivedComplexity
|
|
68
64
|
def scan(classification)
|
|
69
|
-
warnings =
|
|
65
|
+
warnings = Set.new
|
|
70
66
|
symbol_index = {}
|
|
71
67
|
external_file_paths = {} # file_path => relative_require_path for external_roots files
|
|
72
68
|
|
|
@@ -79,7 +75,7 @@ module ReactManifest
|
|
|
79
75
|
symbols = extract_definitions(file_path)
|
|
80
76
|
symbols.each do |sym|
|
|
81
77
|
if symbol_index.key?(sym)
|
|
82
|
-
warnings
|
|
78
|
+
warnings.add("Duplicate symbol '#{sym}' in #{relative} (already from #{symbol_index[sym]})")
|
|
83
79
|
else
|
|
84
80
|
symbol_index[sym] = relative
|
|
85
81
|
end
|
|
@@ -131,7 +127,7 @@ module ReactManifest
|
|
|
131
127
|
files = js_files_in(ctrl[:path])
|
|
132
128
|
used = Set.new
|
|
133
129
|
|
|
134
|
-
warnings
|
|
130
|
+
warnings.add("Controller dir '#{ctrl[:name]}' has no JS/JSX files") if files.empty? && @config.verbose?
|
|
135
131
|
|
|
136
132
|
files.each do |file_path|
|
|
137
133
|
validate_naming(file_path, ctrl[:name], warnings)
|
|
@@ -150,7 +146,7 @@ module ReactManifest
|
|
|
150
146
|
Result.new(
|
|
151
147
|
symbol_index: symbol_index,
|
|
152
148
|
controller_usages: controller_usages,
|
|
153
|
-
warnings: warnings,
|
|
149
|
+
warnings: warnings.to_a,
|
|
154
150
|
shared_violations: shared_violations,
|
|
155
151
|
external_violations: external_violations
|
|
156
152
|
)
|
|
@@ -200,8 +196,8 @@ module ReactManifest
|
|
|
200
196
|
# Expected: <controller>_index, <controller>_show, <controller>_form, etc.
|
|
201
197
|
return if basename.start_with?("#{ctrl_name}_") || basename == ctrl_name
|
|
202
198
|
|
|
203
|
-
warnings
|
|
204
|
-
"'#{ctrl_name}_<action>.js.jsx' naming convention"
|
|
199
|
+
warnings.add("File '#{File.basename(file_path)}' in '#{ctrl_name}' does not follow " \
|
|
200
|
+
"'#{ctrl_name}_<action>.js.jsx' naming convention")
|
|
205
201
|
end
|
|
206
202
|
|
|
207
203
|
def detect_shared_violations(shared_file_paths, controller_symbol_index, warnings)
|
|
@@ -225,9 +221,9 @@ module ReactManifest
|
|
|
225
221
|
info = controller_symbol_index[sym]
|
|
226
222
|
violations << { shared_file: relative, symbol: sym,
|
|
227
223
|
controller: info[:controller], app_file: info[:file] }
|
|
228
|
-
warnings
|
|
224
|
+
warnings.add("Shared file '#{relative}' uses app-dir symbol '#{sym}' " \
|
|
229
225
|
"(from ux/app/#{info[:controller]}). " \
|
|
230
|
-
"Move '#{sym}' to a shared dir or the shared file will be incomplete."
|
|
226
|
+
"Move '#{sym}' to a shared dir or the shared file will be incomplete.")
|
|
231
227
|
end
|
|
232
228
|
end
|
|
233
229
|
end
|
|
@@ -255,9 +251,9 @@ module ReactManifest
|
|
|
255
251
|
info = controller_symbol_index[sym]
|
|
256
252
|
violations << { external_file: relative, symbol: sym,
|
|
257
253
|
controller: info[:controller], app_file: info[:file] }
|
|
258
|
-
warnings
|
|
254
|
+
warnings.add("External file '#{relative}' uses app-dir symbol '#{sym}' " \
|
|
259
255
|
"(from ux/app/#{info[:controller]}). " \
|
|
260
|
-
"Move '#{sym}' into a shared ux dir to avoid duplicate runtime declarations."
|
|
256
|
+
"Move '#{sym}' into a shared ux dir to avoid duplicate runtime declarations.")
|
|
261
257
|
end
|
|
262
258
|
end
|
|
263
259
|
end
|
|
@@ -273,8 +269,7 @@ module ReactManifest
|
|
|
273
269
|
|
|
274
270
|
fanout.each do |file, count|
|
|
275
271
|
if count > 3
|
|
276
|
-
warnings
|
|
277
|
-
"(consider ensuring it's in the shared bundle)"
|
|
272
|
+
warnings.add("High fan-out: '#{file}' is used by #{count} controllers")
|
|
278
273
|
end
|
|
279
274
|
end
|
|
280
275
|
end
|
|
@@ -282,10 +277,10 @@ module ReactManifest
|
|
|
282
277
|
def read_controller_file(file_path, warnings)
|
|
283
278
|
File.read(file_path, encoding: "utf-8")
|
|
284
279
|
rescue Errno::ENOENT, Errno::EACCES => e
|
|
285
|
-
warnings
|
|
280
|
+
warnings.add("Skipping #{file_path}: #{e.message}")
|
|
286
281
|
nil
|
|
287
282
|
rescue Encoding::InvalidByteSequenceError
|
|
288
|
-
warnings
|
|
283
|
+
warnings.add("Skipping #{file_path}: not valid UTF-8")
|
|
289
284
|
nil
|
|
290
285
|
end
|
|
291
286
|
|
|
@@ -5,11 +5,10 @@ module ReactManifest
|
|
|
5
5
|
# <%= react_bundle_tag %>
|
|
6
6
|
#
|
|
7
7
|
# Resolves which ux_*.js bundles to include based on controller_path:
|
|
8
|
-
# 1.
|
|
9
|
-
# 2.
|
|
10
|
-
# 3.
|
|
11
|
-
# 4.
|
|
12
|
-
# 5. Returns "" for pure ERB/HAML pages with no matching bundle, or when
|
|
8
|
+
# 1. Appends config.always_include (e.g. ["ux_main"])
|
|
9
|
+
# 2. Appends "ux_<controller_path>" if that bundle file exists
|
|
10
|
+
# 3. For namespaced controllers (admin/users): checks ux_admin_users, then ux_admin
|
|
11
|
+
# 4. Returns "" for pure ERB/HAML pages with no matching bundle, or when
|
|
13
12
|
# called outside a controller context (mailers, engines, etc.)
|
|
14
13
|
module ViewHelpers
|
|
15
14
|
def react_bundle_tag(**html_options)
|
|
@@ -22,10 +21,14 @@ module ReactManifest
|
|
|
22
21
|
return "".html_safe if bundles.empty?
|
|
23
22
|
|
|
24
23
|
# Record emitted bundles so react_component doesn't re-emit them.
|
|
25
|
-
emitted =
|
|
26
|
-
bundles.
|
|
24
|
+
emitted = emitted_bundles
|
|
25
|
+
fresh_bundles = bundles.reject { |b| emitted_bundle?(emitted, b) }
|
|
26
|
+
return "".html_safe if fresh_bundles.empty?
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
fresh_bundles.each { |b| emitted << b }
|
|
29
|
+
mark_bundle_tag_rendered
|
|
30
|
+
|
|
31
|
+
asset_names = fresh_bundles.map { |bundle| "#{bundle}.js" }
|
|
29
32
|
javascript_include_tag(*asset_names, extname: false, **html_options)
|
|
30
33
|
end
|
|
31
34
|
|
|
@@ -35,12 +38,13 @@ module ReactManifest
|
|
|
35
38
|
# This avoids strict dependence on controller_path -> bundle naming alignment.
|
|
36
39
|
def react_component(*args, **kwargs, &block)
|
|
37
40
|
html = super
|
|
41
|
+
return html if bundle_tag_rendered?
|
|
38
42
|
|
|
39
43
|
component_name = args.first
|
|
40
44
|
bundles = ReactManifest.resolve_bundles_for_component_direct(component_name)
|
|
41
45
|
return html if bundles.empty?
|
|
42
46
|
|
|
43
|
-
emitted =
|
|
47
|
+
emitted = emitted_bundles
|
|
44
48
|
|
|
45
49
|
new_tags = bundles.filter_map do |bundle|
|
|
46
50
|
next if emitted_bundle?(emitted, bundle)
|
|
@@ -64,5 +68,27 @@ module ReactManifest
|
|
|
64
68
|
def canonical_bundle_name(bundle)
|
|
65
69
|
bundle.to_s.split("/").last
|
|
66
70
|
end
|
|
71
|
+
|
|
72
|
+
def emitted_bundles
|
|
73
|
+
# ActionView can instantiate multiple helper contexts during one request.
|
|
74
|
+
# Store emitted bundles in request env so layout + template helpers dedupe.
|
|
75
|
+
if respond_to?(:request, true) && request
|
|
76
|
+
request.env["react_manifest.emitted_bundles"] ||= []
|
|
77
|
+
else
|
|
78
|
+
@_react_manifest_emitted_bundles ||= []
|
|
79
|
+
end
|
|
80
|
+
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
|
|
67
93
|
end
|
|
68
94
|
end
|
data/tasks/react_manifest.rake
CHANGED
|
@@ -130,12 +130,6 @@ namespace :react_manifest do
|
|
|
130
130
|
scan_result = scanner.scan(classification)
|
|
131
131
|
dep_map = ReactManifest::DependencyMap.new(scan_result)
|
|
132
132
|
dep_map.print_report
|
|
133
|
-
|
|
134
|
-
unless scan_result.warnings.empty?
|
|
135
|
-
puts "Warnings (#{scan_result.warnings.size}):"
|
|
136
|
-
scan_result.warnings.each { |w| puts " ⚠ #{w}" }
|
|
137
|
-
puts
|
|
138
|
-
end
|
|
139
133
|
end
|
|
140
134
|
|
|
141
135
|
desc "Analyze application*.js files — show what migrate_application would change"
|