Skip to content

Commit

Permalink
Fix tailwind parser (#376)
Browse files Browse the repository at this point in the history
* Compile CSS using the post-processed templates from pages

Close #194

* make sure the template is loaded

* load css after loading pages

so the page module is updated on ets
  • Loading branch information
leandrocp authored Nov 21, 2023
1 parent 62ca059 commit a2471d1
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 43 deletions.
2 changes: 1 addition & 1 deletion lib/beacon/lifecycle/template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ defmodule Beacon.Lifecycle.Template do
@spec render_template(Beacon.Content.Page.t(), module(), map(), Macro.Env.t()) :: Beacon.Template.t()
def render_template(page, page_module, assigns, env) do
template =
case page_module.render(assigns) do
case Beacon.Template.render(page_module, assigns) do
%Phoenix.LiveView.Rendered{} = rendered -> rendered
:not_loaded -> Beacon.Loader.load_page_template(page, page_module, assigns)
end
Expand Down
50 changes: 28 additions & 22 deletions lib/beacon/loader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ defmodule Beacon.Loader do

defp load_site_from_db(site) do
with :ok <- Beacon.RuntimeJS.load!(),
:ok <- load_runtime_css(site),
:ok <- load_stylesheets(site),
:ok <- load_components(site),
:ok <- load_snippet_helpers(site),
:ok <- load_layouts(site),
:ok <- load_pages(site),
:ok <- load_error_pages(site) do
:ok <- load_error_pages(site),
:ok <- load_stylesheets(site),
:ok <- load_runtime_css(site) do
:ok
else
_ -> raise Beacon.LoaderError, message: "failed to load resources for site #{site}"
Expand Down Expand Up @@ -199,12 +199,15 @@ defmodule Beacon.Loader do

# too slow to run the css compiler on every test
if Code.ensure_loaded?(Mix.Project) and Mix.env() == :test do
defp load_runtime_css(_site), do: :ok
@doc false
def load_runtime_css(_site), do: :ok
else
defp load_runtime_css(site), do: Beacon.RuntimeCSS.load!(site)
@doc false
def load_runtime_css(site), do: Beacon.RuntimeCSS.load!(site)
end

defp load_stylesheets(site) do
@doc false
def load_stylesheets(site) do
StylesheetModuleLoader.load_stylesheets(site, Content.list_stylesheets(site))
:ok
end
Expand All @@ -222,7 +225,8 @@ defmodule Beacon.Loader do
:ok
end

defp load_layouts(site) do
@doc false
def load_layouts(site) do
site
|> Content.list_published_layouts()
|> Enum.map(fn layout ->
Expand All @@ -236,7 +240,8 @@ defmodule Beacon.Loader do
:ok
end

defp load_pages(site) do
@doc false
def load_pages(site) do
site
|> Content.list_published_pages()
|> Enum.map(fn page ->
Expand Down Expand Up @@ -360,8 +365,9 @@ defmodule Beacon.Loader do

@doc false
def handle_call({:load_page, page}, _from, config) do
result = do_load_page!(page)
:ok = load_runtime_css(page.site)
{:reply, do_load_page(page), config}
{:reply, result, config}
end

def handle_call({:load_page_template, page, page_module, assigns}, _from, config) do
Expand All @@ -379,14 +385,14 @@ defmodule Beacon.Loader do
def handle_info({:layout_published, %{site: site, id: id}}, state) do
layout = Content.get_published_layout(site, id)

with :ok <- load_runtime_css(site),
# TODO: load only used components, depends on https://github.com/BeaconCMS/beacon/issues/84
:ok <- load_components(site),
# TODO: load only used components, depends on https://github.com/BeaconCMS/beacon/issues/84
with :ok <- load_components(site),
# TODO: load only used snippet helpers
:ok <- load_snippet_helpers(site),
:ok <- load_stylesheets(site),
{:ok, _module, _ast} <- Beacon.Loader.LayoutModuleLoader.load_layout!(layout),
:ok <- maybe_reload_error_pages(layout) do
:ok <- maybe_reload_error_pages(layout),
:ok <- load_runtime_css(site),
:ok <- load_stylesheets(site) do
:ok
else
_ -> raise Beacon.LoaderError, message: "failed to load resources for layout #{layout.title} of site #{layout.site}"
Expand All @@ -397,25 +403,25 @@ defmodule Beacon.Loader do

@doc false
def handle_info({:page_published, %{site: site, id: id}}, state) do
:ok = load_runtime_css(site)

site
|> Content.get_published_page(id)
|> do_load_page()
|> do_load_page!()

:ok = load_runtime_css(site)

{:noreply, state}
end

@doc false
def handle_info({:pages_published, site, pages}, state) do
:ok = load_runtime_css(site)

for page <- pages do
site
|> Content.get_published_page(page.id)
|> do_load_page()
|> do_load_page!()
end

:ok = load_runtime_css(site)

{:noreply, state}
end

Expand Down Expand Up @@ -449,9 +455,9 @@ defmodule Beacon.Loader do
{:noreply, state}
end

defp do_load_page(page) when is_nil(page), do: nil
defp do_load_page!(page) when is_nil(page), do: nil

defp do_load_page(page) do
defp do_load_page!(page) do
layout = Content.get_published_layout(page.site, page.layout_id)

# TODO: load only used components, depends on https://github.com/BeaconCMS/beacon/issues/84
Expand Down
2 changes: 1 addition & 1 deletion lib/beacon/loader/page_module_loader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ defmodule Beacon.Loader.PageModuleLoader do

with %Content.Page{} = page <- Beacon.Content.get_published_page(page.site, page.id),
{:ok, ^page_module, _ast} <- do_load_page!(page, :request),
%Phoenix.LiveView.Rendered{} = rendered <- page_module.render(assigns) do
%Phoenix.LiveView.Rendered{} = rendered <- Beacon.Template.render(assigns) do
rendered
else
_ ->
Expand Down
4 changes: 3 additions & 1 deletion lib/beacon/media_library.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,16 @@ defmodule Beacon.MediaLibrary do
|> Enum.map(&get_url_for(&1, asset))
end

def srcset_for_image(asset, sources) do
def srcset_for_image(%Asset{} = asset, sources) do
asset = Repo.preload(asset, :assets)

asset.assets
|> filter_sources(sources)
|> build_srcset()
end

def srcset_for_image(_asset, _sources), do: []

defp filter_sources(assets, sources) do
Enum.filter(
assets,
Expand Down
24 changes: 20 additions & 4 deletions lib/beacon/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,26 @@ defmodule Beacon.Router do

@doc false
def dump_pages do
case :ets.match(@ets_table, :"$1") do
[] -> []
[pages] -> pages
end
@ets_table |> :ets.match(:"$1") |> List.flatten()
end

@doc false
def dump_pages(site) do
match = {{site, :_}, :_}
guards = []
body = [:"$_"]

@ets_table
|> :ets.select([{match, guards, body}])
|> List.flatten()
end

def dump_page_modules(site, fun \\ &Function.identity/1) do
site
|> dump_pages()
|> Enum.map(fn {{^site, _path} = key, {_page_id, _layout_id, _format, page_module, _component_module}} ->
fun.(Tuple.append(key, page_module))
end)
end

@doc false
Expand Down
31 changes: 21 additions & 10 deletions lib/beacon/tailwind_compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,6 @@ defmodule Beacon.TailwindCompiler do

defp generate_template_files!(tmp_dir, site) do
[
Task.async(fn ->
Enum.map(Content.list_layouts(site, per_page: :infinity), fn layout ->
layout_path = Path.join(tmp_dir, "#{site}_layout_#{remove_special_chars(layout.title)}.template")
File.write!(layout_path, layout.template)
layout_path
end)
end),
Task.async(fn ->
Enum.map(Beacon.Content.list_components(site, per_page: :infinity), fn component ->
component_path = Path.join(tmp_dir, "#{site}_component_#{remove_special_chars(component.name)}.template")
Expand All @@ -159,9 +152,24 @@ defmodule Beacon.TailwindCompiler do
end)
end),
Task.async(fn ->
Enum.map(Content.list_pages(site, per_page: :infinity), fn page ->
page_path = Path.join(tmp_dir, "#{site}_page_#{remove_special_chars(page.path)}.template")
File.write!(page_path, page.template)
Enum.map(Content.list_layouts(site, per_page: :infinity), fn layout ->
layout_path = Path.join(tmp_dir, "#{site}_layout_#{remove_special_chars(layout.title)}.template")
File.write!(layout_path, layout.template)
layout_path
end)
end),
Task.async(fn ->
# parse from laoded pages (ETS) so it can fetch callback transformations
# thay may include additoinal stylesheet classes as the markdown parser does
Beacon.Router.dump_page_modules(site, fn {_site, path, page_module} ->
template =
page_module
|> Beacon.Template.render()
|> fetch_static()
|> List.to_string()

page_path = Path.join(tmp_dir, "#{site}_page_#{remove_special_chars(path)}.template")
File.write!(page_path, template)
page_path
end)
end),
Expand All @@ -177,6 +185,9 @@ defmodule Beacon.TailwindCompiler do
|> List.flatten()
end

defp fetch_static(%{static: static}), do: static
defp fetch_static(_), do: []

# import app css into input css used by tailwind-cli to load tailwind functions and directives
defp generate_input_css_file!(tmp_dir, site) do
beacon_tailwind_css_path = Path.join([Application.app_dir(:beacon), "priv", "beacon_tailwind.css"])
Expand Down
9 changes: 8 additions & 1 deletion lib/beacon/template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ defmodule Beacon.Template do
# it is NOT supposed to be used to render templates
def __render__(site, path_list) when is_list(path_list) do
case Beacon.Router.lookup_path(site, path_list) do
{{site, path}, {page_id, _layout_id, format, page_module, component_module}} ->
{{^site, path}, {page_id, _layout_id, format, page_module, component_module}} ->
assigns = %{__changed__: %{}, __live_path__: [], __beacon_page_module__: page_module, __beacon_component_module__: component_module}
page = %Beacon.Content.Page{id: page_id, site: site, path: path, format: format}
Beacon.Lifecycle.Template.render_template(page, page_module, assigns, BeaconWeb.PageLive.make_env())
Expand All @@ -59,6 +59,13 @@ defmodule Beacon.Template do
end
end

@doc false
def render(page_module, assigns \\ %{}) when is_atom(page_module) and is_map(assigns) do
%{__changed__: %{}, __live_path__: [], beacon_path_params: %{}, beacon_live_data: %{}}
|> Map.merge(assigns)
|> page_module.render()
end

@doc false
def choose_template([primary]), do: primary
def choose_template([primary | variants]), do: choose_template(variants, Enum.random(1..100), primary)
Expand Down
38 changes: 35 additions & 3 deletions test/beacon/tailwind_compiler_test.exs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
defmodule Beacon.TailwindCompilerTest do
use Beacon.DataCase, async: true
use Beacon.DataCase, async: false

import ExUnit.CaptureIO
import Beacon.Fixtures
alias Beacon.TailwindCompiler

@site :my_site

setup_all do
start_supervised!({Beacon.Loader, Beacon.Config.fetch!(:my_site)})
:ok
end

defp create_page(_) do
stylesheet_fixture()

Expand All @@ -17,15 +24,16 @@ defmodule Beacon.TailwindCompilerTest do
)

layout =
layout_fixture(
published_layout_fixture(
template: """
<header class="text-gray-100">Page header</header>
<%= @inner_content %>
"""
)

page_fixture(
published_page_fixture(
layout_id: layout.id,
path: "/a",
template: """
<main>
<h2 class="text-gray-200">Some Values:</h2>
Expand All @@ -36,6 +44,22 @@ defmodule Beacon.TailwindCompilerTest do
"""
)

page_fixture(
layout_id: layout.id,
path: "/b",
template: """
<main>
<h2 class="text-gray-300">Some Values:</h2>
</main>
"""
)

Beacon.Loader.load_stylesheets(@site)
Beacon.Loader.load_components(@site)
Beacon.Loader.load_layouts(@site)
Beacon.Loader.load_pages(@site)
Beacon.Loader.load_runtime_css(@site)

:ok
end

Expand All @@ -56,5 +80,13 @@ defmodule Beacon.TailwindCompilerTest do
assert output =~ "text-gray-200"
end)
end

test "do not include classes from unpublished pages" do
capture_io(fn ->
assert {:ok, output} = TailwindCompiler.compile(:my_site)

refute output =~ "text-gray-300"
end)
end
end
end

0 comments on commit a2471d1

Please sign in to comment.