Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/surface/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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({<<first, _::binary>>, _, _, _}) when first in ?A..?Z, do: :component
defp node_type({name, _, _, _}) when name in @void_elements, do: :void_tag
Expand All @@ -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)

Expand Down
14 changes: 12 additions & 2 deletions lib/surface/compiler/tokenizer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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("</#raw" <> 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("</#" <> <<first, rest::binary>>, line, column, buffer, acc, state)
when first in ?A..?Z do
handle_tag_close(
Expand Down Expand Up @@ -220,10 +230,10 @@ defmodule Surface.Compiler.Tokenizer do
">" <> rest,
line,
column,
[{:tag_open, "#" <> <<first, _::binary>>, _, _} | _] = acc,
[{:tag_open, "#" <> <<first, _::binary>> = 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
Expand Down
3 changes: 1 addition & 2 deletions lib/surface/components/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -69,7 +68,7 @@ defmodule Surface.Components.Form do
<Context put={__MODULE__, form: form}>
<#slot :props={form: form} />
</Context>
<#Raw></form></#Raw>
<#raw></form></#raw>
"""
end

Expand Down
12 changes: 11 additions & 1 deletion lib/surface/components/raw.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,20 @@ 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> 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)
}
Expand Down
54 changes: 54 additions & 0 deletions test/compiler/tokenizer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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
<div>
text
</div>
text after
</#Macro>
""")

assert [
{:tag_open, "#Macro", [], %{line: 1, column: 2}},
{:text, "\ntext before\n<div>\n text\n</div>\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>
<div>
{ @id }
</div>
</#raw>
""")

assert [
{:tag_open, "#raw", [], %{line: 1, column: 2}},
{:text, "\n <div>\n { @id }\n </div>\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><div>{ @id }</div></#raw>
""")

assert [
{:tag_open, "#raw", [], %{line: 1, column: 2}},
{:text, "<div>{ @id }</div>"},
{:tag_close, "#raw", %{line: 1, column: 27}},
{:text, "\n"}
] = tokens
end
end

defp tokenize_attrs(code) do
[{:tag_open, "div", attrs, %{}}] = tokenize!(code)
attrs
Expand Down
21 changes: 21 additions & 0 deletions test/compiler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,27 @@ defmodule Surface.CompilerTest do
]
} = node
end

test "#raw is treated as a Literal" do
code = """
<div>
<#raw>
I am a macro
</#raw>
</div>
"""

[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
Expand Down
58 changes: 58 additions & 0 deletions test/components/raw_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
defmodule Surface.Components.RawTest do
use Surface.ConnCase

import ExUnit.CaptureIO

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 }</#Raw>
\"""
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> 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>
<div>
{ @id }
</div>
</#raw>
"""
end

assert html =~ """
<div>
{ @id }
</div>
"""
end
end