Packages
surface
0.12.2
0.12.3
0.12.2
0.12.1
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha.0
A component based library for Phoenix LiveView
Current section
46 Versions
Jump to
Current section
46 Versions
Compare versions
11
files changed
+94
additions
-75
deletions
| @@ -1,5 +1,13 @@ | |
| 1 1 | # Changelog |
| 2 2 | |
| 3 | + ## v0.12.2 (2026-01-29) |
| 4 | + |
| 5 | + * Fix compile errors/warnings with Elixir `v1.19` |
| 6 | + * Fix compitibility issues with Liveview >= `v1.1` |
| 7 | + * Remove support for Elixir < `v1.14` |
| 8 | + * Remove support of experimental `:for.index` without generator |
| 9 | + * Optimize compiler using manifest to void unnecessary compilation when there's no change |
| 10 | + |
| 3 11 | ## v0.12.1 (2025-02-14) |
| 4 12 | |
| 5 13 | * Support Liveview `1.0` (#760) |
| @@ -3,9 +3,9 @@ | |
| 3 3 | {<<"Changelog">>,<<"https://hexdocs.pm/surface/changelog.html">>}, |
| 4 4 | {<<"GitHub">>,<<"https://github.com/surface-ui/surface">>}]}. |
| 5 5 | {<<"name">>,<<"surface">>}. |
| 6 | - {<<"version">>,<<"0.12.1">>}. |
| 6 | + {<<"version">>,<<"0.12.2">>}. |
| 7 7 | {<<"description">>,<<"A component based library for Phoenix LiveView">>}. |
| 8 | - {<<"elixir">>,<<"~> 1.13">>}. |
| 8 | + {<<"elixir">>,<<"~> 1.14">>}. |
| 9 9 | {<<"app">>,<<"surface">>}. |
| 10 10 | {<<"licenses">>,[<<"MIT">>]}. |
| 11 11 | {<<"files">>, |
| @@ -143,6 +143,8 @@ defmodule Mix.Tasks.Compile.Surface do | |
| 143 143 | |
| 144 144 | use Mix.Task |
| 145 145 | @recursive true |
| 146 | + @manifest ".compile_surface" |
| 147 | + @manifest_version 1 |
| 146 148 | |
| 147 149 | alias Mix.Task.Compiler.Diagnostic |
| 148 150 | |
| @@ -167,51 +169,62 @@ defmodule Mix.Tasks.Compile.Surface do | |
| 167 169 | {:noop, []} |
| 168 170 | else |
| 169 171 | {compile_opts, _argv, _err} = OptionParser.parse(args, switches: @switches) |
| 172 | + |
| 173 | + {version, diagnostics} = read_manifest() |
| 174 | + manifest_outdated? = version != @manifest_version or manifest_older?() |
| 175 | + |
| 176 | + cond do |
| 177 | + manifest_outdated? || compile_opts[:force] -> |
| 178 | + do_run(compile_opts) |
| 179 | + |
| 180 | + compile_opts[:all_warnings] -> |
| 181 | + handle_diagnostics(diagnostics, compile_opts, :noop) |
| 182 | + |
| 183 | + true -> |
| 184 | + {:noop, []} |
| 185 | + end |
| 186 | + end |
| 187 | + end |
| 188 | + |
| 189 | + def do_run(compile_opts) do |
| 170 190 | opts = Application.get_env(:surface, :compiler, []) |
| 171 191 | asset_opts = Keyword.take(opts, @assets_opts) |
| 172 192 | asset_components = Surface.components() |
| 173 193 | project_components = Surface.components(only_current_project: true) |
| 174 194 | |
| 175 | - [ |
| 195 | + diagnostics = |
| 196 | + List.flatten([ |
| 176 197 | Mix.Tasks.Compile.Surface.ValidateComponents.validate(project_components), |
| 177 198 | Mix.Tasks.Compile.Surface.AssetGenerator.run(asset_components, asset_opts) |
| 178 | - ] |
| 179 | - |> List.flatten() |
| 180 | - |> handle_diagnostics(compile_opts) |
| 181 | - end |
| 182 | - end |
| 199 | + ]) |
| 200 | + |
| 201 | + write_manifest!(@manifest_version, diagnostics) |
| 183 202 | |
| 184 | - @doc false |
| 185 | - def handle_diagnostics(diagnostics, compile_opts) do |
| 186 203 | case diagnostics do |
| 187 204 | [] -> |
| 188 205 | {:noop, []} |
| 189 206 | |
| 190 207 | diagnostics -> |
| 191 | - if !compile_opts[:return_errors], do: print_diagnostics(diagnostics) |
| 192 | - status = status(compile_opts[:warnings_as_errors], diagnostics) |
| 193 | - |
| 194 | - {status, diagnostics} |
| 208 | + handle_diagnostics(diagnostics, compile_opts, :ok) |
| 195 209 | end |
| 196 210 | end |
| 197 211 | |
| 212 | + @doc false |
| 213 | + def handle_diagnostics(diagnostics, compile_opts, status) do |
| 214 | + if !compile_opts[:return_errors], do: print_diagnostics(diagnostics) |
| 215 | + status = status(compile_opts[:warnings_as_errors], diagnostics, status) |
| 216 | + {status, diagnostics} |
| 217 | + end |
| 218 | + |
| 198 219 | defp print_diagnostics(diagnostics) do |
| 199 220 | for %Diagnostic{message: message, severity: severity, file: file, position: position} <- diagnostics do |
| 200 221 | print_diagnostic(message, severity, file, position) |
| 201 222 | end |
| 202 223 | end |
| 203 224 | |
| 204 | - if Version.match?(System.version(), ">= 1.14.0") do |
| 205 225 | defp print_diagnostic(message, :warning, file, {line, col}) do |
| 206 226 | IO.warn(message, file: file, line: line, column: col) |
| 207 227 | end |
| 208 | - end |
| 209 | - |
| 210 | - # TODO: Remove this clause in Surface v0.13 and set required elixir to >= v1.14 |
| 211 | - defp print_diagnostic(message, :warning, file, line) do |
| 212 | - rel_file = file |> Path.relative_to_cwd() |> to_charlist() |
| 213 | - IO.warn(message, [{nil, :__FILE__, 1, [file: rel_file, line: line]}]) |
| 214 | - end |
| 215 228 | |
| 216 229 | defp print_diagnostic(message, :error, file, line) do |
| 217 230 | error = IO.ANSI.format([:red, "error: "]) |
| @@ -223,11 +236,40 @@ defmodule Mix.Tasks.Compile.Surface do | |
| 223 236 | IO.puts(:stderr, [error, message, ?\n, stacktrace]) |
| 224 237 | end |
| 225 238 | |
| 226 | - defp status(warnings_as_errors, diagnostics) do |
| 239 | + defp status(warnings_as_errors, diagnostics, default) do |
| 227 240 | cond do |
| 228 241 | Enum.any?(diagnostics, &(&1.severity == :error)) -> :error |
| 229 242 | warnings_as_errors && Enum.any?(diagnostics, &(&1.severity == :warning)) -> :error |
| 230 | - true -> :ok |
| 243 | + true -> default |
| 231 244 | end |
| 232 245 | end |
| 246 | + |
| 247 | + @doc false |
| 248 | + def manifests, do: [manifest()] |
| 249 | + |
| 250 | + defp manifest, do: Path.join(Mix.Project.manifest_path(), @manifest) |
| 251 | + |
| 252 | + @doc false |
| 253 | + def read_manifest do |
| 254 | + case File.read(manifest()) do |
| 255 | + {:ok, contents} -> :erlang.binary_to_term(contents) |
| 256 | + _ -> {:unknown, nil} |
| 257 | + end |
| 258 | + end |
| 259 | + |
| 260 | + @doc false |
| 261 | + def write_manifest!(version, diagnostics) do |
| 262 | + File.write!(manifest(), :erlang.term_to_binary({version, diagnostics})) |
| 263 | + end |
| 264 | + |
| 265 | + defp manifest_older? do |
| 266 | + other_manifests = Mix.Tasks.Compile.Elixir.manifests() |
| 267 | + manifest_mtime = mtime(manifest()) |
| 268 | + Enum.any?(other_manifests, fn m -> mtime(m) > manifest_mtime end) |
| 269 | + end |
| 270 | + |
| 271 | + defp mtime(file) do |
| 272 | + %File.Stat{mtime: mtime} = File.stat!(file) |
| 273 | + mtime |
| 274 | + end |
| 233 275 | end |
| @@ -292,8 +292,8 @@ defmodule Mix.Tasks.Surface.Init.ExPatcher do | |
| 292 292 | end |
| 293 293 | end |
| 294 294 | |
| 295 | - def set_result(patcher, status) do |
| 296 | - %__MODULE__{patcher | result: status} |
| 295 | + def set_result(%__MODULE__{} = patcher, status) do |
| 296 | + %{patcher | result: status} |
| 297 297 | end |
| 298 298 | |
| 299 299 | def append_code(patcher, text_to_append) do |
| @@ -391,7 +391,7 @@ defmodule Mix.Tasks.Surface.Init.ExPatcher do | |
| 391 391 | patcher |
| 392 392 | end |
| 393 393 | |
| 394 | - def patch(patcher, opts, fun) do |
| 394 | + def patch(%__MODULE__{} = patcher, opts, fun) do |
| 395 395 | zipper = zipper(patcher) |
| 396 396 | |
| 397 397 | patch = |
| @@ -406,7 +406,7 @@ defmodule Mix.Tasks.Surface.Init.ExPatcher do | |
| 406 406 | |
| 407 407 | updated_code = patcher |> code() |> Sourceror.patch_string([patch]) |
| 408 408 | |
| 409 | - %__MODULE__{patcher | code: updated_code, result: :patched} |
| 409 | + %{patcher | code: updated_code, result: :patched} |
| 410 410 | end |
| 411 411 | |
| 412 412 | def code(%__MODULE__{code: code}) do |
| @@ -172,12 +172,12 @@ defmodule Surface do | |
| 172 172 | end |
| 173 173 | |
| 174 174 | @doc false |
| 175 | - def __compile_sface__(name, file, env) do |
| 175 | + def __compile_sface__(name, file, %Macro.Env{} = env) do |
| 176 176 | file |
| 177 177 | |> File.read!() |
| 178 178 | |> Surface.Compiler.compile(1, env, file) |
| 179 179 | |> Surface.Compiler.to_live_struct( |
| 180 | - caller: %Macro.Env{env | file: file, line: 1, function: {String.to_atom(name), 1}}, |
| 180 | + caller: %{env | file: file, line: 1, function: {String.to_atom(name), 1}}, |
| 181 181 | annotate_content: annotate_content() |
| 182 182 | ) |
| 183 183 | end |
Loading more files…