-
-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR adopts an approach that divides the compilation of HEEx into three steps. First, it focuses on the HTML portion, then the EEx part, where certain errors in EEx require evaluation to obtain, and finally, the compile_quoted step. I think this layered approach is quite good, like peeling an onion. Though there are some problems that I haven't thought of a good solution for now, such as 1. In a HEEx file, when a function component does not exist, `mix compile` will emit an error, but how can this be achieved for single file compilation? Jose suggested that we compile the relevant `ex` files directly, but it seems difficult because the latest content of those files may only exist in the `Document.Store` of the server node. 2. In a `.ex` file, like `core_components.ex`, how can the `~H` block be compiled separately? 3. The `~p` block in a `~H` block or `HEEx` file.
- Loading branch information
Showing
7 changed files
with
290 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
apps/remote_control/lib/lexical/remote_control/build/document/compilers/heex.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
defmodule Lexical.RemoteControl.Build.Document.Compilers.HEEx do | ||
@moduledoc """ | ||
A compiler for .heex files | ||
""" | ||
alias Lexical.Document | ||
alias Lexical.Plugin.V1.Diagnostic.Result | ||
alias Lexical.RemoteControl.Build.Document.Compiler | ||
alias Lexical.RemoteControl.Build.Document.Compilers | ||
require Logger | ||
|
||
@behaviour Compiler | ||
|
||
def recognizes?(%Document{} = document) do | ||
Path.extname(document.path) == ".heex" | ||
end | ||
|
||
def enabled? do | ||
true | ||
end | ||
|
||
def compile(%Document{} = document) do | ||
case heex_to_quoted(document) do | ||
{:ok, _} -> | ||
Compilers.EEx.compile(document) | ||
|
||
other -> | ||
other | ||
end | ||
end | ||
|
||
defp heex_to_quoted(%Document{} = document) do | ||
try do | ||
source = Document.to_string(document) | ||
|
||
opts = | ||
[ | ||
source: source, | ||
file: document.path, | ||
caller: __ENV__, | ||
engine: Phoenix.LiveView.TagEngine, | ||
subengine: Phoenix.LiveView.Engine, | ||
tag_handler: Phoenix.LiveView.HTMLEngine | ||
] | ||
|
||
quoted = EEx.compile_string(source, opts) | ||
|
||
{:ok, quoted} | ||
rescue | ||
error -> | ||
{:error, [error_to_result(document, error)]} | ||
end | ||
end | ||
|
||
defp error_to_result(%Document{} = document, %EEx.SyntaxError{} = error) do | ||
position = {error.line, error.column} | ||
|
||
Result.new(document.uri, position, error.message, :error, "EEx") | ||
end | ||
|
||
defp error_to_result(document, %error_struct{} = error) | ||
when error_struct in [ | ||
TokenMissingError, | ||
Phoenix.LiveView.Tokenizer.ParseError | ||
] do | ||
position = {error.line, error.column} | ||
Result.new(document.uri, position, error.description, :error, "HEEx") | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
apps/remote_control/test/lexical/remote_control/build/document/compilers/heex_test.exs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
defmodule Lexical.RemoteControl.Build.Document.Compilers.HeexTest do | ||
alias Lexical.Document | ||
alias Lexical.Plugin.V1.Diagnostic.Result | ||
alias Lexical.RemoteControl.Build.CaptureServer | ||
alias Lexical.RemoteControl.Build.Document.Compilers | ||
alias Lexical.RemoteControl.Dispatch | ||
alias Lexical.RemoteControl.ModuleMappings | ||
|
||
import Lexical.Test.CodeSigil | ||
import Compilers.HEEx, only: [compile: 1] | ||
|
||
use ExUnit.Case | ||
|
||
def with_capture_server(_) do | ||
start_supervised!(CaptureServer) | ||
start_supervised!(Dispatch) | ||
start_supervised!(ModuleMappings) | ||
:ok | ||
end | ||
|
||
def document_with_content(content) do | ||
Document.new("file:///file.heex", content, 0) | ||
end | ||
|
||
setup do | ||
Code.compiler_options(parser_options: [columns: true, token_metadata: true]) | ||
end | ||
|
||
describe "compile/1" do | ||
setup [:with_capture_server] | ||
|
||
test "handles valid HEEx content" do | ||
document = document_with_content(~q[ | ||
<div>thing</div> | ||
]) | ||
assert {:ok, []} = compile(document) | ||
end | ||
|
||
test "ignore undefinied assigns" do | ||
document = document_with_content(~q[ | ||
<div><%= @thing %></div> | ||
]) | ||
|
||
assert {:error, []} = compile(document) | ||
end | ||
|
||
test "returns error when there are unclosed tags" do | ||
document = document_with_content(~q[ | ||
<div>thing | ||
]) | ||
assert {:error, [%Result{} = result]} = compile(document) | ||
|
||
assert result.message =~ | ||
"end of template reached without closing tag for <div>\n |\n1 | <div>thing\n | ^" | ||
|
||
assert result.position == {1, 1} | ||
assert result.severity == :error | ||
assert result.source == "HEEx" | ||
assert result.uri =~ "file:///file.heex" | ||
end | ||
|
||
test "returns error when HEEx syntax is invalid" do | ||
document = document_with_content(~q[ | ||
<span id=@id}></span> | ||
]) | ||
|
||
assert {:error, [%Result{} = result]} = compile(document) | ||
|
||
assert result.message =~ "invalid attribute value after `=`. " | ||
assert result.position == {1, 10} | ||
assert result.severity == :error | ||
assert result.source == "HEEx" | ||
assert result.uri =~ "file:///file.heex" | ||
end | ||
|
||
test "handles EEx syntax error" do | ||
document = document_with_content(~q[ | ||
<%= IO. | ||
]) | ||
assert {:error, [%Result{} = result]} = compile(document) | ||
|
||
assert result.message =~ "'%>'" | ||
assert result.source == "EEx" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters