aboutsummaryrefslogtreecommitdiff
path: root/src/lustre_try_ffi.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/lustre_try_ffi.erl')
-rw-r--r--src/lustre_try_ffi.erl115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/lustre_try_ffi.erl b/src/lustre_try_ffi.erl
new file mode 100644
index 0000000..0209c14
--- /dev/null
+++ b/src/lustre_try_ffi.erl
@@ -0,0 +1,115 @@
+-module(lustre_try_ffi).
+-export([serve/3, response_default_headers/0, exec/1]).
+
+serve({options, Name, Host, Port, NoStyles}, OnStart, OnPortTaken) ->
+ Html =
+ <<
+ "<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ " <meta charset=\"UTF-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
+ " <title>Lustre preview server</title>\n",
+ case NoStyles of
+ true ->
+ <<"">>;
+ false ->
+ <<" <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/lustre-labs/ui/priv/styles.css\">\n">>
+ end/binary,
+ " <script type=\"module\">\n"
+ " import { main } from './",
+ Name/binary,
+ "/",
+ Name/binary,
+ ".mjs'\n"
+ "\n"
+ " document.addEventListener(\"DOMContentLoaded\", () => {\n"
+ " main();\n"
+ " });\n"
+ " </script>\n"
+ "</head>\n"
+ "<body>\n"
+ " <div id=\"app\"></div>\n"
+ "</body>\n"
+ "</html>"
+ >>,
+
+ file:write_file("build/dev/javascript/index.html", Html),
+
+ AbsPath =
+ string:trim(
+ filename:absname("build/dev/javascript"), trailing, "/."
+ ),
+
+ inets:start(),
+ Address = {127, 0, 0, 1},
+
+ ActualPort =
+ case port_available(Port) of
+ true ->
+ Port;
+ false ->
+ OnPortTaken(Port),
+ first_available_port(Port + 1)
+ end,
+
+ {ok, Pid} =
+ httpd:start_service([
+ {bind_address, Address},
+ {document_root, AbsPath},
+ {server_root, AbsPath},
+ {directory_index, ["index.html"]},
+ {server_name, binary_to_list(Host)},
+ {port, ActualPort},
+ {default_type, "text/html"},
+ {mime_types, mime_types()},
+ {customize, ?MODULE},
+ {modules, [mod_alias, mod_dir, mod_get]}
+ ]),
+
+ OnStart(ActualPort),
+
+ receive
+ {From, shutdown} ->
+ ok = httpd:stop_service(Pid),
+ From ! done
+ end.
+
+port_available(Port) ->
+ case gen_tcp:listen(Port, []) of
+ {ok, Sock} ->
+ ok = gen_tcp:close(Sock),
+ true;
+ _ ->
+ false
+ end.
+
+first_available_port(Port) ->
+ case port_available(Port) of
+ true -> Port;
+ false -> first_available_port(Port + 1)
+ end.
+
+mime_types() ->
+ [
+ {"html", "text/html"},
+ {"htm", "text/html"},
+ {"js", "text/javascript"},
+ {"mjs", "text/javascript"},
+ {"css", "text/css"},
+ {"gif", "image/gif"},
+ {"jpg", "image/jpeg"},
+ {"jpeg", "image/jpeg"},
+ {"png", "image/png"}
+ ].
+
+response_default_headers() ->
+ [
+ {"cache-control", "no-store, no-cache, must-revalidate, private"},
+ {"pragma", "no-cache"}
+ ].
+
+exec(Cmd) ->
+ Stdout = os:cmd(unicode:characters_to_list(Cmd)),
+
+ unicode:characters_to_binary(Stdout).