react-manifest-rails 0.2.14 → 0.2.15

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: 9c85309337765f3f24fae46e4cf3895e11e4a3a59b5cbfb96fa90f296f3ba48e
4
- data.tar.gz: b0f320a6ebf2a4210a4fac89d04f5609bd3d261a458f2db26fb92dd814b45465
3
+ metadata.gz: 292c5ccc8f0c7465c5b4fc176d7b28ff5c53e11bad16689ce9ebfcf028fdc75c
4
+ data.tar.gz: c77bf1b90b135fccc39b7c27ae2739c36cb891fcf504f33be2a87d3d415928d0
5
5
  SHA512:
6
- metadata.gz: 85edbe32bf32f739097e60779a34874d1c792db5ea40ed41e671e8b0552718d4549dfddb9b120d74bcce42a15bbc598ed0384c7e609cb6f0d4044fb80900098c
7
- data.tar.gz: d2728828f1fd930cd702aa35fb4ab5ab88b4219dff1fe996fa5cdb8491888b15bb25b0fd1d44c84aa9c0707cd80b87d3d3c2554b15f0723fd979bf484db4b3f1
6
+ metadata.gz: a7630b06c178a9d7433aaeef8eaec5c27a32319a8c6c191347cd614b1dde047ee8584cde6a2d16da4436975210479ef3cca12f62953562a6e1f49f3794829ef7
7
+ data.tar.gz: 28af64d5d5424b6a86b7c8d3b6be5e5b3dd865e500561adc2e4035bd1f38fbbb98ad2211c502003f604bb976967ae8a3eeb6e06c72ca470c06bd4800cef50e8d
@@ -22,6 +22,7 @@ module ReactManifest
22
22
  # to avoid partial reads from concurrent processes.
23
23
  #
24
24
  # Never touches application.js, application_dev.js, or files in exclude_paths.
25
+ # rubocop:disable Metrics/ClassLength
25
26
  class Generator
26
27
  HEADER = <<~JS.freeze
27
28
  // AUTO-GENERATED — DO NOT EDIT
@@ -41,11 +42,12 @@ module ReactManifest
41
42
  # written and others stale/missing.
42
43
  def run!
43
44
  classification = @classifier.classify
45
+ controller_context = build_controller_context(classification.controller_dirs)
44
46
 
45
47
  # Phase 1: build all content in memory — no I/O.
46
48
  manifests = []
47
49
  manifests << build_shared(classification.shared_dirs)
48
- classification.controller_dirs.each { |ctrl| manifests << build_controller(ctrl) }
50
+ classification.controller_dirs.each { |ctrl| manifests << build_controller(ctrl, controller_context) }
49
51
 
50
52
  migrate_legacy_manifests!
51
53
 
@@ -79,19 +81,87 @@ module ReactManifest
79
81
 
80
82
  # --------------------------------------------------------------- controller
81
83
 
82
- def build_controller(ctrl)
84
+ def build_controller(ctrl, controller_context)
83
85
  lines = header_lines
86
+ dep_requires = controller_dependency_requires(ctrl[:bundle_name], controller_context)
84
87
 
85
88
  files = js_files_in(ctrl[:path])
86
- if files.empty?
89
+ own_requires = files.map { |f| relative_require_path(f) }
90
+ all_requires = (dep_requires + own_requires).uniq
91
+
92
+ if all_requires.empty?
87
93
  lines << "// (no JSX files found in #{ctrl[:name]}/)"
88
94
  else
89
- files.each { |f| lines << "//= require #{relative_require_path(f)}" }
95
+ all_requires.each { |req| lines << "//= require #{req}" }
90
96
  end
91
97
 
92
98
  { filename: "#{ctrl[:bundle_name]}.js", content: "#{lines.join("\n")}\n" }
93
99
  end
94
100
 
101
+ def build_controller_context(controller_dirs)
102
+ bundle_files = {}
103
+ symbol_to_bundle = {}
104
+ dependencies = Hash.new { |h, k| h[k] = Set.new }
105
+
106
+ controller_dirs.each do |ctrl|
107
+ bundle_name = ctrl[:bundle_name]
108
+ files = js_files_in(ctrl[:path])
109
+ bundle_files[bundle_name] = files
110
+
111
+ files.each do |file_path|
112
+ extract_defined_symbols(file_path).each do |sym|
113
+ next unless sym.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
114
+
115
+ symbol_to_bundle[sym] ||= bundle_name
116
+ end
117
+ end
118
+ end
119
+
120
+ bundle_files.each do |bundle_name, files|
121
+ files.each do |file_path|
122
+ extract_used_component_symbols(file_path).each do |sym|
123
+ dep_bundle = symbol_to_bundle[sym]
124
+ next unless dep_bundle && dep_bundle != bundle_name
125
+
126
+ dependencies[bundle_name] << dep_bundle
127
+ end
128
+ end
129
+ end
130
+
131
+ {
132
+ bundle_files: bundle_files,
133
+ dependencies: dependencies
134
+ }
135
+ end
136
+
137
+ def controller_dependency_requires(bundle_name, controller_context)
138
+ deps = transitive_dependencies(bundle_name, controller_context[:dependencies])
139
+ deps.flat_map { |dep_bundle| controller_context[:bundle_files].fetch(dep_bundle, []) }
140
+ .map { |abs_path| relative_require_path(abs_path) }
141
+ .uniq
142
+ .sort
143
+ end
144
+
145
+ def transitive_dependencies(bundle_name, dependency_map)
146
+ ordered = []
147
+ visiting = Set.new
148
+ visited = Set.new
149
+
150
+ walk = lambda do |current|
151
+ return if visited.include?(current) || visiting.include?(current)
152
+
153
+ visiting << current
154
+ dependency_map.fetch(current, Set.new).each { |dep| walk.call(dep) }
155
+ visiting.delete(current)
156
+
157
+ visited << current
158
+ ordered << current unless current == bundle_name
159
+ end
160
+
161
+ walk.call(bundle_name)
162
+ ordered
163
+ end
164
+
95
165
  # --------------------------------------------------------------- write
96
166
 
97
167
  def write_manifest(filename, content)
@@ -207,6 +277,34 @@ module ReactManifest
207
277
  rel.sub(/\.js\.jsx$/, "").sub(/\.jsx$/, "").sub(/\.js$/, "")
208
278
  end
209
279
 
280
+ def extract_defined_symbols(file_path)
281
+ content = File.read(file_path, encoding: "utf-8")
282
+ symbols = []
283
+ ReactManifest::Scanner::DEFINITION_PATTERNS.each do |pattern|
284
+ content.scan(pattern) { |m| symbols << m[0] }
285
+ end
286
+ symbols.uniq
287
+ rescue Errno::ENOENT, Errno::EACCES, Encoding::InvalidByteSequenceError
288
+ []
289
+ end
290
+
291
+ def extract_used_component_symbols(file_path)
292
+ content = File.read(file_path, encoding: "utf-8")
293
+ symbols = []
294
+
295
+ content.scan(ReactManifest::Scanner::JSX_ELEMENT_PATTERN) { |m| symbols << m[0] }
296
+ content.scan(ReactManifest::Scanner::REACT_CREATE_PATTERN) { |m| symbols << m[0] }
297
+ content.scan(ReactManifest::Scanner::JSX_PROP_COMPONENT_PATTERN) { |m| symbols << m[0] }
298
+ content.scan(ReactManifest::Scanner::OBJECT_COMPONENT_PATTERN) { |m| symbols << m[0] }
299
+ content.scan(ReactManifest::Scanner::ARRAY_COMPONENT_LIST_PATTERN) do |m|
300
+ m[0].split(/\s*,\s*/).each { |sym| symbols << sym }
301
+ end
302
+
303
+ symbols.uniq
304
+ rescue Errno::ENOENT, Errno::EACCES, Encoding::InvalidByteSequenceError
305
+ []
306
+ end
307
+
210
308
  def auto_generated?(path)
211
309
  # Avoid TOCTOU: don't check existence separately — just attempt the read
212
310
  # and treat a missing/unreadable file as not auto-generated.
@@ -238,4 +336,5 @@ module ReactManifest
238
336
  "#{counts[:skipped_pinned] || 0} skipped (not auto-generated)"
239
337
  end
240
338
  end
339
+ # rubocop:enable Metrics/ClassLength
241
340
  end
@@ -1,3 +1,3 @@
1
1
  module ReactManifest
2
- VERSION = "0.2.14".freeze
2
+ VERSION = "0.2.15".freeze
3
3
  end
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.14
4
+ version: 0.2.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Noonan