Packages
fermo
0.16.4
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
require Logger
import Plug.Conn
import Mix.Fermo.Paths, only: [app_path: 0]
live_reload_js_path = Application.app_dir(:fermo, "priv/static/fermo-live.js.eex")
@external_resource live_reload_js_path
@live_reload_js EEx.eval_string("""
<script type="text/javascript">
#{File.read!(live_reload_js_path)}
</script>
""", env: System.get_env())
def init(_options) do
[]
end
def call(conn, _state) do
request_path(conn) |> 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 "[Fermo.Live.Server] 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)
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
Path.join(app_path(), "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)}
true ->
{:error, :unexpected_extname_result}
end
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