Skip to content

Convert ERB code into Ruby before type-checking#1836

Open
felixefelip wants to merge 4 commits intosoutaro:masterfrom
felixefelip:support_erb/convert_erb_code_into_ruby_before_type_checking
Open

Convert ERB code into Ruby before type-checking#1836
felixefelip wants to merge 4 commits intosoutaro:masterfrom
felixefelip:support_erb/convert_erb_code_into_ruby_before_type_checking

Conversation

@felixefelip
Copy link
Copy Markdown

@felixefelip felixefelip commented Sep 8, 2025

ERB to Ruby code conversion is needed to type-check ERB.

Input:

<html>
  <body>
    <ul>
    <% languages.each do |langage| %>
      <li><%= language %>
    <% end %>
    </ul>
  </body>
</html>

Output: (replace HTML tags with whitespace)

      
        
        
       languages.each do |langage|   
              language   
       end   
         
         
       

refs: #1409

Use class << self for private methods
@felixefelip felixefelip force-pushed the support_erb/convert_erb_code_into_ruby_before_type_checking branch from fd6e3c4 to 63cbedc Compare September 8, 2025 15:34
@ParadoxV5
Copy link
Copy Markdown
Contributor

Good idea, but I suggest making use of ERB::Compiler so you don’t have to parse things yourself.

Additionally, ERB isn’t limited to Rails, let alone HTML.
Your parser probably doesn’t care, but the test appears designed for Rails only.

@felixefelip
Copy link
Copy Markdown
Author

@ParadoxV5 thanks for the review!

Good idea, but I suggest making use of ERB::Compiler so you don’t have to parse things yourself.

The ERB::Compiler looks great, I'm going to use it.

Additionally, ERB isn’t limited to Rails, let alone HTML. Your parser probably doesn’t care, but the test appears designed for Rails only.

My fault, I'm used ERB only with Rails, I'm going to adjust the tests examples.

Regarding moving this to RBS, I will analyze the projects better to give an opinion.

@felixefelip
Copy link
Copy Markdown
Author

felixefelip commented Oct 19, 2025

@ParadoxV5 I have not found a way to use ERB::Compiler for the proposal of this PR. Could you instruct me on how to use it, please?

Alternatively, I found the Herb that almost provides the parser we need. Herb just doesn't provide a public API with Ruby code with semicolons, but I'm trying to make a contribution to doing that here. It's ok to use that?

@ParadoxV5
Copy link
Copy Markdown
Contributor

ParadoxV5 commented Oct 25, 2025

@felixefelip:

Could you instruct me on how to use it, please?

The documentation above class ERB::Compiler reads: (formatting mine)

expand/collapse section

Internally ERB does something like this to generate the code returned by ERB#src:

compiler = ERB::Compiler.new('<>')
compiler.pre_cmd    = ["_erbout=+''"]
compiler.put_cmd    = "_erbout.<<"
compiler.insert_cmd = "_erbout.<<"
compiler.post_cmd   = ["_erbout"]

code, enc = compiler.compile("Got <%= obj %>!\n")
puts code

Generates:

  #coding:UTF-8
  _erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout

By default the output is sent to the print method. For example:

compiler = ERB::Compiler.new('<>')
code, enc = compiler.compile("Got <%= obj %>!\n")
puts code

Generates:

#coding:UTF-8
print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze

That means, ERB::Compiler.new(flags).compile(erb_file).first generates a string containing code that ERB executes to process ERB files, with Ruby instructions as-is and ERB text (e.g., HTML) outputted via Kernel#print.
You can pass this generated code to some Steep API (or dump to a temporary file if all fails).
You can also optionally configure an ERB::Compiler to be more Steep-user-friendly.
(There might be a way to completely ignore the verbatim texts, too.)

erb = ERB::Compiler.new nil
erb.put_cmd = erb.insert_cmd= "\n"
puts erb.compile('Got <%= process(obj) %>!').first
#coding:UTF-8

 "Got ".freeze;
(( process(obj) ).to_s);
 "!".freeze

It's ok to use that?

Assuming Herb.extract_ruby works with non-HTML as well, then my only concern is that ERB is still preferable for its popularity and StdLib status.

@felixefelip
Copy link
Copy Markdown
Author

felixefelip commented Oct 27, 2025

Thanks for the instructions @ParadoxV5!

I might have misunderstood some part, but I still couldn't to handle the verbatim texts like .freeze with the ERB::Compiler, and it's important the parsed code to preserve the exact number and position characters of original source code to display the Steep diagnostics. ERB::Compiler seems to be more designed to use for Kernel#print and not for a parser.

marcoroth added a commit to marcoroth/herb that referenced this pull request Feb 13, 2026
)

This pull request adds `semicolons`, `comments`, and
`preserve_positions` options to `Herb.extract_ruby` across all language
bindings (Ruby, JavaScript, Java, Rust).

This was originally requested to use the extract Ruby code with
semicolons in Steep/RBS, more specifically in this PR:
soutaro/steep#1836. It's because we need the
Ruby code with semicolons to display the static typing errors correctly.
I think this functionality could also be used in others projects like
Sorbet and Packwerk.

Resolves #100

---------

Co-authored-by: Felipe Felix <felipe.felix@maino.com.br>
Co-authored-by: Everton <e.santos081992@gmail.com>
Co-authored-by: Marco Roth <marco.roth@intergga.ch>
@soutaro soutaro added this to the Steep 2.0 milestone Mar 13, 2026
@felixefelip felixefelip force-pushed the support_erb/convert_erb_code_into_ruby_before_type_checking branch from 78925c9 to 4867dce Compare March 22, 2026 07:25
@felixefelip felixefelip force-pushed the support_erb/convert_erb_code_into_ruby_before_type_checking branch from 4867dce to 4841deb Compare March 22, 2026 07:32
@felixefelip
Copy link
Copy Markdown
Author

felixefelip commented Mar 22, 2026

Hello @ParadoxV5,

Sorry for the long time to update this PR.

I managed the merge of the adjustment in Herb gem to make Herb compatible for this PR, as I mentioned here #1836 (comment)

So I already added it on this PR in this commit: 4841deb

It's okay to proceed in this way?

@soutaro soutaro modified the milestones: Steep 2.0, Steep 2.1 Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants