From 8097a1b472b7ae39f4c0011bad6952b68cd76362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Pinz=C3=B3n=20Eslava?= Date: Thu, 19 Feb 2026 12:42:18 +1100 Subject: [PATCH] Fix ERB expression compilation when code contains a heredoc When an ERB output tag contains a heredoc (e.g. `<%= method_call <<~GRAPHQL, variables %>`), the closing parenthesis in the compiled Ruby must appear on its own line after the heredoc terminator. Without this, the compiled Ruby is syntactically invalid. Amp-Thread-ID: https://ampcode.com/threads/T-019c738c-52b4-772e-ab48-9bfb6b31e8f0 Co-authored-by: Amp --- lib/herb/engine.rb | 12 ++++++++++-- sig/herb/engine.rbs | 2 ++ test/engine/engine_test.rb | 13 +++++++++++++ ..._valid_Ruby_46fb4e6ec5bd55cc1041f3f88ad9508c.txt | 12 ++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 test/snapshots/engine/engine_test/test_0018_heredoc_with_trailing_arguments_compiles_to_valid_Ruby_46fb4e6ec5bd55cc1041f3f88ad9508c.txt diff --git a/lib/herb/engine.rb b/lib/herb/engine.rb index 1c75fdad6..cbc2b5c55 100644 --- a/lib/herb/engine.rb +++ b/lib/herb/engine.rb @@ -213,13 +213,17 @@ def add_expression(indicator, code) def add_expression_result(code) with_buffer { - @src << " << (" << code << comment_aware_newline(code) << ").to_s" + @src << " << (" << code + @src << "\n" if heredoc?(code) + @src << comment_aware_newline(code) << ").to_s" } end def add_expression_result_escaped(code) with_buffer { - @src << " << " << @escapefunc << "((" << code << comment_aware_newline(code) << "))" + @src << " << " << @escapefunc << "((" << code + @src << "\n" if heredoc?(code) + @src << comment_aware_newline(code) << "))" } end @@ -247,6 +251,10 @@ def comment_aware_newline(code) code.include?("#") ? "\n" : "" end + def heredoc?(code) + code.match?(/<<[~-]?\s*['"`]?\w/) + end + def add_postamble(postamble) terminate_expression @src << postamble diff --git a/sig/herb/engine.rbs b/sig/herb/engine.rbs index a904f6ed7..c7eff1298 100644 --- a/sig/herb/engine.rbs +++ b/sig/herb/engine.rbs @@ -53,6 +53,8 @@ module Herb def comment_aware_newline: (untyped code) -> untyped + def heredoc?: (untyped code) -> untyped + def add_postamble: (untyped postamble) -> untyped def with_buffer: () ?{ (?) -> untyped } -> untyped diff --git a/test/engine/engine_test.rb b/test/engine/engine_test.rb index 97d0b7376..48af2239d 100644 --- a/test/engine/engine_test.rb +++ b/test/engine/engine_test.rb @@ -164,5 +164,18 @@ class EngineTest < Minitest::Spec assert_compiled_snapshot(template, escape: false) end + + test "heredoc with trailing arguments compiles to valid Ruby" do + template = <<~ERB + <%= method_call <<~GRAPHQL, variables + query { + field + } + GRAPHQL + %> + ERB + + assert_compiled_snapshot(template) + end end end diff --git a/test/snapshots/engine/engine_test/test_0018_heredoc_with_trailing_arguments_compiles_to_valid_Ruby_46fb4e6ec5bd55cc1041f3f88ad9508c.txt b/test/snapshots/engine/engine_test/test_0018_heredoc_with_trailing_arguments_compiles_to_valid_Ruby_46fb4e6ec5bd55cc1041f3f88ad9508c.txt new file mode 100644 index 000000000..cc0970090 --- /dev/null +++ b/test/snapshots/engine/engine_test/test_0018_heredoc_with_trailing_arguments_compiles_to_valid_Ruby_46fb4e6ec5bd55cc1041f3f88ad9508c.txt @@ -0,0 +1,12 @@ +--- +source: "Engine::EngineTest#test_0018_heredoc with trailing arguments compiles to valid Ruby" +input: "{source: \"<%= method_call <<~GRAPHQL, variables\\n query {\\n field\\n }\\nGRAPHQL\\n%>\\n\", options: {}}" +--- +_buf = ::String.new; _buf << (method_call <<~GRAPHQL, variables + query { + field + } +GRAPHQL +).to_s; _buf << ' +'.freeze; +_buf.to_s