~mika/www.m1kadev.nl

ref: dce311bd3c1f578889cf6db2742444875d820341 www.m1kadev.nl/kethel/lib/pages.ex -rw-r--r-- 2.7 KiB
dce311bd — m1ka im actually stupid 3 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
require Rambo

defmodule Pages do
  defstruct [:source_files, :main_template, :project_root, :bricks]

  defp trim_abs_path(path, project_root) do
    # ROOT + pages/
    begin = byte_size(project_root) + byte_size("pages/")
    # remove extension
    binary_part(path, begin, byte_size(path) - begin - byte_size(".fxg"))
  end

  @spec collect(%Context{}, Bricks.bricks()) :: %Pages{}
  def collect(project_root, bricks) do
    pages_root = project_root <> "/pages"

    %Pages{
      project_root: project_root,
      source_files:
        Path.wildcard(pages_root <> "/**/*.fxg")
        |> Task.async_stream(fn path -> {trim_abs_path(path, project_root), File.read!(path)} end)
        # we assume every file read succeeds :)) always be positive
        |> Enum.map(fn {:ok, val} -> val end),
      main_template: File.read!(project_root <> "/templates/base.thtml"),
      bricks: bricks
    }
  end

  @spec compile(%Pages{}) :: none()
  def compile(pages) do
    t_begin = System.monotonic_time(:millisecond)

    Task.async_stream(pages.source_files, fn page -> compile_file(pages, page) end)
    |> Enum.to_list()

    t_end = System.monotonic_time(:millisecond)
    t_end - t_begin
  end

  # doc "compiles a file, already read into process memory. Does not return the binary, but instantly writes it to disk"
  @spec compile_file(%Pages{}, {}) :: none()
  defp compile_file(pages, {page_path, page_data}) do
    output_path = "#{pages.project_root}/build#{page_path}.html"
    # check for a custom template
    custom_template_path = "#{pages.project_root}/templates/pages#{page_path}.thtml"

    template =
      case File.read(custom_template_path) do
        {:ok, custom_template} -> custom_template
        {:error, :enoent} -> pages.main_template
        {:error, err} -> raise err
      end

    location = page_path |> binary_slice(1..byte_size(page_data))

    mustache_context =
      Map.merge(%{fxg_content: invoke_fxg(page_data), location: location}, pages.bricks)

    page_rendered = Mustache.render(template, mustache_context)
    File.write!(output_path, invoke_hmn(page_rendered))
    IO.puts("[COMPILED] #{pages.project_root}/pages#{page_path}.fxg -> #{output_path}")
  end

  # doc "Invokes the fxg compiler, a la `fxg -`, piping `data` into the process stdin, and reading the stdout. "
  @spec invoke_fxg(binary()) :: binary()
  defp invoke_fxg(data) do
    {:ok, res} = Rambo.run("fxg", ["-"], in: data)
    res.out
  end

  # doc "Invokes the fxg compiler, a la `fxg -`, piping `data` into the process stdin, and reading the stdout. "
  @spec invoke_hmn(binary()) :: binary()
  defp invoke_hmn(data) do
    {:ok, res} = Rambo.run("npx", ["html-minifier-next", "--preset", "conservative"], in: data)
    res.out
  end
end