diff --git a/lib/herb/engine.rb b/lib/herb/engine.rb index db193a916..5bfb8e4d5 100644 --- a/lib/herb/engine.rb +++ b/lib/herb/engine.rb @@ -131,9 +131,17 @@ def initialize(input, properties = {}) action_view_helpers = @optimize && source_may_contain_action_view_helpers?(input) transform_conditionals = @optimize && action_view_helpers - parse_result = ::Herb.parse(input, **@parser_options, track_whitespace: true, - action_view_helpers: action_view_helpers, - transform_conditionals: transform_conditionals) + render_nodes = @optimize && input.include?("render") + + parse_result = ::Herb.parse( + input, + **@parser_options, + track_whitespace: true, + action_view_helpers: action_view_helpers, + transform_conditionals: transform_conditionals, + render_nodes: render_nodes + ) + ast = parse_result.value parser_errors = parse_result.errors @@ -159,7 +167,19 @@ def initialize(input, properties = {}) ast.accept(visitor) end - compiler = Compiler.new(self, properties) + compiler_options = properties.dup + + if @optimize && render_nodes && properties.fetch(:inline_render, false) + require_relative "engine/render_inliner" + + compiler_options[:render_inliner] = RenderInliner.new( + self, + project_path: @project_path, + filename: @relative_file_path + ) + end + + compiler = Compiler.new(self, compiler_options) ast.accept(compiler) diff --git a/lib/herb/engine/compiler.rb b/lib/herb/engine/compiler.rb index bdf551baa..201a074b5 100644 --- a/lib/herb/engine/compiler.rb +++ b/lib/herb/engine/compiler.rb @@ -18,6 +18,7 @@ def initialize(engine, options = {}) @engine = engine @escape = options.fetch(:escape) { options.fetch(:escape_html, false) } + @render_inliner = options[:render_inliner] @tokens = [] #: Array[untyped] @element_stack = [] #: Array[String] @context_stack = [:html_content] @@ -215,6 +216,18 @@ def visit_erb_content_node(node) process_erb_tag(node) end + def visit_erb_render_node(node) + if @render_inliner&.can_inline?(node) + if @render_inliner.collection?(node) + inline_collection(node) + else + inline_partial(node) + end + else + process_erb_tag(node) + end + end + def visit_erb_control_node(node, &) if node.content apply_trim(node, node.content.value.strip) @@ -358,6 +371,51 @@ def visit_erb_control_with_parts(node, *parts) private + def inline_partial(node) + resolved_path = @render_inliner.resolve_path(node) + return process_erb_tag(node) unless resolved_path + + source = File.read(resolved_path) + locals = @render_inliner.local_assignments(node) + + @render_inliner.push(resolved_path) + + add_code("begin") + locals.each { |name, value| add_code("; #{name} = (#{value})") } + add_code(";") + + partial_ast = @render_inliner.parse(source) + partial_ast&.accept(self) + + add_code("; end;") + + @render_inliner.pop(resolved_path) + end + + def inline_collection(node) + resolved_path = @render_inliner.resolve_path(node) + return process_erb_tag(node) unless resolved_path + + source = File.read(resolved_path) + collection_expr = @render_inliner.collection_expression(node) + item_name = @render_inliner.collection_item_name(node) + counter_name = "#{item_name}_counter" + locals = @render_inliner.local_assignments(node) + + @render_inliner.push(resolved_path) + + add_code("; __herb_collection = (#{collection_expr}); __herb_collection.each_with_index do |#{item_name}, #{counter_name}|") + locals.each { |name, value| add_code("; #{name} = (#{value})") } + add_code(";") + + partial_ast = @render_inliner.parse(source) + partial_ast&.accept(self) + + add_code("; end;") + + @render_inliner.pop(resolved_path) + end + def check_for_escaped_erb_tag!(opening) return unless opening.start_with?("<%%") diff --git a/lib/herb/engine/render_inliner.rb b/lib/herb/engine/render_inliner.rb new file mode 100644 index 000000000..b67c168de --- /dev/null +++ b/lib/herb/engine/render_inliner.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +module Herb + class Engine + class RenderInliner + def initialize(engine, options = {}) + @engine = engine + @project_path = options[:project_path] || Pathname.new(Dir.pwd) + @filename = options[:filename] + @view_root = find_view_root + @source_directory = find_source_directory + @inlining_stack = Set.new + end + + def can_inline?(node) + return false unless @view_root + return false unless node.static_partial? + return false if node.body&.any? + + return can_inline_collection?(node) if node.keywords&.collection + + resolved = resolve_path(node) + return false unless resolved + return false if @inlining_stack.include?(resolved.to_s) + return false unless safe_to_inline?(resolved) + + true + end + + def can_inline_collection?(node) + return false unless node.static_partial? + return false if node.keywords&.spacer_template + + resolved = resolve_path(node) + return false unless resolved + return false if @inlining_stack.include?(resolved.to_s) + return false unless safe_to_inline?(resolved) + + true + end + + def collection?(node) + !!node.keywords&.collection + end + + def collection_expression(node) + node.keywords&.collection&.value + end + + def collection_item_name(node) + as_name = node.keywords&.as_name&.value + return as_name if as_name + + partial = node.partial_path + return nil unless partial + + File.basename(partial) + end + + def resolve_path(node) + node.resolve(view_root: @view_root, source_directory: @source_directory) + end + + def optimizable_renders(ast) + collector = OptimizableRenderCollector.new(self) + ast.accept(collector) + collector.results + end + + def local_assignments(node) + locals = {} #: Hash[String, String] + + node.keywords&.locals&.each do |local| + name = local.name&.value + value = local.value&.content + + next unless name && value + + value = name if value == "#{name}:" # Shorthand hash syntax + + locals[name] = value + end + + locals + end + + def parse(source) + result = ::Herb.parse( + source, + render_nodes: true, + track_whitespace: true, + action_view_helpers: true, + transform_conditionals: true + ) + + result.value + end + + def push(path) + @inlining_stack.add(path.to_s) + end + + def pop(path) + @inlining_stack.delete(path.to_s) + end + + private + + def safe_to_inline?(file_path) + source = File.read(file_path) + + return false if source.include?("content_for") + return false if source.match?(/\byield\b/) + return false if source.include?("local_assigns") + + true + end + + def find_view_root + candidates = [ + @project_path.join("app", "views") + ] + + candidates.find(&:directory?) + end + + def find_source_directory + return nil unless @filename && @view_root + + dir = Pathname.new(@filename).dirname + @view_root.join(dir) + end + end + + class OptimizableRenderCollector < ::Herb::Visitor + attr_reader :results + + def initialize(inliner) + super() + @inliner = inliner + @results = [] #: Array[Hash[Symbol, untyped]] + end + + def visit_erb_render_node(node) + if @inliner.can_inline?(node) + entry = { + node: node, + partial_path: node.partial_path, + resolved_path: @inliner.resolve_path(node), + locals: @inliner.local_assignments(node), + collection: @inliner.collection?(node), + } + + if entry[:collection] + entry[:collection_expression] = @inliner.collection_expression(node) + entry[:item_name] = @inliner.collection_item_name(node) + end + + @results << entry + end + + visit_child_nodes(node) + end + end + end +end diff --git a/sig/herb/engine/compiler.rbs b/sig/herb/engine/compiler.rbs index 638c28149..672b2ee30 100644 --- a/sig/herb/engine/compiler.rbs +++ b/sig/herb/engine/compiler.rbs @@ -61,6 +61,8 @@ module Herb def visit_erb_content_node: (untyped node) -> untyped + def visit_erb_render_node: (untyped node) -> untyped + def visit_erb_control_node: (untyped node) ?{ (?) -> untyped } -> untyped def visit_erb_if_node: (untyped node) -> untyped @@ -101,6 +103,10 @@ module Herb private + def inline_partial: (untyped node) -> untyped + + def inline_collection: (untyped node) -> untyped + def check_for_escaped_erb_tag!: (untyped opening) -> untyped def current_context: () -> untyped diff --git a/sig/herb/engine/render_inliner.rbs b/sig/herb/engine/render_inliner.rbs new file mode 100644 index 000000000..693fcffc6 --- /dev/null +++ b/sig/herb/engine/render_inliner.rbs @@ -0,0 +1,47 @@ +# Generated from lib/herb/engine/render_inliner.rb with RBS::Inline + +module Herb + class Engine + class RenderInliner + def initialize: (untyped engine, ?untyped options) -> untyped + + def can_inline?: (untyped node) -> untyped + + def can_inline_collection?: (untyped node) -> untyped + + def collection?: (untyped node) -> untyped + + def collection_expression: (untyped node) -> untyped + + def collection_item_name: (untyped node) -> untyped + + def resolve_path: (untyped node) -> untyped + + def optimizable_renders: (untyped ast) -> untyped + + def local_assignments: (untyped node) -> untyped + + def parse: (untyped source) -> untyped + + def push: (untyped path) -> untyped + + def pop: (untyped path) -> untyped + + private + + def safe_to_inline?: (untyped file_path) -> untyped + + def find_view_root: () -> untyped + + def find_source_directory: () -> untyped + end + + class OptimizableRenderCollector < ::Herb::Visitor + attr_reader results: untyped + + def initialize: (untyped inliner) -> untyped + + def visit_erb_render_node: (untyped node) -> untyped + end + end +end diff --git a/test/engine/render_inliner_test.rb b/test/engine/render_inliner_test.rb new file mode 100644 index 000000000..9b104a8d2 --- /dev/null +++ b/test/engine/render_inliner_test.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require_relative "../test_helper" +require_relative "../snapshot_utils" +require_relative "../../lib/herb/engine" + +module Engine + class RenderInlinerTest < Minitest::Spec + include SnapshotUtils + + PROJECT_PATH = "test/fixtures/render_inliner" + + test "inlines a simple partial" do + assert_compiled_snapshot('<%= render "shared/header" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "inlines partial with locals" do + assert_compiled_snapshot('<%= render partial: "posts/card", locals: { title: @post.title } %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "does not inline dynamic render" do + assert_compiled_snapshot("<%= render @product %>", filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "does not inline when optimize is false" do + assert_compiled_snapshot('<%= render "shared/header" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: false, escape: false) + end + + test "does not inline partial with yield" do + assert_compiled_snapshot('<%= render "shared/wrapper" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "does not inline partial with content_for" do + assert_compiled_snapshot('<%= render "shared/sidebar" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "does not inline partial with local_assigns" do + assert_compiled_snapshot('<%= render "shared/item" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "inlines collection renders" do + assert_compiled_snapshot('<%= render partial: "posts/post", collection: @posts %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "inlines collection with as: option" do + assert_compiled_snapshot('<%= render partial: "posts/post", collection: @posts, as: :item %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "does not inline collection with spacer_template" do + assert_compiled_snapshot('<%= render partial: "posts/post", collection: @posts, spacer_template: "posts/spacer" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "inlines nested partials" do + assert_compiled_snapshot('<%= render "shared/outer" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "detects circular references and falls back" do + assert_compiled_snapshot('<%= render "shared/a" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "scopes locals with begin/end block" do + assert_compiled_snapshot('<%= render partial: "posts/card", locals: { name: "test" } %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "falls back for unresolvable partial" do + assert_compiled_snapshot('<%= render "nonexistent/missing" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "handles Ruby 3.1 shorthand hash locals" do + assert_compiled_snapshot('<%= render partial: "posts/card", locals: { title: } %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "inlines shorthand render with inline locals" do + assert_compiled_snapshot('<%= render "posts/card", title: "Title" %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + + test "inlines shorthand render with multiple inline locals" do + assert_compiled_snapshot('<%= render "posts/card", title: "Title", name: @user.name %>', filename: "app/views/posts/index.html.erb", project_path: PROJECT_PATH, optimize: true, inline_render: true, escape: false) + end + end +end diff --git a/test/fixtures/render_inliner/app/views/posts/_card.html.erb b/test/fixtures/render_inliner/app/views/posts/_card.html.erb new file mode 100644 index 000000000..90f33f56e --- /dev/null +++ b/test/fixtures/render_inliner/app/views/posts/_card.html.erb @@ -0,0 +1 @@ +
<%= title %>
diff --git a/test/fixtures/render_inliner/app/views/posts/_post.html.erb b/test/fixtures/render_inliner/app/views/posts/_post.html.erb new file mode 100644 index 000000000..fc9a37462 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/posts/_post.html.erb @@ -0,0 +1 @@ +
Post
diff --git a/test/fixtures/render_inliner/app/views/posts/_spacer.html.erb b/test/fixtures/render_inliner/app/views/posts/_spacer.html.erb new file mode 100644 index 000000000..e123ba7e3 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/posts/_spacer.html.erb @@ -0,0 +1 @@ +
diff --git a/test/fixtures/render_inliner/app/views/shared/_a.html.erb b/test/fixtures/render_inliner/app/views/shared/_a.html.erb new file mode 100644 index 000000000..df26f4170 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_a.html.erb @@ -0,0 +1 @@ +<%= render "shared/b" %> diff --git a/test/fixtures/render_inliner/app/views/shared/_b.html.erb b/test/fixtures/render_inliner/app/views/shared/_b.html.erb new file mode 100644 index 000000000..d5ed9a10b --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_b.html.erb @@ -0,0 +1 @@ +<%= render "shared/a" %> diff --git a/test/fixtures/render_inliner/app/views/shared/_header.html.erb b/test/fixtures/render_inliner/app/views/shared/_header.html.erb new file mode 100644 index 000000000..ae6b431c0 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_header.html.erb @@ -0,0 +1 @@ +
Hello
diff --git a/test/fixtures/render_inliner/app/views/shared/_inner.html.erb b/test/fixtures/render_inliner/app/views/shared/_inner.html.erb new file mode 100644 index 000000000..291277aa0 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_inner.html.erb @@ -0,0 +1 @@ +Inner diff --git a/test/fixtures/render_inliner/app/views/shared/_item.html.erb b/test/fixtures/render_inliner/app/views/shared/_item.html.erb new file mode 100644 index 000000000..66869596c --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_item.html.erb @@ -0,0 +1 @@ +<%= local_assigns[:name] %> diff --git a/test/fixtures/render_inliner/app/views/shared/_outer.html.erb b/test/fixtures/render_inliner/app/views/shared/_outer.html.erb new file mode 100644 index 000000000..5d9eedc47 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_outer.html.erb @@ -0,0 +1 @@ +

Outer<%= render "shared/inner" %>

diff --git a/test/fixtures/render_inliner/app/views/shared/_sidebar.html.erb b/test/fixtures/render_inliner/app/views/shared/_sidebar.html.erb new file mode 100644 index 000000000..86bce3b61 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_sidebar.html.erb @@ -0,0 +1 @@ +<%= content_for :sidebar %> diff --git a/test/fixtures/render_inliner/app/views/shared/_wrapper.html.erb b/test/fixtures/render_inliner/app/views/shared/_wrapper.html.erb new file mode 100644 index 000000000..1c26cb986 --- /dev/null +++ b/test/fixtures/render_inliner/app/views/shared/_wrapper.html.erb @@ -0,0 +1 @@ +
<%= yield %>
diff --git a/test/snapshots/engine/evaluation_test/test_0022_complex_real_world_example_d8e54f09b6cf5d8199c941a30266ede6.txt b/test/snapshots/engine/evaluation_test/test_0022_complex_real_world_example_d8e54f09b6cf5d8199c941a30266ede6.txt new file mode 100644 index 000000000..722cb3b32 --- /dev/null +++ b/test/snapshots/engine/evaluation_test/test_0022_complex_real_world_example_d8e54f09b6cf5d8199c941a30266ede6.txt @@ -0,0 +1,66 @@ +--- +source: "Engine::EvaluationTest#test_0022_complex real world example" +input: "{source: \"\\n\\n \\n <%= page_title %>\\n \\\">\\n \\n \\\">\\n
\\n

<%= site_name %>

\\n \\n
\\n \\n
\\n <% if flash_message %>\\n
\\\">\\n <%= flash_message %>\\n
\\n <% end %>\\n \\n
\\n <% unless posts.empty? %>\\n <% posts.each_with_index do |post, index| %>\\n
\\\">\\n

<%= post.title %>

\\n

\\n By <%= post.author %> on <%= post.date.strftime(\\\"%B %d, %Y\\\") %>\\n

\\n
\\n <%= post.excerpt %>\\n
\\n <% if post.tags.any? %>\\n
\\n <% post.tags.each do |tag| %>\\n <%= tag %>\\n <% end %>\\n
\\n <% end %>\\n
\\n <% end %>\\n <% else %>\\n

No posts available.

\\n <% end %>\\n
\\n
\\n \\n \\n \\n\\n\", locals: {page_title: \"My Blog\", meta_description: \"A blog about programming\", body_classes: [\"blog\", \"home\"], site_name: \"My Awesome Blog\", navigation_items: [{title: \"Home\", url: \"/\", active: true}, {title: \"About\", url: \"/about\", active: false}, {title: \"Contact\", url: \"/contact\", active: false}], flash_message: \"Welcome!\", flash_type: \"success\", posts: [#, excerpt=\"This is the first post excerpt.\", tags=[\"ruby\", \"programming\"]>, #, excerpt=\"This is the second post excerpt.\", tags=[\"html\", \"css\"]>]}, options: {escape: false}}" +--- + + + + My Blog + + + +
+

My Awesome Blog

+ +
+ +
+
+ Welcome! +
+ +
+
+

First Post

+

+ By John Doe on January 15, 2024 +

+
+ This is the first post excerpt. +
+
+ ruby + programming +
+
+
+

Second Post

+

+ By Jane Smith on February 01, 2024 +

+
+ This is the second post excerpt. +
+
+ html + css +
+
+
+
+ +
+

© 2025 My Awesome Blog

+
+ + diff --git a/test/snapshots/engine/render_inliner_test/test_0001_inlines_a_simple_partial_603df3fe395b10fa9c997c61ff9a15f9.txt b/test/snapshots/engine/render_inliner_test/test_0001_inlines_a_simple_partial_603df3fe395b10fa9c997c61ff9a15f9.txt new file mode 100644 index 000000000..c5059c766 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0001_inlines_a_simple_partial_603df3fe395b10fa9c997c61ff9a15f9.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0001_inlines a simple partial" +input: "{source: \"<%= render \\\"shared/header\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ;; _buf << '
Hello
+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0002_inlines_partial_with_locals_95135940db611a0a5e761afc44f22616.txt b/test/snapshots/engine/render_inliner_test/test_0002_inlines_partial_with_locals_95135940db611a0a5e761afc44f22616.txt new file mode 100644 index 000000000..a70a2abb4 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0002_inlines_partial_with_locals_95135940db611a0a5e761afc44f22616.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0002_inlines partial with locals" +input: "{source: \"<%= render partial: \\\"posts/card\\\", locals: { title: @post.title } %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ; title = (@post.title); ;; _buf << '
'.freeze; _buf << (title).to_s; _buf << '
+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0003_does_not_inline_dynamic_render_d4b9ae2333f9edd9d350a39182291bce.txt b/test/snapshots/engine/render_inliner_test/test_0003_does_not_inline_dynamic_render_d4b9ae2333f9edd9d350a39182291bce.txt new file mode 100644 index 000000000..1b7771c4b --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0003_does_not_inline_dynamic_render_d4b9ae2333f9edd9d350a39182291bce.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::RenderInlinerTest#test_0003_does not inline dynamic render" +input: "{source: \"<%= render @product %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; _buf << (render @product).to_s; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0004_does_not_inline_when_optimize_is_false_edfdb37e0ee42d3137ab9f6954974e6e.txt b/test/snapshots/engine/render_inliner_test/test_0004_does_not_inline_when_optimize_is_false_edfdb37e0ee42d3137ab9f6954974e6e.txt new file mode 100644 index 000000000..e3ce4e2fc --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0004_does_not_inline_when_optimize_is_false_edfdb37e0ee42d3137ab9f6954974e6e.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::RenderInlinerTest#test_0004_does not inline when optimize is false" +input: "{source: \"<%= render \\\"shared/header\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: false, escape: false}}" +--- +_buf = ::String.new; _buf << (render "shared/header").to_s; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0005_does_not_inline_partial_with_yield_614327996b5f80569bb8967a712a666d.txt b/test/snapshots/engine/render_inliner_test/test_0005_does_not_inline_partial_with_yield_614327996b5f80569bb8967a712a666d.txt new file mode 100644 index 000000000..e865db3c4 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0005_does_not_inline_partial_with_yield_614327996b5f80569bb8967a712a666d.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::RenderInlinerTest#test_0005_does not inline partial with yield" +input: "{source: \"<%= render \\\"shared/wrapper\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; _buf << (render "shared/wrapper").to_s; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0006_does_not_inline_partial_with_content_for_a745f9a05124c3f3ad68036a4bdd192f.txt b/test/snapshots/engine/render_inliner_test/test_0006_does_not_inline_partial_with_content_for_a745f9a05124c3f3ad68036a4bdd192f.txt new file mode 100644 index 000000000..871e41eac --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0006_does_not_inline_partial_with_content_for_a745f9a05124c3f3ad68036a4bdd192f.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::RenderInlinerTest#test_0006_does not inline partial with content_for" +input: "{source: \"<%= render \\\"shared/sidebar\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; _buf << (render "shared/sidebar").to_s; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0007_does_not_inline_partial_with_local_assigns_270738f467068b11565be247f854bf68.txt b/test/snapshots/engine/render_inliner_test/test_0007_does_not_inline_partial_with_local_assigns_270738f467068b11565be247f854bf68.txt new file mode 100644 index 000000000..9e8974d30 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0007_does_not_inline_partial_with_local_assigns_270738f467068b11565be247f854bf68.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::RenderInlinerTest#test_0007_does not inline partial with local_assigns" +input: "{source: \"<%= render \\\"shared/item\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; _buf << (render "shared/item").to_s; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0008_inlines_collection_renders_2b36b6c15d03c691c65df99d2dcbb6bf.txt b/test/snapshots/engine/render_inliner_test/test_0008_inlines_collection_renders_2b36b6c15d03c691c65df99d2dcbb6bf.txt new file mode 100644 index 000000000..a59324efd --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0008_inlines_collection_renders_2b36b6c15d03c691c65df99d2dcbb6bf.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0008_inlines collection renders" +input: "{source: \"<%= render partial: \\\"posts/post\\\", collection: @posts %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; ; __herb_collection = (@posts); __herb_collection.each_with_index do |post, post_counter|; ;; _buf << '
Post
+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0009_inlines_collection_with_as_option_c959e2228d7be537f50a0415f1c9b577.txt b/test/snapshots/engine/render_inliner_test/test_0009_inlines_collection_with_as_option_c959e2228d7be537f50a0415f1c9b577.txt new file mode 100644 index 000000000..16f637679 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0009_inlines_collection_with_as_option_c959e2228d7be537f50a0415f1c9b577.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0009_inlines collection with as: option" +input: "{source: \"<%= render partial: \\\"posts/post\\\", collection: @posts, as: :item %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; ; __herb_collection = (@posts); __herb_collection.each_with_index do |item, item_counter|; ;; _buf << '
Post
+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0010_does_not_inline_collection_with_spacer_template_1a8e2afa971bed617e1ca00a7b672f7e.txt b/test/snapshots/engine/render_inliner_test/test_0010_does_not_inline_collection_with_spacer_template_1a8e2afa971bed617e1ca00a7b672f7e.txt new file mode 100644 index 000000000..446fa9d59 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0010_does_not_inline_collection_with_spacer_template_1a8e2afa971bed617e1ca00a7b672f7e.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::RenderInlinerTest#test_0010_does not inline collection with spacer_template" +input: "{source: \"<%= render partial: \\\"posts/post\\\", collection: @posts, spacer_template: \\\"posts/spacer\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; _buf << (render partial: "posts/post", collection: @posts, spacer_template: "posts/spacer").to_s; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0011_inlines_nested_partials_d1b017469e1b907e2043c278abeef1a5.txt b/test/snapshots/engine/render_inliner_test/test_0011_inlines_nested_partials_d1b017469e1b907e2043c278abeef1a5.txt new file mode 100644 index 000000000..7a223bb97 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0011_inlines_nested_partials_d1b017469e1b907e2043c278abeef1a5.txt @@ -0,0 +1,8 @@ +--- +source: "Engine::RenderInlinerTest#test_0011_inlines nested partials" +input: "{source: \"<%= render \\\"shared/outer\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ;; _buf << '

Outer'.freeze; begin; ;; _buf << 'Inner +'.freeze; ; end;; _buf << '

+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0012_detects_circular_references_and_falls_back_ffa0077aac262faa659ea5be61da7f83.txt b/test/snapshots/engine/render_inliner_test/test_0012_detects_circular_references_and_falls_back_ffa0077aac262faa659ea5be61da7f83.txt new file mode 100644 index 000000000..937351609 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0012_detects_circular_references_and_falls_back_ffa0077aac262faa659ea5be61da7f83.txt @@ -0,0 +1,8 @@ +--- +source: "Engine::RenderInlinerTest#test_0012_detects circular references and falls back" +input: "{source: \"<%= render \\\"shared/a\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ;; begin; ;; _buf << (render "shared/a").to_s; _buf << ' +'.freeze; ; end;; _buf << ' +'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0013_scopes_locals_with_begin_end_block_eed6faed6d9f587a049f5e651c305a4c.txt b/test/snapshots/engine/render_inliner_test/test_0013_scopes_locals_with_begin_end_block_eed6faed6d9f587a049f5e651c305a4c.txt new file mode 100644 index 000000000..185818a72 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0013_scopes_locals_with_begin_end_block_eed6faed6d9f587a049f5e651c305a4c.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0013_scopes locals with begin/end block" +input: "{source: \"<%= render partial: \\\"posts/card\\\", locals: { name: \\\"test\\\" } %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ; name = ("test"); ;; _buf << '
'.freeze; _buf << (title).to_s; _buf << '
+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0014_falls_back_for_unresolvable_partial_6b2741fc46cb17dbbbaab71cefbf48dd.txt b/test/snapshots/engine/render_inliner_test/test_0014_falls_back_for_unresolvable_partial_6b2741fc46cb17dbbbaab71cefbf48dd.txt new file mode 100644 index 000000000..7f0b14cb5 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0014_falls_back_for_unresolvable_partial_6b2741fc46cb17dbbbaab71cefbf48dd.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::RenderInlinerTest#test_0014_falls back for unresolvable partial" +input: "{source: \"<%= render \\\"nonexistent/missing\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; _buf << (render "nonexistent/missing").to_s; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0015_handles_Ruby_3.1_shorthand_hash_locals_37b2a50c3b7db3cfeff62565497a8174.txt b/test/snapshots/engine/render_inliner_test/test_0015_handles_Ruby_3.1_shorthand_hash_locals_37b2a50c3b7db3cfeff62565497a8174.txt new file mode 100644 index 000000000..4df5919fd --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0015_handles_Ruby_3.1_shorthand_hash_locals_37b2a50c3b7db3cfeff62565497a8174.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0015_handles Ruby 3.1 shorthand hash locals" +input: "{source: \"<%= render partial: \\\"posts/card\\\", locals: { title: } %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ; title = (title); ;; _buf << '
'.freeze; _buf << (title).to_s; _buf << '
+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0016_inlines_shorthand_render_with_inline_locals_b86766e862c57e3e004ab4073580e1ce.txt b/test/snapshots/engine/render_inliner_test/test_0016_inlines_shorthand_render_with_inline_locals_b86766e862c57e3e004ab4073580e1ce.txt new file mode 100644 index 000000000..ecb7df640 --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0016_inlines_shorthand_render_with_inline_locals_b86766e862c57e3e004ab4073580e1ce.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0016_inlines shorthand render with inline locals" +input: "{source: \"<%= render \\\"posts/card\\\", title: \\\"Title\\\" %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ; title = ("Title"); ;; _buf << '
'.freeze; _buf << (title).to_s; _buf << '
+'.freeze; ; end;; +_buf.to_s diff --git a/test/snapshots/engine/render_inliner_test/test_0017_inlines_shorthand_render_with_multiple_inline_locals_ff0e362cc9121c9f7c204ba9fcad8dbe.txt b/test/snapshots/engine/render_inliner_test/test_0017_inlines_shorthand_render_with_multiple_inline_locals_ff0e362cc9121c9f7c204ba9fcad8dbe.txt new file mode 100644 index 000000000..34aa3b2ca --- /dev/null +++ b/test/snapshots/engine/render_inliner_test/test_0017_inlines_shorthand_render_with_multiple_inline_locals_ff0e362cc9121c9f7c204ba9fcad8dbe.txt @@ -0,0 +1,7 @@ +--- +source: "Engine::RenderInlinerTest#test_0017_inlines shorthand render with multiple inline locals" +input: "{source: \"<%= render \\\"posts/card\\\", title: \\\"Title\\\", name: @user.name %>\", options: {filename: \"app/views/posts/index.html.erb\", project_path: \"test/fixtures/render_inliner\", optimize: true, inline_render: true, escape: false}}" +--- +_buf = ::String.new; begin; ; title = ("Title"); ; name = (@user.name); ;; _buf << '
'.freeze; _buf << (title).to_s; _buf << '
+'.freeze; ; end;; +_buf.to_s