From dd8c2c81d9345ffcbdcb6ebdb432afce2ecd3b7f Mon Sep 17 00:00:00 2001 From: Nighty Date: Tue, 30 Apr 2024 19:13:46 +0200 Subject: [PATCH] Vite HMR for JS/TS (and jankily for CSS) (#242) * prelim work on vite reload * the best solution to a problem is usually the easiest one --- assets/js/app.js | 9 +++++++++ config/dev.exs | 10 ++++++++++ config/runtime.exs | 6 ++++++ docker-compose.yml | 2 ++ docker/app/Dockerfile | 3 ++- .../plugs/content_security_policy_plug.ex | 13 +++++++++++-- lib/philomena_web/templates/layout/app.html.slime | 9 +++++++-- lib/philomena_web/views/layout_view.ex | 4 ++++ 8 files changed, 51 insertions(+), 5 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index a64ec696..4f23d655 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -8,3 +8,12 @@ // Our code import './ujs'; import './when-ready'; + +// When developing CSS, include the relevant CSS you're working on here +// in order to enable HMR (live reload) on it. +// Would typically be either the theme file, or any additional file +// you later intend to put in the tag. + +// import '../css/themes/default.scss'; +// import '../css/themes/dark.scss'; +// import '../css/themes/red.scss'; diff --git a/config/dev.exs b/config/dev.exs index 90d6d4db..781d0ee2 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -15,6 +15,16 @@ config :philomena, PhilomenaWeb.Endpoint, code_reloader: true, check_origin: false, watchers: [ + node: [ + "node_modules/vite/bin/vite.js", + "--mode", + "development", + "--host", + "0.0.0.0", + "--config", + "vite.config.ts", + cd: Path.expand("../assets", __DIR__) + ], node: [ "node_modules/vite/bin/vite.js", "build", diff --git a/config/runtime.exs b/config/runtime.exs index 3f911fa5..b7a71c3e 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -137,6 +137,9 @@ if config_env() == :prod do # Do not relax CSP in production config :philomena, csp_relaxed: false + + # Disable Vite HMR in prod + config :philomena, vite_reload: false else # Don't send email in development config :philomena, Philomena.Mailer, adapter: Bamboo.LocalAdapter @@ -146,4 +149,7 @@ else # Relax CSP rules in development and test servers config :philomena, csp_relaxed: true + + # Enable Vite HMR + config :philomena, vite_reload: true end diff --git a/docker-compose.yml b/docker-compose.yml index 84d25060..8e644165 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,6 +48,8 @@ services: - postgres - elasticsearch - redis + ports: + - '5173:5173' postgres: image: postgres:16.2-alpine diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile index 7f620d52..fb76abbc 100644 --- a/docker/app/Dockerfile +++ b/docker/app/Dockerfile @@ -1,4 +1,4 @@ -FROM elixir:1.16.1-alpine +FROM elixir:1.16.2-alpine ADD https://api.github.com/repos/philomena-dev/FFmpeg/git/refs/heads/release/6.1 /tmp/ffmpeg_version.json RUN (echo "https://github.com/philomena-dev/prebuilt-ffmpeg/raw/master"; cat /etc/apk/repositories) > /tmp/repositories \ @@ -24,4 +24,5 @@ COPY docker/app/run-test /usr/local/bin/run-test COPY docker/app/safe-rsvg-convert /usr/local/bin/safe-rsvg-convert COPY docker/app/purge-cache /usr/local/bin/purge-cache ENV PATH=$PATH:/root/.cargo/bin +EXPOSE 5173 CMD run-development diff --git a/lib/philomena_web/plugs/content_security_policy_plug.ex b/lib/philomena_web/plugs/content_security_policy_plug.ex index 58a18ac2..d958541e 100644 --- a/lib/philomena_web/plugs/content_security_policy_plug.ex +++ b/lib/philomena_web/plugs/content_security_policy_plug.ex @@ -24,8 +24,9 @@ defmodule PhilomenaWeb.ContentSecurityPolicyPlug do csp_config = [ {:default_src, ["'self'"]}, - {:script_src, ["'self'" | script_src]}, - {:style_src, ["'self'" | style_src]}, + {:script_src, [default_script_src() | script_src]}, + {:connect_src, [default_connect_src()]}, + {:style_src, [default_style_src() | style_src]}, {:object_src, ["'none'"]}, {:frame_ancestors, ["'none'"]}, {:frame_src, frame_src || ["'none'"]}, @@ -63,6 +64,14 @@ defmodule PhilomenaWeb.ContentSecurityPolicyPlug do defp cdn_uri, do: Application.get_env(:philomena, :cdn_host) |> to_uri() defp camo_uri, do: Application.get_env(:philomena, :camo_host) |> to_uri() + defp vite_reload?, do: Application.get_env(:philomena, :vite_reload) + + defp default_script_src, do: if(vite_reload?(), do: "'self' localhost:5173", else: "'self'") + + defp default_connect_src, + do: if(vite_reload?(), do: "'self' localhost:5173 ws://localhost:5173", else: "'self'") + + defp default_style_src, do: if(vite_reload?(), do: "'self' 'unsafe-inline'", else: "'self'") defp to_uri(host) when host in [nil, ""], do: "" defp to_uri(host), do: URI.to_string(%URI{scheme: "https", host: host}) diff --git a/lib/philomena_web/templates/layout/app.html.slime b/lib/philomena_web/templates/layout/app.html.slime index be918a5e..16a43d7a 100644 --- a/lib/philomena_web/templates/layout/app.html.slime +++ b/lib/philomena_web/templates/layout/app.html.slime @@ -19,9 +19,14 @@ html lang="en" meta name="theme-color" content="#618fc3" meta name="format-detection" content="telephone=no" = csrf_meta_tag() - script type="module" src=Routes.static_path(@conn, "/js/app.js") async="async" + + = if vite_reload?() do + script type="module" src="http://localhost:5173/@vite/client" + script type="module" src="http://localhost:5173/js/app.js" + - else + script type="text/javascript" src=Routes.static_path(@conn, "/js/app.js") async="async" = render PhilomenaWeb.LayoutView, "_opengraph.html", assigns - body data-theme=theme_name(@current_user) + body data-theme=theme_name(@current_user) data-vite-reload=to_string(vite_reload?()) = render PhilomenaWeb.LayoutView, "_burger.html", assigns #container class=container_class(@current_user) = render PhilomenaWeb.LayoutView, "_header.html", assigns diff --git a/lib/philomena_web/views/layout_view.ex b/lib/philomena_web/views/layout_view.ex index 261bb571..3ea772a7 100644 --- a/lib/philomena_web/views/layout_view.ex +++ b/lib/philomena_web/views/layout_view.ex @@ -22,6 +22,10 @@ defmodule PhilomenaWeb.LayoutView do Application.get_env(:philomena, :cdn_host) end + def vite_reload? do + Application.get_env(:philomena, :vite_reload) + end + defp ignored_tag_list(nil), do: [] defp ignored_tag_list([]), do: [] defp ignored_tag_list([{tag, _body, _dnp_entries}]), do: [tag.id]