Packages
phoenix_kit
1.7.78
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/publishing/page_builder.ex
defmodule PhoenixKit.Modules.Publishing.PageBuilder do
@moduledoc """
Rendering pipeline for PHK (PhoenixKit) page content.
Processes component-based page definitions through:
1. Parse XML to AST
2. Inject dynamic data ({{variable}} placeholders)
3. Resolve components (map to actual component modules)
4. Apply theme/variants
5. Render to HTML
"""
alias PhoenixKit.Modules.Publishing.PageBuilder.Parser
alias PhoenixKit.Modules.Publishing.PageBuilder.Renderer
@type assigns :: map()
@type ast :: map()
@type render_result :: {:ok, Phoenix.LiveView.Rendered.t()} | {:error, term()}
@doc """
Renders PHK content directly from a string.
"""
@spec render_content(String.t(), assigns()) :: render_result()
def render_content(content, assigns \\ %{}) do
with {:ok, ast} <- parse_to_ast(content),
{:ok, ast_with_data} <- inject_dynamic_data(ast, assigns),
{:ok, resolved} <- resolve_components(ast_with_data),
{:ok, themed} <- apply_theme(resolved, assigns),
{:ok, html} <- render_to_html(themed, assigns) do
{:ok, html}
else
{:error, reason} -> {:error, reason}
end
end
# Step 1: Parse XML to AST
defp parse_to_ast(content) do
Parser.parse(content)
end
# Step 3: Inject dynamic data (replace {{variable}} placeholders)
defp inject_dynamic_data(ast, assigns) do
{:ok, inject_assigns(ast, assigns)}
end
# Step 4: Resolve components (map XML tags to actual component modules)
defp resolve_components(ast) do
{:ok, ast}
end
# Step 5: Apply theme/variant settings
defp apply_theme(ast, _assigns) do
{:ok, ast}
end
# Step 6: Render to HTML
defp render_to_html(ast, assigns) do
Renderer.render(ast, assigns)
end
# Recursively inject assigns into AST nodes
defp inject_assigns(ast, assigns) when is_map(ast) do
ast
|> Map.update(:content, nil, &inject_assigns(&1, assigns))
|> Map.update(:attributes, %{}, &inject_assigns(&1, assigns))
|> Map.update(:children, [], &inject_assigns(&1, assigns))
end
defp inject_assigns(ast, assigns) when is_list(ast) do
Enum.map(ast, &inject_assigns(&1, assigns))
end
defp inject_assigns(content, assigns) when is_binary(content) do
interpolate_string(content, assigns)
end
defp inject_assigns(value, _assigns), do: value
# Interpolate {{variable}} placeholders
defp interpolate_string(string, assigns) do
Regex.replace(~r/\{\{([^}]+)\}\}/, string, fn _, path ->
get_nested_value(assigns, String.trim(path)) |> to_string()
end)
end
# Get nested value from assigns (e.g., "user.name" -> assigns.user.name)
defp get_nested_value(map, path) do
path
|> String.split(".")
|> Enum.reduce(map, fn key, acc ->
case acc do
%{} -> Map.get(acc, key) || Map.get(acc, String.to_existing_atom(key))
_ -> nil
end
end)
rescue
_ -> ""
end
end