Skip to content
Open
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
17 changes: 7 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ jobs:
fail-fast: false
matrix:
include:
- elixir: '1.13.4'
otp: '25.0'
blend: phoenix_live_view_0_19
- elixir: '1.13.4'
otp: '25.0'
blend: phoenix_live_view_0_20
- elixir: '1.14.1'
otp: '25.0'
run_plugin_tests: true
Expand All @@ -34,24 +28,27 @@ jobs:
warnings_as_errors: true
- elixir: '1.16.0'
otp: '26.2.1'
check_formatted: true
run_plugin_tests: true
run_integration_tests: true
warnings_as_errors: true
- elixir: '1.17.0'
otp: '27.0.1'
check_formatted: true
run_plugin_tests: true
run_integration_tests: true
warnings_as_errors: true
- elixir: '1.18.0'
otp: '27.0.1'
run_plugin_tests: true
run_integration_tests: true
warnings_as_errors: true
- elixir: '1.19.5'
otp: '28.3.1'
check_formatted: true
run_plugin_tests: true
run_integration_tests: true
warnings_as_errors: true
- elixir: '1.18.0'
otp: '27.0.1'
- elixir: '1.19.5'
otp: '28.3.1'
update_deps: true
run_plugin_tests: true
env:
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
erlang 27.2.2
elixir 1.18.2-otp-27
erlang 28.3.1
elixir 1.19.5-otp-28
96 changes: 68 additions & 28 deletions lib/mix/tasks/compile/surface.ex
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ defmodule Mix.Tasks.Compile.Surface do

use Mix.Task
@recursive true
@manifest ".compile_surface"
@manifest_version 1

alias Mix.Task.Compiler.Diagnostic

Expand All @@ -167,50 +169,61 @@ defmodule Mix.Tasks.Compile.Surface do
{:noop, []}
else
{compile_opts, _argv, _err} = OptionParser.parse(args, switches: @switches)
opts = Application.get_env(:surface, :compiler, [])
asset_opts = Keyword.take(opts, @assets_opts)
asset_components = Surface.components()
project_components = Surface.components(only_current_project: true)

[
Mix.Tasks.Compile.Surface.ValidateComponents.validate(project_components),
Mix.Tasks.Compile.Surface.AssetGenerator.run(asset_components, asset_opts)
]
|> List.flatten()
|> handle_diagnostics(compile_opts)
{version, diagnostics} = read_manifest()
manifest_outdated? = version != @manifest_version or manifest_older?()

cond do
manifest_outdated? || compile_opts[:force] ->
do_run(compile_opts)

compile_opts[:all_warnings] ->
handle_diagnostics(diagnostics, compile_opts, :noop)

true ->
{:noop, []}
end
end
end

@doc false
def handle_diagnostics(diagnostics, compile_opts) do
def do_run(compile_opts) do
opts = Application.get_env(:surface, :compiler, [])
asset_opts = Keyword.take(opts, @assets_opts)
asset_components = Surface.components()
project_components = Surface.components(only_current_project: true)

diagnostics =
List.flatten([
Mix.Tasks.Compile.Surface.ValidateComponents.validate(project_components),
Mix.Tasks.Compile.Surface.AssetGenerator.run(asset_components, asset_opts)
])

write_manifest!(diagnostics)

case diagnostics do
[] ->
{:noop, []}

diagnostics ->
if !compile_opts[:return_errors], do: print_diagnostics(diagnostics)
status = status(compile_opts[:warnings_as_errors], diagnostics)

{status, diagnostics}
handle_diagnostics(diagnostics, compile_opts, :ok)
end
end

@doc false
def handle_diagnostics(diagnostics, compile_opts, status) do
if !compile_opts[:return_errors], do: print_diagnostics(diagnostics)
status = status(compile_opts[:warnings_as_errors], diagnostics, status)
{status, diagnostics}
end

defp print_diagnostics(diagnostics) do
for %Diagnostic{message: message, severity: severity, file: file, position: position} <- diagnostics do
print_diagnostic(message, severity, file, position)
end
end

if Version.match?(System.version(), ">= 1.14.0") do
defp print_diagnostic(message, :warning, file, {line, col}) do
IO.warn(message, file: file, line: line, column: col)
end
end

# TODO: Remove this clause in Surface v0.13 and set required elixir to >= v1.14
defp print_diagnostic(message, :warning, file, line) do
rel_file = file |> Path.relative_to_cwd() |> to_charlist()
IO.warn(message, [{nil, :__FILE__, 1, [file: rel_file, line: line]}])
defp print_diagnostic(message, :warning, file, {line, col}) do
IO.warn(message, file: file, line: line, column: col)
end

defp print_diagnostic(message, :error, file, line) do
Expand All @@ -223,11 +236,38 @@ defmodule Mix.Tasks.Compile.Surface do
IO.puts(:stderr, [error, message, ?\n, stacktrace])
end

defp status(warnings_as_errors, diagnostics) do
defp status(warnings_as_errors, diagnostics, default) do
cond do
Enum.any?(diagnostics, &(&1.severity == :error)) -> :error
warnings_as_errors && Enum.any?(diagnostics, &(&1.severity == :warning)) -> :error
true -> :ok
true -> default
end
end

@doc false
def manifests, do: [manifest()]

defp manifest, do: Path.join(Mix.Project.manifest_path(), @manifest)

defp read_manifest do
case File.read(manifest()) do
{:ok, contents} -> :erlang.binary_to_term(contents)
_ -> {:unknown, nil}
end
end

defp write_manifest!(diagnostics) do
File.write!(manifest(), :erlang.term_to_binary({@manifest_version, diagnostics}))
end

defp manifest_older? do
other_manifests = Mix.Tasks.Compile.Elixir.manifests()
manifest_mtime = mtime(manifest())
Enum.any?(other_manifests, fn m -> mtime(m) > manifest_mtime end)
end

defp mtime(file) do
%File.Stat{mtime: mtime} = File.stat!(file)
mtime
end
end
8 changes: 4 additions & 4 deletions lib/mix/tasks/surface/surface.init/ex_patcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,8 @@ defmodule Mix.Tasks.Surface.Init.ExPatcher do
end
end

def set_result(patcher, status) do
%__MODULE__{patcher | result: status}
def set_result(%__MODULE__{} = patcher, status) do
%{patcher | result: status}
end

def append_code(patcher, text_to_append) do
Expand Down Expand Up @@ -391,7 +391,7 @@ defmodule Mix.Tasks.Surface.Init.ExPatcher do
patcher
end

def patch(patcher, opts, fun) do
def patch(%__MODULE__{} = patcher, opts, fun) do
zipper = zipper(patcher)

patch =
Expand All @@ -406,7 +406,7 @@ defmodule Mix.Tasks.Surface.Init.ExPatcher do

updated_code = patcher |> code() |> Sourceror.patch_string([patch])

%__MODULE__{patcher | code: updated_code, result: :patched}
%{patcher | code: updated_code, result: :patched}
end

def code(%__MODULE__{code: code}) do
Expand Down
4 changes: 2 additions & 2 deletions lib/surface.ex
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,12 @@ defmodule Surface do
end

@doc false
def __compile_sface__(name, file, env) do
def __compile_sface__(name, file, %Macro.Env{} = env) do
file
|> File.read!()
|> Surface.Compiler.compile(1, env, file)
|> Surface.Compiler.to_live_struct(
caller: %Macro.Env{env | file: file, line: 1, function: {String.to_atom(name), 1}},
caller: %{env | file: file, line: 1, function: {String.to_atom(name), 1}},
annotate_content: annotate_content()
)
end
Expand Down
14 changes: 0 additions & 14 deletions lib/surface/directive/for.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,6 @@ defmodule Surface.Directive.For do
handle_modifiers([{:<-, clause_meta, [var, udpated_list]}], modifiers, meta)
end

defp handle_modifiers([{_, clause_meta, _} = list], ["index" | modifiers], meta) do
var =
quote do
var!(index)
end

udpated_list =
quote do
Enum.scan(unquote(list), -1, fn _, a -> a + 1 end)
end

handle_modifiers([{:<-, clause_meta, [var, udpated_list]}], modifiers, meta)
end

defp handle_modifiers(clauses, [modifier | _modifiers], meta) when length(clauses) > 1 do
message = "cannot apply modifier \"#{modifier}\" on generators with multiple clauses"
IOHelper.compile_error(message, meta.file, meta.line)
Expand Down
33 changes: 6 additions & 27 deletions lib/surface/io_helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,8 @@ defmodule Surface.IOHelper do
IO.warn(message, stacktrace)
end

if Version.match?(System.version(), ">= 1.14.0") do
def warn(message, _caller, file, {line, column}) do
IO.warn(message, file: file, line: line, column: column)
end
else
# TODO: Remove this clause in Surface v0.13 and set required elixir to >= v1.14
def warn(message, caller, file, {line, _column}) do
warn(message, caller, file, line)
end
def warn(message, _caller, file, {line, column}) do
IO.warn(message, file: file, line: line, column: column)
end

def warn(message, caller, file, line) do
Expand All @@ -30,30 +23,16 @@ defmodule Surface.IOHelper do
IO.warn(message, stacktrace)
end

if Version.match?(System.version(), ">= 1.14.0") do
def compile_error(message, file, {line, column}) do
reraise(%Surface.CompileError{file: file, line: line, column: column, description: message}, [])
end
else
# TODO: Remove this clause in Surface v0.13 and set required elixir to >= v1.14
def compile_error(message, file, {line, _column}) do
reraise(%Surface.CompileError{line: line, file: file, description: message}, [])
end
def compile_error(message, file, {line, column}) do
reraise(%Surface.CompileError{file: file, line: line, column: column, description: message}, [])
end

def compile_error(message, file, line) do
reraise(%Surface.CompileError{line: line, file: file, description: message}, [])
end

if Version.match?(System.version(), ">= 1.14.0") do
def compile_error(message, hint, file, {line, column}) do
reraise(%Surface.CompileError{file: file, line: line, column: column, description: message, hint: hint}, [])
end
else
# TODO: Remove this clause in Surface v0.13 and set required elixir to >= v1.14
def compile_error(message, hint, file, {line, _column}) do
reraise(%Surface.CompileError{file: file, line: line, description: message, hint: hint}, [])
end
def compile_error(message, hint, file, {line, column}) do
reraise(%Surface.CompileError{file: file, line: line, column: column, description: message, hint: hint}, [])
end

def compile_error(message, hint, file, line) do
Expand Down
4 changes: 2 additions & 2 deletions lib/surface/macro_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ defmodule Surface.MacroComponent do
{name, value}
end

defp eval_value(%AST.Attribute{value: value_ast}, prop, caller) do
defp eval_value(%AST.Attribute{value: value_ast}, prop, %Macro.Env{} = caller) do
%AST.AttributeExpr{original: value, value: expr, meta: %{line: line, file: file}} = value_ast

env = %Macro.Env{caller | line: line}
env = %{caller | line: line}

{evaluated_value, _} =
try do
Expand Down
8 changes: 6 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ defmodule Surface.MixProject do
[
app: :surface,
version: @version,
elixir: "~> 1.13",
elixir: "~> 1.14",
description: "A component based library for Phoenix LiveView",
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
deps: deps(),
preferred_cli_env: [docs: :docs],
# Docs
name: "Surface",
source_url: @source_url,
Expand All @@ -32,6 +31,10 @@ defmodule Surface.MixProject do
]
end

def cli do
[preferred_envs: [docs: :docs]]
end

defp elixirc_paths(:dev), do: ["lib"] ++ catalogues()
defp elixirc_paths(:test), do: ["lib", "test/support"] ++ catalogues()
defp elixirc_paths(_), do: ["lib"]
Expand All @@ -48,6 +51,7 @@ defmodule Surface.MixProject do
{:blend, "~> 0.3.0", only: :dev},
{:jason, "~> 1.0", only: :test},
{:floki, "~> 0.35", only: :test},
{:lazy_html, ">= 0.1.0", only: :test},
{:ex_doc, ">= 0.31.0", only: :docs}
]
end
Expand Down
Loading
Loading