Skip to content
Closed
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
14 changes: 5 additions & 9 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ PATH
activesupport (>= 6.0)
ast
benchmark
better_html
bundler
constant_resolver (>= 0.3)
herb
parallel (< 2)
parser
prism (>= 1.4.0)
Expand Down Expand Up @@ -50,13 +50,6 @@ GEM
ast (2.4.3)
base64 (0.3.0)
benchmark (0.5.0)
better_html (2.1.1)
actionview (>= 6.0)
activesupport (>= 6.0)
ast (~> 2.0)
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
bigdecimal (4.1.2)
builder (3.3.0)
byebug (11.1.3)
Expand All @@ -70,6 +63,10 @@ GEM
erb (4.0.4.1)
cgi (>= 0.3.3)
erubi (1.13.1)
herb (0.10.1-aarch64-linux-gnu)
herb (0.10.1-arm64-darwin)
herb (0.10.1-x86_64-darwin)
herb (0.10.1-x86_64-linux-gnu)
i18n (1.14.8)
concurrent-ruby (~> 1.0)
io-console (0.8.0)
Expand Down Expand Up @@ -185,7 +182,6 @@ GEM
rubydex (0.2.0-x86_64-darwin)
rubydex (0.2.0-x86_64-linux)
securerandom (0.4.1)
smart_properties (1.17.0)
sorbet (0.6.13184)
sorbet-static (= 0.6.13184)
sorbet-runtime (0.6.13184)
Expand Down
56 changes: 8 additions & 48 deletions lib/packwerk/parsers/erb.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
# typed: strict
# frozen_string_literal: true

require "ast/node"
require "better_html"
require "better_html/parser"
require "herb"
require "parser/source/buffer"

module Packwerk
module Parsers
class Erb
include ParserInterface

#: (?parser_class: untyped, ?ruby_parser: Ruby) -> void
def initialize(parser_class: BetterHtml::Parser, ruby_parser: Ruby.new)
@parser_class = parser_class #: singleton(BetterHtml::Parser)
#: (?ruby_parser: Ruby) -> void
def initialize(ruby_parser: Ruby.new)
@ruby_parser = ruby_parser
end

Expand All @@ -25,53 +22,16 @@ def call(io:, file_path: "<unknown>")
parse_buffer(buffer, file_path: file_path)
end

# `Herb.extract_ruby` returns the Ruby parts of the ERB source with whitespace padding
# so character positions match the original file — that gives Packwerk's downstream
# reference offenses accurate line/column info in ERB templates.
#: (Parser::Source::Buffer buffer, file_path: String) -> AST::Node?
def parse_buffer(buffer, file_path:)
parser = @parser_class.new(buffer, template_language: :html)
to_ruby_ast(parser.ast, file_path)
ruby_source = Herb.extract_ruby(buffer.source)
@ruby_parser.call(io: StringIO.new(ruby_source), file_path: file_path)
rescue EncodingError => e
result = ParseResult.new(file: file_path, message: e.message)
raise Parsers::ParseError, result
rescue Parser::SyntaxError => e
result = ParseResult.new(file: file_path, message: "Syntax error: #{e}")
raise Parsers::ParseError, result
end

private

#: ((::AST::Node & Object) erb_ast, String file_path) -> ::AST::Node?
def to_ruby_ast(erb_ast, file_path)
# Note that we're not using the source location (line/column) at the moment, but if we did
# care about that, we'd need to tweak this to insert empty lines and spaces so that things
# line up with the ERB file
nodes = code_nodes(erb_ast) #: as !nil
code_pieces = nodes.map do |node|
node #: as ::AST::Node
.children.first
end

@ruby_parser.call(
io: StringIO.new(code_pieces.join("\n")),
file_path: file_path,
)
end

#: ((::AST::Node | String)? node) ?{ (::AST::Node arg0) -> void } -> (Enumerator[::AST::Node] | Array[String])?
def code_nodes(node, &block)
return enum_for(:code_nodes, node) unless block
return unless node.is_a?(::AST::Node)

yield node if node.type == :code

# Skip descending into an ERB comment node, which may contain code nodes
if node.type == :erb
first_child = node.children.first
return if first_child&.type == :indicator && first_child&.children&.first == "#"
end

node.children.each do |child|
code_nodes(child, &block)
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion packwerk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ Gem::Specification.new do |spec|
spec.add_dependency("prism", ">= 1.4.0")

# For ERB parsing
spec.add_dependency("better_html")
spec.add_dependency("herb")
end
Loading
Loading