Skip to content

Commit

Permalink
Showing 2 changed files with 56 additions and 1 deletion.
29 changes: 28 additions & 1 deletion lib/phoenix_live_view/tag_engine.ex
Original file line number Diff line number Diff line change
@@ -812,7 +812,18 @@ defmodule Phoenix.LiveView.TagEngine do
defp handle_tag_attrs(state, meta, attrs) do
Enum.reduce(attrs, state, fn
{:root, {:expr, _, _} = expr, _attr_meta}, state ->
handle_attrs_escape(state, meta, parse_expr!(expr, state.file))
ast = parse_expr!(expr, state.file)

if pairs = dynamic_attrs_literal(ast) do
# Optimization: if keys are known at compilation time, we
# inline the dynamic attributes
Enum.reduce(pairs, state, fn {key, value}, state ->
name = to_string(key)
handle_attr_escape(state, meta, name, value)
end)
else
handle_attrs_escape(state, meta, ast)
end

{name, {:expr, _, _} = expr, _attr_meta}, state ->
handle_attr_escape(state, meta, name, parse_expr!(expr, state.file))
@@ -828,6 +839,22 @@ defmodule Phoenix.LiveView.TagEngine do
end)
end

defp dynamic_attrs_literal({:%{}, _meta, pairs}) do
if literal_keys?(pairs), do: pairs
end

defp dynamic_attrs_literal(list) when is_list(list) do
if literal_keys?(list), do: list
end

defp dynamic_attrs_literal(_other), do: nil

def literal_keys?([{key, _value} | rest]) when is_atom(key) or is_binary(key),
do: literal_keys?(rest)

def literal_keys?([]), do: true
def literal_keys?(_other), do: false

defp handle_special_expr(state, tag_meta) do
ast =
case tag_meta do
28 changes: 28 additions & 0 deletions test/phoenix_live_view/html_engine_test.exs
Original file line number Diff line number Diff line change
@@ -170,6 +170,34 @@ defmodule Phoenix.LiveView.HTMLEngineTest do
eval(template, assigns)
end

test "inlines dynamic attributes when keys are known at compilation time" do
assigns = %{val: 1}

# keyword list
template = ~S(<div {[d1: @val, d2: "2", d3: @val]} />)

assert %Phoenix.LiveView.Rendered{static: ["<div", " d2=\"2\"", "></div>"]} =
eval(template, assigns)

# list with string keys
template = ~S(<div {[{"d1", @val}, {"d2", "2"}, {"d3", @val}]} />)

assert %Phoenix.LiveView.Rendered{static: ["<div", " d2=\"2\"", "></div>"]} =
eval(template, assigns)

# map with atom keys
template = ~S(<div {%{d1: @val, d2: "2", d3: @val}} />)

assert %Phoenix.LiveView.Rendered{static: ["<div", " d2=\"2\"", "></div>"]} =
eval(template, assigns)

# map with string keys
template = ~S(<div {%{"d1" => @val, "d2" => "2", "d3" => @val}} />)

assert %Phoenix.LiveView.Rendered{static: ["<div", " d2=\"2\"", "></div>"]} =
eval(template, assigns)
end

test "optimizes attributes with literal string values" do
assigns = %{unsafe: "<foo>", safe: {:safe, "<foo>"}}

0 comments on commit 0041275

Please sign in to comment.