Current section

46 Versions

Jump to

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…