From 547e1c7089a6e07d14f84e292d5205ed78cea4a7 Mon Sep 17 00:00:00 2001 From: Piotr Baj Date: Mon, 3 Mar 2025 17:08:34 +0100 Subject: [PATCH 1/5] Capture errors with Plug.Debugger --- lib/mix/tasks/tableau.build.ex | 21 ++++++++++++++++----- lib/tableau.ex | 26 ++++++++++++++++++++++++++ lib/tableau_dev_server/router.ex | 1 + 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/lib/mix/tasks/tableau.build.ex b/lib/mix/tasks/tableau.build.ex index 4d03860..3c19cae 100644 --- a/lib/mix/tasks/tableau.build.ex +++ b/lib/mix/tasks/tableau.build.ex @@ -44,12 +44,17 @@ defmodule Mix.Tasks.Tableau.Build do pages = pages |> Task.async_stream(fn {mod, page} -> - content = Tableau.Document.render(graph, mod, token, page) - permalink = Nodable.permalink(mod) - - Map.merge(page, %{body: content, permalink: permalink}) + try do + content = Tableau.Document.render(graph, mod, token, page) + permalink = Nodable.permalink(mod) + + Map.merge(page, %{body: content, permalink: permalink}) + rescue + exception -> + reraise Tableau.BuildException, [page: page, exception: exception], __STACKTRACE__ + end end) - |> Stream.map(fn {:ok, result} -> result end) + |> Stream.map(&map_pages/1) |> Enum.to_list() token = put_in(token.site[:pages], pages) @@ -74,6 +79,12 @@ defmodule Mix.Tasks.Tableau.Build do token end + defp map_pages({:ok, result}), do: result + + defp map_pages({:exit, {exception, stacktrace}}) do + reraise exception, stacktrace + end + @file_extensions [".html"] defp build_file_path(out, permalink) do if Path.extname(permalink) in @file_extensions do diff --git a/lib/tableau.ex b/lib/tableau.ex index 6cc2449..1f0b77f 100644 --- a/lib/tableau.ex +++ b/lib/tableau.ex @@ -51,3 +51,29 @@ defmodule Tableau do MDEx.to_html!(content, Keyword.merge(config.markdown[:mdex], overrides)) end end + +defmodule Tableau.BuildException do + @moduledoc false + @type t :: %__MODULE__{ + page: map(), + exception: any() + } + + defexception [:page, :exception] + + @impl true + def exception(page: page, exception: exception) do + %__MODULE__{page: page, exception: exception} + end + + @impl true + def message(%__MODULE__{page: page, exception: exception}) do + """ + An exception was raised: + #{Exception.format_banner(:error, exception)} + + occurred during the build process on the page: + #{inspect(page, pretty: true)} + """ + end +end diff --git a/lib/tableau_dev_server/router.ex b/lib/tableau_dev_server/router.ex index 699d042..d791994 100644 --- a/lib/tableau_dev_server/router.ex +++ b/lib/tableau_dev_server/router.ex @@ -1,6 +1,7 @@ defmodule TableauDevServer.Router do @moduledoc false use Plug.Router, init_mode: :runtime + use Plug.Debugger require Logger From 4ddcf146a1e287e1bb7f41c8ef8c76ffee715b05 Mon Sep 17 00:00:00 2001 From: Piotr Baj Date: Mon, 3 Mar 2025 18:40:34 +0100 Subject: [PATCH 2/5] Handle compiler errors from code reloader --- lib/tableau_dev_server/router.ex | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/tableau_dev_server/router.ex b/lib/tableau_dev_server/router.ex index d791994..8a993ee 100644 --- a/lib/tableau_dev_server/router.ex +++ b/lib/tableau_dev_server/router.ex @@ -33,11 +33,21 @@ defmodule TableauDevServer.Router do end defp recompile(conn, _) do - if conn.request_path != "/ws" do - WebDevUtils.CodeReloader.reload() + if conn.request_path == "/ws" do + conn + else + case WebDevUtils.CodeReloader.reload() do + {:error, errors} -> + errors = Enum.filter(errors, &(&1.severity == :error)) + message = Enum.reduce(errors, "", fn error, acc -> "#{acc}\n#{error.message}" end) + stacktrace = List.first(errors).stacktrace + + reraise CompileError, [description: message], stacktrace + + _ -> + conn + end end - - conn end defp rerender(conn, _) do From d088984a1fda81405b4d0fb17fe8d739b47596ee Mon Sep 17 00:00:00 2001 From: Piotr Baj Date: Tue, 4 Mar 2025 09:20:50 +0100 Subject: [PATCH 3/5] Implement changes from code review feedback --- lib/mix/tasks/tableau.build.ex | 11 ++++------- lib/tableau_dev_server/router.ex | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/mix/tasks/tableau.build.ex b/lib/mix/tasks/tableau.build.ex index 3c19cae..e28e1ac 100644 --- a/lib/mix/tasks/tableau.build.ex +++ b/lib/mix/tasks/tableau.build.ex @@ -54,7 +54,10 @@ defmodule Mix.Tasks.Tableau.Build do reraise Tableau.BuildException, [page: page, exception: exception], __STACKTRACE__ end end) - |> Stream.map(&map_pages/1) + |> Stream.map(fn + {:ok, result} -> result + {:exit, {exception, stacktrace}} -> reraise exception, stacktrace + end) |> Enum.to_list() token = put_in(token.site[:pages], pages) @@ -79,12 +82,6 @@ defmodule Mix.Tasks.Tableau.Build do token end - defp map_pages({:ok, result}), do: result - - defp map_pages({:exit, {exception, stacktrace}}) do - reraise exception, stacktrace - end - @file_extensions [".html"] defp build_file_path(out, permalink) do if Path.extname(permalink) in @file_extensions do diff --git a/lib/tableau_dev_server/router.ex b/lib/tableau_dev_server/router.ex index 8a993ee..5721b4a 100644 --- a/lib/tableau_dev_server/router.ex +++ b/lib/tableau_dev_server/router.ex @@ -39,7 +39,7 @@ defmodule TableauDevServer.Router do case WebDevUtils.CodeReloader.reload() do {:error, errors} -> errors = Enum.filter(errors, &(&1.severity == :error)) - message = Enum.reduce(errors, "", fn error, acc -> "#{acc}\n#{error.message}" end) + message = Enum.map_join(errors, "\n", & &1.message) stacktrace = List.first(errors).stacktrace reraise CompileError, [description: message], stacktrace From b77b40b03c3c0d7e3cf77a9f7050629e7dadceac Mon Sep 17 00:00:00 2001 From: Piotr Baj Date: Tue, 4 Mar 2025 11:08:41 +0100 Subject: [PATCH 4/5] Bump web_dev_utils --- mix.exs | 2 +- mix.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 5629c9a..67a60e9 100644 --- a/mix.exs +++ b/mix.exs @@ -42,7 +42,7 @@ defmodule Tableau.MixProject do {:plug_static_index_html, "~> 1.0"}, {:schematic, "~> 0.4"}, {:tz, "~> 0.28.1"}, - {:web_dev_utils, "~> 0.2"}, + {:web_dev_utils, "~> 0.3"}, {:websock_adapter, "~> 0.5"}, {:xml_builder, "~> 2.1"}, {:yaml_elixir, "~> 2.9"}, diff --git a/mix.lock b/mix.lock index 6e7866c..9c646e3 100644 --- a/mix.lock +++ b/mix.lock @@ -4,7 +4,7 @@ "date_time_parser": {:hex, :date_time_parser, "1.2.0", "3d5a816b91967f51e0f94dcb16a34b2cb780f22cd48931779e81d72f7d3eadb1", [:mix], [{:kday, "~> 1.0", [hex: :kday, repo: "hexpm", optional: false]}], "hexpm", "0cf09ada9f42c0b3bfba02dc0ea2e4b4d2f543d9d2bf99b831a29e6b4a4160e5"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, - "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, + "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, @@ -25,7 +25,7 @@ "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"}, "tz": {:hex, :tz, "0.28.1", "717f5ffddfd1e475e2a233e221dc0b4b76c35c4b3650b060c8e3ba29dd6632e9", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:mint, "~> 1.6", [hex: :mint, repo: "hexpm", optional: true]}], "hexpm", "bfdca1aa1902643c6c43b77c1fb0cb3d744fd2f09a8a98405468afdee0848c8a"}, - "web_dev_utils": {:hex, :web_dev_utils, "0.2.0", "3bcd109d1ac18445d06db095d5cfdca5de7586022641c0b09b0702788b8e4ea8", [:mix], [{:file_system, "~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "595e9bd35440ea2add0156ae9e68e578d986805d801dacf22b13067ca48980ed"}, + "web_dev_utils": {:hex, :web_dev_utils, "0.3.0", "463f3695dcdee9a6f18dbc783ee76f0ecdd1c9a489a6482aadcf166b3e5fee12", [:mix], [{:file_system, "~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "c239d4d24bfc61110a98b7d75bb7bbbb1752a5d8fcb5a2d369c5d4f64d1d7aec"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"}, "xml_builder": {:hex, :xml_builder, "2.3.0", "69d214c6ad41ae1300b36acff4367551cdfd9dc1b860affc16e103c6b1589053", [:mix], [], "hexpm", "972ec33346a225cd5acd14ab23d4e79042bd37cb904e07e24cd06992dde1a0ed"}, From aece5e3596c9f93896a16a71f07e358f35db161a Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Tue, 11 Mar 2025 19:09:11 -0400 Subject: [PATCH 5/5] strip ansi escape sequences from compiler error --- lib/mix/tasks/tableau.build.ex | 2 +- lib/tableau.ex | 26 ---------------------- lib/tableau_dev_server/build_exception.ex | 27 +++++++++++++++++++++++ lib/tableau_dev_server/router.ex | 7 +++++- 4 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 lib/tableau_dev_server/build_exception.ex diff --git a/lib/mix/tasks/tableau.build.ex b/lib/mix/tasks/tableau.build.ex index e28e1ac..53eb54c 100644 --- a/lib/mix/tasks/tableau.build.ex +++ b/lib/mix/tasks/tableau.build.ex @@ -51,7 +51,7 @@ defmodule Mix.Tasks.Tableau.Build do Map.merge(page, %{body: content, permalink: permalink}) rescue exception -> - reraise Tableau.BuildException, [page: page, exception: exception], __STACKTRACE__ + reraise TableauDevServer.BuildException, [page: page, exception: exception], __STACKTRACE__ end end) |> Stream.map(fn diff --git a/lib/tableau.ex b/lib/tableau.ex index 1f0b77f..6cc2449 100644 --- a/lib/tableau.ex +++ b/lib/tableau.ex @@ -51,29 +51,3 @@ defmodule Tableau do MDEx.to_html!(content, Keyword.merge(config.markdown[:mdex], overrides)) end end - -defmodule Tableau.BuildException do - @moduledoc false - @type t :: %__MODULE__{ - page: map(), - exception: any() - } - - defexception [:page, :exception] - - @impl true - def exception(page: page, exception: exception) do - %__MODULE__{page: page, exception: exception} - end - - @impl true - def message(%__MODULE__{page: page, exception: exception}) do - """ - An exception was raised: - #{Exception.format_banner(:error, exception)} - - occurred during the build process on the page: - #{inspect(page, pretty: true)} - """ - end -end diff --git a/lib/tableau_dev_server/build_exception.ex b/lib/tableau_dev_server/build_exception.ex new file mode 100644 index 00000000..79eb440 --- /dev/null +++ b/lib/tableau_dev_server/build_exception.ex @@ -0,0 +1,27 @@ +defmodule TableauDevServer.BuildException do + @moduledoc false + @type t :: %__MODULE__{ + page: map(), + exception: any() + } + + defexception [:page, :exception] + + @impl true + def exception(page: page, exception: exception) do + %__MODULE__{page: page, exception: exception} + end + + @impl true + def message(%__MODULE__{page: page, exception: exception}) do + exception = String.replace(exception, ~r/\x1B\[[0-9;]*m/, "") + + """ + An exception was raised: + #{Exception.format_banner(:error, exception)} + + occurred during the build process on the page: + #{inspect(page, pretty: true)} + """ + end +end diff --git a/lib/tableau_dev_server/router.ex b/lib/tableau_dev_server/router.ex index 5721b4a..279372e 100644 --- a/lib/tableau_dev_server/router.ex +++ b/lib/tableau_dev_server/router.ex @@ -39,7 +39,12 @@ defmodule TableauDevServer.Router do case WebDevUtils.CodeReloader.reload() do {:error, errors} -> errors = Enum.filter(errors, &(&1.severity == :error)) - message = Enum.map_join(errors, "\n", & &1.message) + + message = + errors + |> Enum.map_join("\n", & &1.message) + |> String.replace(~r/\x1B\[[0-9;]*m/, "") + stacktrace = List.first(errors).stacktrace reraise CompileError, [description: message], stacktrace