Packages
fermo
0.20.1
0.20.1
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.1
0.17.0
0.16.7
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.0
0.14.9
0.14.8
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.4
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A static site generator
Current section
Files
Jump to
Current section
Files
lib/fermo/live/server.ex
defmodule Fermo.Live.Server do
import Plug.Conn
import Mix.Fermo.Paths, only: [app_path: 0]
alias Fermo.Live.Dependencies
require Logger
live_reload_js_path = Application.app_dir(:fermo, "priv/static/fermo-live.js")
@external_resource live_reload_js_path
@live_reload_js """
#{File.read!(live_reload_js_path)}
#{Application.compile_env(:fermo, :live_reload_js, "")}
"""
def init(_options) do
[]
end
def call(conn, _state) do
conn |> request_path() |> handle_request_path(conn)
end
defp handle_request_path({:error, :request_path_missing}, conn) do
respond_403(conn)
end
defp handle_request_path({:ok, request_path}, conn) do
Logger.debug("GET #{request_path}")
if is_static?(request_path) do
serve_static(request_path, conn)
else
case find_page(request_path) do
{:ok, page} ->
Logger.debug("[Fermo.Live.Server] Serving page #{request_path}")
serve_page(page, conn)
_ ->
Logger.debug("[Fermo.Live.Server] Page #{request_path} not found")
respond_404(conn)
end
end
end
defp is_static?(path) do
build_path = build_path(path)
File.regular?(build_path)
end
defp serve_static(path, conn) do
build_path = build_path(path)
{:ok, extension} = extension(build_path)
mime_type = mime_type(extension)
respond_with_file(conn, build_path, mime_type)
end
defp find_page(request_path) do
Fermo.Live.Dependencies.page_from_path(request_path)
end
defp serve_page(page, conn) do
{:ok} = Fermo.Live.Dependencies.start_page(page.path)
if page.params.layout do
{:ok} = Fermo.Live.Dependencies.add_page_dependency(page.path, page.params.layout)
end
html = live_page(page)
respond_with_html(conn, html)
end
defp live_page(page) do
html = Fermo.Build.render_page(page)
inject_reload(html)
end
defp inject_reload(html) do
if has_body_close?(html) do
[body | tail] = String.split(html, "</body>")
Enum.join([body, live_reload_js() | tail], "\n")
else
Enum.join([html, live_reload_js()], "\n")
end
end
defp has_body_close?(html) do
String.contains?(html, "</body>")
end
defp respond_403(conn) do
conn
|> put_resp_content_type("text/plain")
|> send_resp(403, "Forbidden")
end
defp respond_404(conn) do
conn
|> put_resp_content_type("text/plain")
|> send_resp(404, "Not found")
end
defp respond_with_file(conn, full_path, mime_type) do
conn
|> put_resp_content_type(mime_type)
|> send_resp(200, File.read!(full_path))
end
defp respond_with_html(conn, html) do
conn
|> put_resp_content_type("text/html")
|> send_resp(200, html)
end
defp request_path(conn) do
case conn.request_path do
nil ->
{:error, :request_path_missing}
_ ->
expanded = Path.expand(conn.request_path)
if expanded == "/" do
{:ok, "/"}
else
{:ok, URI.decode(expanded) <> "/"}
end
end
end
defp build_root do
app_path()
|> Path.join("build")
|> Path.expand()
end
defp build_path(path) do
Path.join(build_root(), path)
end
defp extension(path) do
maybe_with_dot = Path.extname(path)
cond do
maybe_with_dot == "" ->
{:ok, ""}
maybe_with_dot == "." ->
# We'll treat files with a final dot as HTML
{:ok, ""}
String.starts_with?(maybe_with_dot, ".") ->
{:ok, String.slice(maybe_with_dot, 1..-1//1)}
true ->
{:error, :unexpected_extname_result}
end
end
defp live_reload_js() do
{:ok, generation} = Dependencies.generation()
"""
<script type="text/javascript">
window.fermoDependenciesGeneration = '#{generation}'
#{@live_reload_js}
</script>
"""
end
defp mime_type(extension) do
case extension do
"js" -> "application/javascript"
"css" -> "text/css"
"html" -> "text/html"
"ico" -> "image/vnd.microsoft.icon"
"jpg" -> "image/jpeg"
"jpeg" -> "image/jpeg"
"pdf" -> "application/pdf"
"png" -> "image/png"
"txt" -> "text/plain"
"xml" -> "application/xml"
_ -> "text/html"
end
end
end