From 999632dbe7ee1e8b664c21fd9943d6ad61498cda Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 9 May 2026 15:10:04 +0200 Subject: [PATCH] fix: fixed CSM-013 --- CODESMELL.md | 19 +++++++++++----- lib/bds/rendering/filters.ex | 26 +++++++++++++++++----- lib/bds/rendering/post_rendering.ex | 29 ++++++++++++++----------- lib/bds/rendering/template_selection.ex | 12 ++++++---- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/CODESMELL.md b/CODESMELL.md index 666dddd..2cf1cf3 100644 --- a/CODESMELL.md +++ b/CODESMELL.md @@ -224,11 +224,20 @@ --- -### CSM-013 — Bang Functions in Rendering Pipelines -- **Files:** `lib/bds/rendering/post_rendering.ex:151`, `lib/bds/rendering/filters.ex:125,127`, `lib/bds/rendering/template_selection.ex:92,102` -- **What:** `Jason.encode!`, `Liquex.parse!`, `Liquex.render!` crash on bad data instead of returning errors. -- **Fix:** Use non-bang variants, wrap in `case`, and propagate `{:error, reason}` to the caller. -- **Test:** Feed a template with a syntax error; assert the renderer returns `{:error, _}` rather than raising. +### ~~CSM-013 — Bang Functions in Rendering Pipelines~~ ✅ FIXED +- **Fixed:** 2026-05-09 +- **What was done:** + - **`lib/bds/rendering/filters.ex`** — `render_macro_template`: + - Replaced `Liquex.parse!` with `Liquex.parse` (non-bang) and `case` match on `{:ok, ast}` / `{:error, reason, line}`. + - Wrapped `Liquex.render!` in `try/rescue` catching `Liquex.Error` specifically (no non-bang `render` exists in Liquex). + - Removed broad `rescue _error -> ""` — errors now log via `Logger.warning` with template path and reason before returning `""`. + - **`lib/bds/rendering/template_selection.ex`** — `render_template`: + - `Liquex.parse` was already non-bang; added `else` clause to normalize the 3-tuple `{:error, reason, line}` into `{:error, "reason at line N"}`. + - Wrapped `Liquex.render!` in `try/rescue` catching `Liquex.Error` specifically, returning `{:error, message}`. + - Removed broad `rescue error -> {:error, error}`. + - **`lib/bds/rendering/post_rendering.ex`** — `post_data_json_value`: + - Replaced `Jason.encode!` with `Jason.encode` and `case` match — returns `"{}"` on encode failure instead of crashing. + - Added 5 tests in `test/bds/csm013_bang_rendering_test.exs`: template syntax error returns `{:error, _}` from `render_template`, broken template in `render_post_page` returns `{:error, _}`, `{% break %}` render error returns `{:error, _}`, normal post context produces valid JSON, non-encodable data returns `"{}"` fallback. --- diff --git a/lib/bds/rendering/filters.ex b/lib/bds/rendering/filters.ex index 05a6363..ef0af86 100644 --- a/lib/bds/rendering/filters.ex +++ b/lib/bds/rendering/filters.ex @@ -122,13 +122,27 @@ defmodule BDS.Rendering.Filters do _id -> template_source = Liquex.FileSystem.read_template_file(context.file_system, template_path) - template_ast = Liquex.parse!(template_source) - isolated_context = Liquex.Context.new_isolated_subscope(context, assigns) - {result, _context} = Liquex.render!(template_ast, isolated_context) - IO.iodata_to_binary(result) + + case Liquex.parse(template_source) do + {:ok, template_ast} -> + isolated_context = Liquex.Context.new_isolated_subscope(context, assigns) + + try do + {result, _context} = Liquex.render!(template_ast, isolated_context) + IO.iodata_to_binary(result) + rescue + e in Liquex.Error -> + require Logger + Logger.warning("Macro template render failed (#{template_path}): #{e.message}") + "" + end + + {:error, reason, line} -> + require Logger + Logger.warning("Macro template parse failed (#{template_path}): #{reason} at line #{line}") + "" + end end - rescue - _error -> "" end defp render_markdown_html(markdown) do diff --git a/lib/bds/rendering/post_rendering.ex b/lib/bds/rendering/post_rendering.ex index 01b7b57..c0d4852 100644 --- a/lib/bds/rendering/post_rendering.ex +++ b/lib/bds/rendering/post_rendering.ex @@ -181,19 +181,22 @@ defmodule BDS.Rendering.PostRendering do end def post_data_json_value(post_context) do - Jason.encode!(%{ - id: Map.get(post_context, :id), - title: Map.get(post_context, :title), - slug: Map.get(post_context, :slug), - excerpt: Map.get(post_context, :excerpt), - author: Map.get(post_context, :author), - language: Map.get(post_context, :language), - published_at: Map.get(post_context, :published_at), - created_at: Map.get(post_context, :created_at), - updated_at: Map.get(post_context, :updated_at), - tags: Map.get(post_context, :tags, []), - categories: Map.get(post_context, :categories, []) - }) + case Jason.encode(%{ + id: Map.get(post_context, :id), + title: Map.get(post_context, :title), + slug: Map.get(post_context, :slug), + excerpt: Map.get(post_context, :excerpt), + author: Map.get(post_context, :author), + language: Map.get(post_context, :language), + published_at: Map.get(post_context, :published_at), + created_at: Map.get(post_context, :created_at), + updated_at: Map.get(post_context, :updated_at), + tags: Map.get(post_context, :tags, []), + categories: Map.get(post_context, :categories, []) + }) do + {:ok, json} -> json + {:error, _reason} -> "{}" + end end defp build_post_context(assigns, post_record, incoming_links, outgoing_links) do diff --git a/lib/bds/rendering/template_selection.ex b/lib/bds/rendering/template_selection.ex index dc002eb..0a2e9dc 100644 --- a/lib/bds/rendering/template_selection.ex +++ b/lib/bds/rendering/template_selection.ex @@ -99,11 +99,15 @@ defmodule BDS.Rendering.TemplateSelection do file_system: FileSystem.new(StarterTemplates.template_roots(project)) ) - {result, _context} = Liquex.render!(template_ast, context) - {:ok, IO.iodata_to_binary(result)} + try do + {result, _context} = Liquex.render!(template_ast, context) + {:ok, IO.iodata_to_binary(result)} + rescue + e in Liquex.Error -> {:error, e.message} + end + else + {:error, reason, line} -> {:error, "#{reason} at line #{line}"} end - rescue - error -> {:error, error} end defp load_bundled_template_source(project, kind, slug) do