From a48ce6a9535b4a5c25ce2ec2d2b6abf35e25f465 Mon Sep 17 00:00:00 2001 From: Malian De Ron Date: Fri, 7 May 2021 13:17:56 +0200 Subject: [PATCH 1/4] Introduce <#raw> and deprecate <#Raw> --- lib/surface/compiler.ex | 7 ++++ lib/surface/compiler/tokenizer.ex | 14 ++++++-- lib/surface/components/form.ex | 3 +- lib/surface/components/raw.ex | 13 ++++++- test/compiler/tokenizer_test.exs | 54 ++++++++++++++++++++++++++++ test/compiler_test.exs | 21 +++++++++++ test/components/raw_test.exs | 60 +++++++++++++++++++++++++++++++ 7 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 test/components/raw_test.exs diff --git a/lib/surface/compiler.ex b/lib/surface/compiler.ex index a462533f..1c809c70 100644 --- a/lib/surface/compiler.ex +++ b/lib/surface/compiler.ex @@ -231,6 +231,9 @@ defmodule Surface.Compiler do defp node_type({"#elseif", _, _, _}), do: :if_elseif_else defp node_type({"#else", _, _, _}), do: :if_elseif_else + # Raw + defp node_type({"#raw", _, _, _}), do: :raw + defp node_type({"#" <> _, _, _, _}), do: :macro_component defp node_type({<>, _, _, _}) when first in ?A..?Z, do: :component defp node_type({name, _, _, _}) when name in @void_elements, do: :void_tag @@ -254,6 +257,10 @@ defmodule Surface.Compiler do defp convert_node_to_ast(:text, text, _), do: {:ok, %AST.Literal{value: text}} + defp convert_node_to_ast(:raw, {_, _, children, _}, _) do + {:ok, %AST.Literal{value: List.to_string(children)}} + end + defp convert_node_to_ast(:interpolation, {_, text, node_meta}, compile_meta) do meta = Helpers.to_meta(node_meta, compile_meta) diff --git a/lib/surface/compiler/tokenizer.ex b/lib/surface/compiler/tokenizer.ex index 4016af16..00029fc2 100644 --- a/lib/surface/compiler/tokenizer.ex +++ b/lib/surface/compiler/tokenizer.ex @@ -111,6 +111,16 @@ defmodule Surface.Compiler.Tokenizer do handle_macro_body(rest, line + 1, state.column_offset, ["\n" | buffer], acc, state) end + defp handle_macro_body(" rest, line, column, buffer, acc, state) do + handle_tag_close( + "#raw" <> rest, + line, + column + 2, + text_to_acc(buffer, acc), + state + ) + end + defp handle_macro_body(" <>, line, column, buffer, acc, state) when first in ?A..?Z do handle_tag_close( @@ -220,10 +230,10 @@ defmodule Surface.Compiler.Tokenizer do ">" <> rest, line, column, - [{:tag_open, "#" <> <>, _, _} | _] = acc, + [{:tag_open, "#" <> <> = name, _, _} | _] = acc, state ) - when first in ?A..?Z do + when first in ?A..?Z or name == "#raw" do acc = reverse_attrs(acc) handle_macro_body(rest, line, column + 1, [], acc, state) end diff --git a/lib/surface/components/form.ex b/lib/surface/components/form.ex index a1edfafe..7500e1ff 100644 --- a/lib/surface/components/form.ex +++ b/lib/surface/components/form.ex @@ -15,7 +15,6 @@ defmodule Surface.Components.Form do import Phoenix.HTML.Form import Surface.Components.Form.Utils, only: [props_to_opts: 2, props_to_attr_opts: 2] - alias Surface.Components.Raw @doc "Atom or changeset to inform the form data" prop for, :any, required: true @@ -69,7 +68,7 @@ defmodule Surface.Components.Form do <#slot :props={form: form} /> - <#Raw> + <#raw> """ end diff --git a/lib/surface/components/raw.ex b/lib/surface/components/raw.ex index 02283e54..99240d8f 100644 --- a/lib/surface/components/raw.ex +++ b/lib/surface/components/raw.ex @@ -12,10 +12,21 @@ defmodule Surface.Components.Raw do use Surface.MacroComponent + alias Surface.IOHelper + @doc "The content that will not be translated by Surface" slot default - def expand(_attributes, children, _meta) do + def expand(_attributes, children, meta) do + message = """ + using <#Raw> to not translate any of its contents has been deprecated and will be \ + removed in future versions. + + Hint: replace `<#Raw>` with `<#raw>` + """ + + IOHelper.warn(message, meta.caller, fn _ -> meta.line end) + %Surface.AST.Literal{ value: List.to_string(children) } diff --git a/test/compiler/tokenizer_test.exs b/test/compiler/tokenizer_test.exs index 096b7ebe..9f40b7cb 100644 --- a/test/compiler/tokenizer_test.exs +++ b/test/compiler/tokenizer_test.exs @@ -602,6 +602,60 @@ defmodule Surface.Compiler.TokenizerTest do ] = tokens end + describe "macro components" do + test "do not tokenize its contents" do + tokens = + tokenize!(""" + <#Macro> + text before +
+ text +
+ text after + + """) + + assert [ + {:tag_open, "#Macro", [], %{line: 1, column: 2}}, + {:text, "\ntext before\n
\n text\n
\ntext after\n"}, + {:tag_close, "#Macro", %{line: 7, column: 3}}, + {:text, "\n"} + ] = tokens + end + + test "<#raw> multi lines is treated like a macro" do + tokens = + tokenize!(""" + <#raw> +
+ { @id } +
+ + """) + + assert [ + {:tag_open, "#raw", [], %{line: 1, column: 2}}, + {:text, "\n
\n { @id }\n
\n"}, + {:tag_close, "#raw", %{line: 5, column: 3}}, + {:text, "\n"} + ] = tokens + end + + test "<#raw> single line is treated like a macro" do + tokens = + tokenize!(""" + <#raw>
{ @id }
+ """) + + assert [ + {:tag_open, "#raw", [], %{line: 1, column: 2}}, + {:text, "
{ @id }
"}, + {:tag_close, "#raw", %{line: 1, column: 27}}, + {:text, "\n"} + ] = tokens + end + end + defp tokenize_attrs(code) do [{:tag_open, "div", attrs, %{}}] = tokenize!(code) attrs diff --git a/test/compiler_test.exs b/test/compiler_test.exs index bd7c9680..7463e9ec 100644 --- a/test/compiler_test.exs +++ b/test/compiler_test.exs @@ -476,6 +476,27 @@ defmodule Surface.CompilerTest do ] } = node end + + test "#raw is treated as a LiteralValue" do + code = """ +
+ <#raw> + I am a macro + +
+ """ + + [node | _] = Surface.Compiler.compile(code, 1, __ENV__) + + assert %Surface.AST.Tag{ + element: "div", + children: [ + %Surface.AST.Literal{value: "\n "}, + %Surface.AST.Literal{value: "\n I am a macro\n "}, + %Surface.AST.Literal{value: "\n"} + ] + } = node + end end describe "constructs" do diff --git a/test/components/raw_test.exs b/test/components/raw_test.exs new file mode 100644 index 00000000..c9cfee42 --- /dev/null +++ b/test/components/raw_test.exs @@ -0,0 +1,60 @@ +defmodule Surface.Components.RawTest do + use Surface.ConnCase + + import ExUnit.CaptureIO + alias Surface.PropertiesTest.StringProp, warn: false + + test "warn if deprecated <#Raw> is used" do + id = :erlang.unique_integer([:positive]) |> to_string() + module = "TestComponentThatUsesDeprecatedRawSyntax_#{id}" + + code = """ + defmodule #{module} do + use Surface.Component + alias Surface.Components.Raw + + prop label, :string, default: "My Label", required: true + + def render(assigns) do + ~H\""" + <#Raw>{ @label } + \""" + end + end + """ + + output = + capture_io(:standard_error, fn -> + {{:module, _, _, _}, _} = + Code.eval_string(code, [], %{__ENV__ | file: "code.exs", line: 1}) + end) + + assert output =~ ~r""" + using <#Raw> to not translate any of its contents has been deprecated and will be \ + removed in future versions. + + Hint: replace `<#Raw>` with `<#raw>` + """ + end + + test "<#raw> does not translate any of its contents" do + assigns = %{id: "1234"} + + html = + render_surface do + ~H""" + <#raw> +
+ { @id } +
+ + """ + end + + assert html =~ """ +
+ { @id } +
+ """ + end +end From d6529e454a8a4d8021e40caaed5747f0449e1fd2 Mon Sep 17 00:00:00 2001 From: Malian De Ron Date: Fri, 7 May 2021 13:19:35 +0200 Subject: [PATCH 2/4] Clarify test name --- test/compiler_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compiler_test.exs b/test/compiler_test.exs index 7463e9ec..70e703b2 100644 --- a/test/compiler_test.exs +++ b/test/compiler_test.exs @@ -477,7 +477,7 @@ defmodule Surface.CompilerTest do } = node end - test "#raw is treated as a LiteralValue" do + test "#raw is treated as a Literal" do code = """
<#raw> From 7a31ad2c9eba4abbefd3243724d0d5386ba26b5b Mon Sep 17 00:00:00 2001 From: Malian De Ron Date: Fri, 7 May 2021 13:20:14 +0200 Subject: [PATCH 3/4] Remove unused alias --- test/components/raw_test.exs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/components/raw_test.exs b/test/components/raw_test.exs index c9cfee42..17ac82ff 100644 --- a/test/components/raw_test.exs +++ b/test/components/raw_test.exs @@ -2,7 +2,6 @@ defmodule Surface.Components.RawTest do use Surface.ConnCase import ExUnit.CaptureIO - alias Surface.PropertiesTest.StringProp, warn: false test "warn if deprecated <#Raw> is used" do id = :erlang.unique_integer([:positive]) |> to_string() From 8df707ab714a8d0b795e0f1e7d6083cd9b936605 Mon Sep 17 00:00:00 2001 From: Malian De Ron Date: Sat, 8 May 2021 06:40:21 +0200 Subject: [PATCH 4/4] Rephrase warning message --- lib/surface/components/raw.ex | 3 +-- test/components/raw_test.exs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/surface/components/raw.ex b/lib/surface/components/raw.ex index 99240d8f..ff2fe90b 100644 --- a/lib/surface/components/raw.ex +++ b/lib/surface/components/raw.ex @@ -19,8 +19,7 @@ defmodule Surface.Components.Raw do def expand(_attributes, children, meta) do message = """ - using <#Raw> to not translate any of its contents has been deprecated and will be \ - removed in future versions. + using <#Raw> has been deprecated and will be removed in future versions. Hint: replace `<#Raw>` with `<#raw>` """ diff --git a/test/components/raw_test.exs b/test/components/raw_test.exs index 17ac82ff..bec9bf9b 100644 --- a/test/components/raw_test.exs +++ b/test/components/raw_test.exs @@ -29,8 +29,7 @@ defmodule Surface.Components.RawTest do end) assert output =~ ~r""" - using <#Raw> to not translate any of its contents has been deprecated and will be \ - removed in future versions. + using <#Raw> has been deprecated and will be removed in future versions. Hint: replace `<#Raw>` with `<#raw>` """