From 4d656d6d3c0d08b651351dd9aa938794e7a481b6 Mon Sep 17 00:00:00 2001 From: m1kadev Date: Wed, 7 Jan 2026 22:14:34 +0100 Subject: [PATCH] Start refactoring build.exs into kethel --- build.exs | 371 ------------------------------------------ kethel/.formatter.exs | 4 + kethel/.gitignore | 23 +++ kethel/lib/bricks.ex | 17 ++ kethel/main.exs | 11 ++ kethel/mix.exs | 20 +++ kethel/mix.lock | 12 ++ 7 files changed, 87 insertions(+), 371 deletions(-) delete mode 100644 build.exs create mode 100644 kethel/.formatter.exs create mode 100644 kethel/.gitignore create mode 100644 kethel/lib/bricks.ex create mode 100644 kethel/main.exs create mode 100644 kethel/mix.exs create mode 100644 kethel/mix.lock diff --git a/build.exs b/build.exs deleted file mode 100644 index c5d0ed06842c97a9df3b7a1e0a798931652ccce2..0000000000000000000000000000000000000000 --- a/build.exs +++ /dev/null @@ -1,371 +0,0 @@ -require Mix -require EEx - -Mix.install([ - {:tzdata, "~> 1.1"}, - {:mustache, "~> 0.5.0"} -]) - -Calendar.put_time_zone_database(Tzdata.TimeZoneDatabase) - -defmodule Git do - def get_file_commit(path) do - {ts_string, 0} = System.cmd("git", ["log", "--format=%ct", path]) - String.to_integer(String.trim_trailing(ts_string)) - end -end - -defmodule Templates do - binfo_template = """ - commit=<%= commit %>lightningcss=<%= lightningcss %>html_minifier_next=<%= html_minifier_next %>uglifyjs=<%= uglifyjs %>build_time=<%= build_time %> - """ - - pastelink_template = """ - <%= name %> | <%= date %> - """ - - EEx.function_from_string( - :def, - :build_info, - binfo_template, - [ - :commit, - :lightningcss, - :html_minifier_next, - :uglifyjs, - :build_time - ] - ) - - EEx.function_from_string( - :def, - :pastelink, - pastelink_template, - [ - :location, - :name, - :date - ] - ) - - def ogp(title, url, description) do - """ - - - - - - - - """ - end -end - -defmodule Builders do - def html(input_path) do - output_path = String.replace_prefix(input_path, "src", "build") - - {_, 0} = - System.cmd("html-minifier-next", [ - "--preset", - "conservative", - "-o", - output_path, - input_path - ]) - end - - def css(input_path) do - output_path = "build/" <> input_path - - {_, 0} = - System.cmd("lightningcss", [ - "-m", - "-o", - output_path, - input_path - ]) - end - - def js(input_path) do - output_path = "build/" <> input_path - - {_, 0} = - System.cmd("uglifyjs", [ - "-c", - "-o", - output_path, - input_path - ]) - end - - def fxg(input_path, template, bricks) do - own_template = - input_path - |> String.replace_prefix("pages", "templates/pages") - |> String.replace_suffix("fxg", "html") - - case File.read(own_template) do - {:ok, ctemplate} -> _fxg(input_path, ctemplate, bricks) - _ -> _fxg(input_path, template, bricks) - end - end - - def paste(input_path, template, bricks) do - {:ok, code} = File.read(input_path) - IO.inspect(code) - - output = "build/" <> input_path <> ".html" - - lang = Path.extname(input_path) |> String.slice(1..-1//1) - file = Path.basename(input_path) - time = FileX.createdf(input_path) - - result = - Mustache.render( - template, - Map.merge( - %{ - filename: file, - about_paste: "Created on " <> time, - language: "", - codetag: "
 lang <> "\">",
-            code: code,
-            ogp:
-              Templates.ogp(file, "https://m1kadev.nl/pastes/#{file}.html", "Created on " <> time)
-          },
-          bricks
-        )
-      )
-
-    IO.inspect(input_path)
-
-    :ok = File.write(output, result)
-  end
-
-  defp _fxg(input_path, template, bricks) do
-    output_path =
-      String.replace_prefix(input_path, "pages", "build")
-      |> String.replace_suffix("fxg", "html")
-
-    name =
-      case Path.basename(input_path) |> Path.rootname() do
-        "index" -> "home"
-        x -> x
-      end
-
-    {content, 0} = System.cmd("fxg", [input_path])
-
-    ogp =
-      Templates.ogp(
-        name,
-        "https://m1kadev.nl/#{String.replace_prefix(output_path, "build/", "")}",
-        ""
-      )
-
-    output =
-      Mustache.render(
-        template,
-        Map.merge(
-          %{
-            fxg_content: content,
-            location: name,
-            ogp: ogp
-          },
-          bricks
-        )
-      )
-
-    IO.inspect(output_path)
-    :ok = File.write(output_path, output)
-  end
-
-  def thought(path, unix) do
-    thought = File.read!(path)
-    name = String.replace_prefix(path, "thoughts/", "")
-
-    date = DateTime.from_unix!(unix) |> Calendar.strftime("%d/%m/%y (%H:%M)")
-
-    """
-    
-

#{name}

- #{thought} - -mika, #{date} -
- """ - end -end - -defmodule FileX do - def createdf(path) do - {:ok, %File.Stat{mtime: {{y, m, d}, _}}} = File.stat(path, time: :local) - "#{d}/#{m}/#{y}" - end - - def created(path) do - {:ok, %File.Stat{mtime: x}} = File.stat(path, time: :posix) - x - end - - def sanitised_name(path) do - Path.basename(path) - |> String.trim_trailing(".html") - |> String.replace("-", "_") - |> String.replace(" ", "_") - end -end - -{commit, 0} = System.cmd("git", ["rev-parse", "HEAD"]) - -{lightningcss, 0} = System.cmd("lightningcss", ["-V"]) - -{html_minifier_next, 0} = System.cmd("html-minifier-next", ["-V"]) - -{uglifyjs, 0} = System.cmd("uglifyjs", ["-V"]) - -File.rm_rf("build") - -File.mkdir_p!("build") - -bricks = - Path.wildcard("bricks/*.html") - |> Map.new(fn x -> {FileX.sanitised_name(x), File.read!(x)} end) - -{:ok, main_template} = File.read("templates/base.html") - -Path.wildcard("pages/**/*.fxg") -|> Task.async_stream(fn file -> Builders.fxg(file, main_template, bricks) end) -|> Enum.to_list() - -File.mkdir_p!("build/static") - -Path.wildcard("static/**/*") -|> Task.async_stream(fn file -> File.cp(file, "build/" <> file) end) -|> Enum.to_list() - -File.mkdir_p!("build/scripts") - -Path.wildcard("scripts/**/*.js") -|> Task.async_stream(fn file -> Builders.js(file) end) -|> Enum.to_list() - -File.mkdir_p!("build/styles") - -Path.wildcard("styles/**/*.css") -|> Task.async_stream(fn file -> Builders.css(file) end) -|> Enum.to_list() - -File.mkdir_p("build/pastes") - -{:ok, paste_template} = File.read("templates/paste.html") - -pastes = - Path.wildcard("pastes/**/*") - |> Enum.map(fn path -> {path, Git.get_file_commit(path)} end) - |> Enum.sort_by(fn {_, t} -> t end, :desc) - |> Enum.map(fn {path, _} -> path end) - -pastes -|> Task.async_stream(fn file -> Builders.paste(file, paste_template, bricks) end) -|> Enum.to_list() - -paste_data = - Map.new(pastes, fn x -> {x, FileX.createdf(x)} end) - |> Enum.map(fn {x, y} -> Templates.pastelink(x, x, y) end) - |> Enum.join("
") - -pastes_header = """ -

my pastes

-

random code snippets i found useful or wanted to save :))

-
-""" - -# main_template -# |> String.replace("", pastes_header <> "

" <> paste_data <> "

") -paste_index = - Mustache.render( - main_template, - Map.merge(bricks, %{ - fxg_content: pastes_header <> "

" <> paste_data <> "

", - location: "pastes", - ogp: - Templates.ogp( - "thoughts", - "https://m1kadev.nl/pastes", - "random code snippets i found useful or wanted to save :))" - ) - }) - ) - -File.write("build/pastes/index.html", paste_index) - -File.mkdir_p("build/thoughts") - -thoughts_template = File.read!("templates/thoughts.html") -thought_template = File.read!("templates/thought.html") - -thought_paths = - Path.wildcard("thoughts/**/*") - |> Enum.map(fn path -> {path, Git.get_file_commit(path)} end) - |> Enum.sort_by(fn {_, t} -> t end, :desc) - -thoughts = Enum.map(thought_paths, fn {path, date} -> Builders.thought(path, date) end) - -thoughts_html = - Mustache.render( - thoughts_template, - Map.merge(bricks, %{ - fxg_content: Enum.join(thoughts, "
"), - location: "thoughts", - ogp: - Templates.ogp( - "thoughts", - "https://m1kadev.nl/thoughts", - "important thoughts that needed publishing" - ) - }) - ) - -for {thought, {path, time}} <- Enum.zip(thoughts, thought_paths) do - thought = FileX.sanitised_name(path) - output_path = "build/thoughts/" <> thought <> ".html" - - when_said = DateTime.from_unix!(time) |> Calendar.strftime("%H:%M, %d/%m/%y") - thought_txt = File.read!(path) - - thought_html = - Mustache.render( - thought_template, - Map.merge(bricks, %{ - fxg_content: thought_txt, - thought: thought, - date: when_said, - ogp: - Templates.ogp( - thought, - "https://m1kadev.nl/#{String.replace_prefix(output_path, "build/", "")}", - String.replace(thought_txt, "\"", """) - ) - }) - ) - - File.write(output_path, thought_html) -end - -File.write("build/thoughts/index.html", thoughts_html) - -File.copy("favicon.ico", "build/favicon.ico") - -{:ok, date} = DateTime.now("Europe/Amsterdam") -build_time = Calendar.strftime(date, "%H:%M:%S %d/%m/%y") - -File.write( - "build/info.txt", - Templates.build_info( - commit, - lightningcss |> String.split_at(13) |> elem(1), - html_minifier_next, - uglifyjs |> String.split_at(10) |> elem(1), - build_time - ) -) diff --git a/kethel/.formatter.exs b/kethel/.formatter.exs new file mode 100644 index 0000000000000000000000000000000000000000..d2cda26eddc9110057ce9e8002585682217542cc --- /dev/null +++ b/kethel/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/kethel/.gitignore b/kethel/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..634ff5cfc433b8706eb933a41a47e9256c239bf3 --- /dev/null +++ b/kethel/.gitignore @@ -0,0 +1,23 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +kethel-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/kethel/lib/bricks.ex b/kethel/lib/bricks.ex new file mode 100644 index 0000000000000000000000000000000000000000..7e2e5b897f952b40c08e2561e6d07e388622db7c --- /dev/null +++ b/kethel/lib/bricks.ex @@ -0,0 +1,17 @@ +defmodule Bricks do + @typedoc "A path, relative to mix.exs" + @type relative_path() :: String.t() + + @typedoc "The name of the brick" + @type brick_name() :: String.t() + + @typedoc "A valid mustache template" + @type mustache() :: String.t() + + @spec collect(relative_path()) :: %{brick_name() => mustache()} + def collect(folder) do + Path.wildcard(folder <> "/bricks/*.thtml") + |> Enum.map(fn brick -> { brick, File.read!(brick) } end) + |> Enum.map(fn { brick, contents } -> { Path.basename(brick) |> Path.rootname(), contents } end) # "../bricks/base_header.html" would beccome just "base_header" + end + end diff --git a/kethel/main.exs b/kethel/main.exs new file mode 100644 index 0000000000000000000000000000000000000000..037295b0886a2fd31201d8b9bb9793730575ab3e --- /dev/null +++ b/kethel/main.exs @@ -0,0 +1,11 @@ +args = System.argv() + +folder = Enum.at(args, 1) + +if folder != nil do + IO.inspect(folder) + bricks = Bricks.collect(folder) + IO.inspect(bricks) +else + IO.puts(:stderr, "The root project folder should be provided on the command line") +end diff --git a/kethel/mix.exs b/kethel/mix.exs new file mode 100644 index 0000000000000000000000000000000000000000..82ffd4011bf0591db0e702eb82b02156e4d1e4f7 --- /dev/null +++ b/kethel/mix.exs @@ -0,0 +1,20 @@ +defmodule Kethel.MixProject do + use Mix.Project + + def project do + [ + app: :kethel, + version: "0.1.0", + elixir: "~> 1.18", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + defp deps do + [ + { :tzdata, "~> 1.1" }, + { :mustache, "~> 0.5.0" } + ] + end +end diff --git a/kethel/mix.lock b/kethel/mix.lock new file mode 100644 index 0000000000000000000000000000000000000000..9ddcd5613d09cc2d5bd542d33a83762118125347 --- /dev/null +++ b/kethel/mix.lock @@ -0,0 +1,12 @@ +%{ + "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, + "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, + "mustache": {:hex, :mustache, "0.5.1", "1bfee8a68a8e72d47616541a93a632ae1684c1abc17c9eb5f7bfecb78d7496e5", [:mix], [], "hexpm", "524c9bbb6080a52d7b6806436b4e269e0224c785a228faf3293ef30a75016bfa"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, + "tzdata": {:hex, :tzdata, "1.1.3", "b1cef7bb6de1de90d4ddc25d33892b32830f907e7fc2fccd1e7e22778ab7dfbc", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d4ca85575a064d29d4e94253ee95912edfb165938743dbf002acdf0dcecb0c28"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, +}