Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed new template re-compilation issue #176

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/live_view_native/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,24 @@ defmodule LiveViewNative.Component do
case LiveViewNative.fetch_plugin(format) do
{:ok, plugin} ->
quote do
Module.register_attribute(__MODULE__, :template_files, accumulate: true)
Module.register_attribute(__MODULE__, :embeded_templates_opts, accumulate: true)

import LiveViewNative.Renderer, only: [
delegate_to_target: 1,
delegate_to_target: 2,
embed_templates: 1,
embed_templates: 2
]

use unquote(plugin.component)

if (unquote(opts[:as])) do
@before_compile LiveViewNative.Renderer
end

@before_compile LiveViewNative.Component
@before_compile {LiveViewNative.Renderer, :__inject_mix_recompile__}
end

:error ->
Expand Down
169 changes: 113 additions & 56 deletions lib/live_view_native/renderer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,34 @@ defmodule LiveViewNative.Renderer do
end
end

@doc false
defmacro __inject_mix_recompile__(_env) do
quote do
@template_file_hash @template_files |> Enum.sort() |> :erlang.md5()

@doc false
def __mix_recompile__? do
files =
@embeded_templates_opts
|> Enum.reduce([], fn({root, pattern, name}, templates_acc) ->
root
|> LiveViewNative.Renderer.find_templates(pattern, __MODULE__, name)
|> Enum.reduce(templates_acc, fn
{:no_embed, _reason}, templates_acc -> templates_acc
{:embed, templates}, templates_acc -> templates_acc ++ templates
end)
end)

file_hash =
files
|> Enum.sort()
|> :erlang.md5()

!(file_hash == @template_file_hash)
end
end
end

@doc false
defmacro delegate_to_target(name, opts \\ []) do
%{module: module} = env = __CALLER__
Expand All @@ -33,6 +61,7 @@ defmodule LiveViewNative.Renderer do
[]
else
quote do
@doc false
def unquote(name)(var!(assigns)) do
interface = LiveViewNative.Utils.get_interface(var!(assigns))
apply(__MODULE__, unquote(name), [var!(assigns), interface])
Expand Down Expand Up @@ -71,20 +100,51 @@ defmodule LiveViewNative.Renderer do
%{module: module} = env = __CALLER__
native_opts = Module.get_attribute(module, :native_opts)
format = native_opts[:format]

root = build_root(env.file, opts[:root])
name = opts[:name]

root
|> Phoenix.Template.find_all(pattern)
|> Enum.chunk_by(&chunk_name(&1))
|> ensure_naming_uniq(env, opts[:name])

attr_ast = quote do
Module.put_attribute(__MODULE__, :embeded_templates_opts, {
unquote(root),
unquote(pattern),
unquote(name)
})
end

templates_ast = root
|> find_templates(pattern, module, name)
|> Enum.map(&(__embed_templates__(&1,
format: format,
name: opts[:name],
env: env,
root: root,
pattern: pattern
)))

[attr_ast | templates_ast]
end

@doc false
def find_templates(root, pattern, module, default_name) do
root
|> Phoenix.Template.find_all(pattern)
|> Enum.chunk_by(&chunk_name(&1))
|> ensure_naming_uniq(pattern, default_name)
|> Enum.map(fn(templates) ->
name = build_name(templates, default_name)
render? = case Code.ensure_compiled(module) do
{:error, _} -> Module.defines?(module, {name, 2})
{:module, _} -> false
end

case {render?, templates} do
{true, [_template | _templates]} -> {:no_embed, :render_defined_with_templates}
{true, []} -> {:no_embed, :render_defined_no_templates}
{false, []} -> {:no_embed, :no_render_no_templates}
{false, templates} -> {:embed, templates}
end
end)
end

# this function ensures there is a single template group when applying a custom render function name
Expand Down Expand Up @@ -115,74 +175,71 @@ defmodule LiveViewNative.Renderer do
|> List.first()
end

defp __embed_templates__(templates, opts) do
defp __embed_templates__({:no_embed, reason}, opts) do
%{module: module} = env = opts[:env]
format = opts[:format]
name = build_name(templates, opts[:name])
name = build_name([], opts[:name])

render? = Module.defines?(module, {name, 2})
filename = build_filename(module, format)

case {render?, templates} do
{true, [_template | _templates]} ->
case reason do
:render_defined_with_templates ->
IO.warn(
"You have #{module}.render/2 defined as well as at least one template file. You must remove " <>
" #{module}.render/2 if you wish to use any template files.",
"You have #{module}.#{name}/2 defined as well as at least one template file. You must remove " <>
" #{module}.#{name}/2 if you wish to use any template files.",
Macro.Env.stacktrace(env)
)

[]

{true, []} -> []

{false, []} ->
:render_defined_no_templates -> []
:no_render_no_templates ->
IO.warn(
"You do not have any templates or any `render/2` functions defined for #{module}.",
Macro.Env.stacktrace(env)
)

[]

{false, templates} ->
templates
|> Enum.sort(&(String.length(&1) >= String.length(&2)))
|> Enum.map(fn(template) ->

engine = Map.fetch!(LiveViewNative.Template.engines(), format)
ast = engine.compile(template, filename)

case extract_target(template, format) do
nil ->
quote do
@file unquote(template)
@external_resource unquote(template)
def unquote(name)(var!(assigns), _interface) do
unquote(ast)
end
end

target ->
quote do
@file unquote(template)
@external_resource unquote(template)
def unquote(name)(var!(assigns), %{"target" => unquote(target)}) do
unquote(ast)
end
end
end
end)
_unmatched_reason -> []
end
|> inject_target_delegate(name)
end

defp inject_target_delegate([], _name), do: []
defp inject_target_delegate(quoted_renders, name) do
quoted_render =
quote do
delegate_to_target unquote(name)
end
defp __embed_templates__({:embed, templates}, opts) do
%{module: module} = opts[:env]
format = opts[:format]
name = build_name(templates, opts[:name])
filename = build_filename(module, format)

templates
|> Enum.sort(&(String.length(&1) >= String.length(&2)))
|> Enum.map(fn(template) ->

engine = Map.fetch!(LiveViewNative.Template.engines(), format)
ast = engine.compile(template, filename)

case extract_target(template, format) do
nil ->
quote do
@file unquote(template)
@external_resource unquote(template)
@template_files unquote(template)
@doc false
def unquote(name)(var!(assigns), _interface) do
unquote(ast)
end
end

[quoted_render | quoted_renders]
target ->
quote do
@file unquote(template)
@external_resource unquote(template)
@template_files unquote(template)
@doc false
def unquote(name)(var!(assigns), %{"target" => unquote(target)}) do
unquote(ast)
end
end
end
end)
|> List.insert_at(-1, quote do
delegate_to_target unquote(name)
end)
end

defp extract_target(template, format) do
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
%{
"castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"},
"castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"},
"cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"floki": {:hex, :floki, "0.36.1", "712b7f2ba19a4d5a47dfe3e74d81876c95bbcbee44fe551f0af3d2a388abb3da", [:mix], [], "hexpm", "21ba57abb8204bcc70c439b423fc0dd9f0286de67dc82773a14b0200ada0995f"},
"floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
"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_test": {:git, "https://github.com/liveview-native/live_view_native_test.git", "f36efa463e172df27d50ab0bcbd16f2e59e6c05b", [tag: "v0.3.0"]},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
Expand Down
Loading