Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ Style/AccessorGrouping:
Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: diff_comma

Metrics/BlockNesting:
Exclude:
- lib/herb/cli.rb

Metrics/CyclomaticComplexity:
Max: 15
Exclude:
Expand Down
74 changes: 74 additions & 0 deletions lib/herb/action_view/render_analyzer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ def check!

print_results(result, duration)

warnings = check_dependencies(erb_files, view_root)
print_dependency_warnings(warnings) if warnings.any?

result.issues?
end

Expand Down Expand Up @@ -673,6 +676,77 @@ def print_results(result, duration)
puts ""
end

def check_dependencies(erb_files, view_root)
require_relative "template_dependencies"

dep_analyzer = TemplateDependencies.new(@project_path)
dep_analyzer.scan_helpers!

warnings = [] #: Array[Hash[Symbol, untyped]]

erb_files.each do |file|
result = dep_analyzer.analyze(file)
is_partial = File.basename(file).start_with?("_")
relative = relative_path(file)

if is_partial && result.instance_variables.any?
result.instance_variables.each do |ivar|
warnings << { type: :ivar_in_partial, file: relative, ivar: ivar }
end
end

if result.unknown_calls.any?
result.unknown_calls.each do |call|
warnings << { type: :unknown_call, file: relative, call: call }
end
end
end

warnings
rescue StandardError => e
warn "Warning: Dependency analysis failed: #{e.message}"
[] #: Array[Hash[Symbol, untyped]]
end

def print_dependency_warnings(warnings)
ivar_warnings = warnings.select { |w| w[:type] == :ivar_in_partial }
unknown_warnings = warnings.select { |w| w[:type] == :unknown_call }

puts ""
puts " #{bold("Dependency warnings:")}"
puts ""

if ivar_warnings.any?
grouped = ivar_warnings.group_by { |w| w[:file] }
puts " #{yellow("Instance variables in partials")} #{dimmed("(#{grouped.size} #{pluralize(grouped.size, "file")})")}"
puts " #{dimmed("Partials should receive data as locals for reactivity tracing.")}"
puts ""

grouped.each do |file, file_warnings|
ivars = file_warnings.map { |w| w[:ivar] }.uniq.sort
puts " #{yellow(file)}"
ivars.each { |ivar| puts " #{dimmed(ivar)}" }
puts ""
end
end

if unknown_warnings.any?
grouped = unknown_warnings.group_by { |w| w[:file] }
puts " #{yellow("Unknown method calls")} #{dimmed("(#{grouped.size} #{pluralize(grouped.size, "file")})")}"
puts " #{dimmed("Methods not in the ActionView helper registry or app/helpers/.")}"
puts ""

grouped.each do |file, file_warnings|
calls = file_warnings.map { |w| w[:call] }.uniq.sort
puts " #{yellow(file)}"
calls.each { |call| puts " #{dimmed(call)}" }
puts ""
end

puts ""
end
end

def find_erb_files
patterns = configuration.file_include_patterns
exclude = configuration.file_exclude_patterns
Expand Down
Loading
Loading