Skip to content

Commit

Permalink
Prevent crash when native layouts are undefined
Browse files Browse the repository at this point in the history
  • Loading branch information
supernintendo committed Nov 29, 2023
1 parent 4d469d7 commit 216dbd1
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 31 deletions.
96 changes: 72 additions & 24 deletions lib/live_view_native/layouts.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
defmodule LiveViewNative.Layouts do
def extract_layouts({:embed_templates, _meta, [template | _args]}, %{} = opts) do
def extract_layouts(%{file: file} = opts) do
file
|> File.read!()
|> Code.string_to_quoted!()
|> extract_layouts_recursive(opts)
|> List.flatten()
|> Enum.map(fn layout_params -> {layout_params.render_function, layout_params} end)
|> Enum.reject(&(format_excluded?(&1, opts)))
|> Enum.into(%{})
|> apply_default_layouts(opts)
end

def extract_layouts_recursive({:embed_templates, _meta, [template | _args]}, %{} = opts) do
base_path = Path.join([opts.dirname, Path.dirname(template)])
opts = Map.put(opts, :base_path, base_path)

Expand All @@ -9,15 +21,15 @@ defmodule LiveViewNative.Layouts do
|> Enum.filter(& &1)
end

def extract_layouts({_func, _meta, [_ | _] = nodes}, %{} = opts),
do: Enum.map(nodes, &extract_layouts(&1, opts))
def extract_layouts_recursive({_func, _meta, [_ | _] = nodes}, %{} = opts),
do: Enum.map(nodes, &extract_layouts_recursive(&1, opts))

def extract_layouts([do: {:__block__, [], args}], %{} = opts), do: extract_layouts(args, opts)
def extract_layouts_recursive([do: {:__block__, [], args}], %{} = opts), do: extract_layouts_recursive(args, opts)

def extract_layouts([_ | _] = nodes, %{} = opts),
do: Enum.map(nodes, &extract_layouts(&1, opts))
def extract_layouts_recursive([_ | _] = nodes, %{} = opts),
do: Enum.map(nodes, &extract_layouts_recursive(&1, opts))

def extract_layouts(_node, _opts), do: []
def extract_layouts_recursive(_node, _opts), do: []

def extract_layout(filename, %{platforms: platforms} = opts) do
template_path = Path.join(opts.base_path, filename)
Expand All @@ -27,7 +39,7 @@ defmodule LiveViewNative.Layouts do
|> compile_layout(template_path, opts)
end

def compile_layout({format, platform}, template_path, _opts) when format != "html" do
def compile_layout({_format, platform}, template_path, _opts) do
render_function_name =
template_path
|> Path.basename()
Expand Down Expand Up @@ -57,40 +69,76 @@ defmodule LiveViewNative.Layouts do
end
end

###

defp apply_default_layouts(%{} = layouts, %{default_layouts: true, platforms: platforms} = opts) do
platforms
|> Enum.reject(&(format_excluded?(&1, opts)))
|> Enum.flat_map(fn {format, %{default_layouts: %{} = default_layouts} = platform} ->
Enum.map(default_layouts, fn {layout_name, layout_source} ->
{String.to_atom("#{layout_name}_#{format}"), {layout_source, platform}}
end)
end)
|> Enum.into(%{})
|> Enum.reduce(layouts, fn {render_function_name, {layout_source, platform}}, %{} = acc ->
if Map.has_key?(acc, render_function_name) do
acc
else
Map.put(acc, render_function_name, %{
template: layout_source,
render_function: render_function_name,
template_path: nil,
eex_engine: platform.eex_engine,
platform_id: platform.platform_id,
tag_handler: platform.tag_handler
})
end
end)
end

defp apply_default_layouts(%{} = layouts, _opts), do: layouts

defp format_excluded?({_, %{platform_id: platform_id}}, %{} = opts) do
case opts do
%{exclude: [_ | _] = excluded_formats} ->
platform_id in excluded_formats

_ ->
false
end
end

defmacro __using__(_opts \\ []) do
quote bind_quoted: [caller: Macro.escape(__CALLER__)], location: :keep do
use LiveViewNative.Extensions, role: :layouts

layout_templates =
__ENV__.file
|> File.read!()
|> Code.string_to_quoted!()
|> LiveViewNative.Layouts.extract_layouts(%{
%{
caller: caller,
default_layouts: true,
dirname: Path.dirname(__ENV__.file),
exclude: [:html],
file: __ENV__.file,
platforms: LiveViewNative.platforms()
})
|> List.flatten()
|> Enum.map(fn %{} = layout_params ->
@external_resource layout_params.template_path
}
|> LiveViewNative.Layouts.extract_layouts()
|> Enum.map(fn {render_func, %{} = layout_params} ->
if layout_params.template_path do
@external_resource layout_params.template_path
end

eex_opts = [
caller: caller,
engine: layout_params.eex_engine,
file: __ENV__.file,
render_function: {layout_params.render_function, 1},
render_function: {render_func, 1},
source: layout_params.template,
tag_handler: layout_params.tag_handler
]
LiveViewNative.Templates.compile_class_tree(layout_params.template, layout_params.platform_id, eex_opts)
expr = LiveViewNative.Templates.with_stylesheet_wrapper(layout_params.template)

EEx.function_from_string(
:def,
layout_params.render_function,
LiveViewNative.Templates.with_stylesheet_wrapper(layout_params.template),
[:assigns],
eex_opts
)
EEx.function_from_string(:def, render_func, expr, [:assigns], eex_opts)
end)
end
end
Expand Down
7 changes: 4 additions & 3 deletions lib/live_view_native/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ defmodule LiveViewNative.Templates do
end

def compile_class_tree(expr, platform_id, eex_opts) do
with %Macro.Env{module: template_module} <- eex_opts[:caller],
%Meeseeks.Document{} = doc <- Meeseeks.parse(expr, :html),
%Macro.Env{module: template_module} = eex_opts[:caller]

with %Meeseeks.Document{} = doc <- Meeseeks.parse(expr, :html),
[_ | _] = class_names <- extract_all_class_names(doc),
%{} = class_tree_context <- class_tree_context(platform_id, template_module),
%{} = class_tree <- build_class_tree(class_tree_context, class_names, eex_opts)
do
dump_class_tree_bytecode(class_tree, template_module)
else
_fallback ->
# TODO: Generate fallback stylesheet module
dump_class_tree_bytecode(%{}, template_module)

:skipped
end
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ defmodule LiveViewNative.MixProject do
{:makeup_eex, ">= 0.1.1", only: :dev, runtime: false},
{:dialyxir, "~> 1.0", only: :dev, runtime: false},
{:meeseeks, "~> 0.17.0"},
{:live_view_native_platform, "0.2.0-beta.0"}
{:live_view_native_platform, "0.2.0-beta.2"}
]
end

Expand Down
6 changes: 3 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.1", "a22ed1e7bd3a3e3f197b68d806ef66acb61ee8f57b3ac85fc5d57354c5482a93", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "84b795d6d7796297cca5a3118444b80c7d94f7ce247d49886e7c291e1ae49801"},
"earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"},
"ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"},
"ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"},
"floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"live_view_native_platform": {:hex, :live_view_native_platform, "0.2.0-beta.0", "ea900fecb30f4a286609034ba0b58290b4d553b1cfdabdb24d3b57154a346220", [:mix], [{:ecto, "~> 3.8", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "e4b1bc28adc3234199a30a1d36d4f36d54c078aabaf6c725c372a8ddcc927303"},
"live_view_native_platform": {:hex, :live_view_native_platform, "0.2.0-beta.2", "8cd8eb8d18e2250bc497f1e0a5338430783ec97bf9f64b148df4bfb267c32ec4", [:mix], [{:ecto, "~> 3.8", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "2ee4626999348e643736e5993aa6f1458754e4c73b58b84197f2b446eb48ce50"},
"live_view_native_stylesheet": {:git, "https://github.com/liveview-native/live_view_native_stylesheet", "4ec329ebcbe81adacda7d864f2e2e406b42cb2ef", [branch: "main"]},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_eex": {:hex, :makeup_eex, "0.1.1", "89352d5da318d97ae27bbcc87201f274504d2b71ede58ca366af6a5fbed9508d", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.16", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_html, "~> 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d111a0994eaaab09ef1a4b3b313ef806513bb4652152c26c0d7ca2be8402a964"},
Expand All @@ -28,7 +28,7 @@
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"},
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
"plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"},
"plug": {:hex, :plug, "1.15.2", "94cf1fa375526f30ff8770837cb804798e0045fd97185f0bb9e5fcd858c792a3", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02731fa0c2dcb03d8d21a1d941bdbbe99c2946c0db098eee31008e04c6283615"},
"plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"},
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
Expand Down

0 comments on commit 216dbd1

Please sign in to comment.