Skip to content

Instantly share code, notes, and snippets.

@29decibel
Created March 8, 2022 17:51
Show Gist options
  • Save 29decibel/5d8443d5121232bc1033aec53c2fc9cc to your computer and use it in GitHub Desktop.
Save 29decibel/5d8443d5121232bc1033aec53c2fc9cc to your computer and use it in GitHub Desktop.
Use Svelte in Phoenix the simple way
import esbuild from "esbuild";
import esbuildSvelte from "esbuild-svelte";
import sveltePreprocess from "svelte-preprocess";
import { readdir } from "fs/promises";
async function allSvelteComponents() {
const baseDir = "./js/app/";
const all = await readdir(baseDir);
return all.filter((f) => f.endsWith(".svelte")).map((f) => `${baseDir}${f}`);
}
(async function () {
const components = await allSvelteComponents();
const isProd = process.env.NODE_ENV == "production";
esbuild
.build({
entryPoints: components,
format: "esm",
bundle: true,
minify: isProd,
watch: !isProd,
outdir: "../priv/static/assets/svelte",
plugins: [
esbuildSvelte({
preprocess: sveltePreprocess(),
}),
],
})
.catch(() => process.exit(1));
})();
diff --git a/config/dev.exs b/config/dev.exs
index 43604e1..19e7c62 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -40,7 +40,9 @@ config :example, ExampleWeb.Endpoint,
"--postcss",
"--watch",
cd: Path.expand("../assets", __DIR__)
- ]
+ ],
+ # build and watch svelte components
+ node: ["build.mjs", "--watch", cd: Path.expand("../assets", __DIR__)]
]
<%= ExampleWeb.SvelteView.render_component(@conn, "Hello", name: "Mike") %>
diff --git a/mix.exs b/mix.exs
--- a/mix.exs
+++ b/mix.exs
@@ -116,7 +116,9 @@ defmodule Example.MixProject do
"assets.deploy": [
"esbuild default --minify",
- "phx.digest"
+ "phx.digest",
+ # build svelte components
+ "NODE_ENV=production cmd --cd assets node ./build.mjs"
]
]
end
diff --git a/assets/package.json b/assets/package.json
--- a/assets/package.json
+++ b/assets/package.json
@@ -12,11 +12,16 @@
"dependencies": {
+ "esbuild": "^0.14.23",
+ "esbuild-svelte": "^0.6.2",
+ "svelte": "^3.46.4",
+ "svelte-preprocess": "^4.10.4"
}
}
defmodule ExampleWeb.SvelteView do
use ExampleWeb, :view
# just for default values
def render_component(conn, name, props \\ [])
def render_component(conn, name, props) when is_list(props) do
render_component(conn, name, Enum.into(props, %{}))
end
def render_component(conn, name, props) when is_map(props) do
component_id = "svelte-ele-#{name}-#{random_id()}"
component_import_path = RootRoutes.static_path(conn, "/assets/svelte/#{name}.js")
props_json = props |> Jason.encode!()
component_class_name = name |> String.split("-") |> Enum.map_join(&String.capitalize(&1))
[
content_tag(:div, "", id: component_id),
content_tag :script, type: "module" do
"""
import #{component_class_name} from "#{component_import_path}"
new #{component_class_name}({
target: document.getElementById("#{component_id}"),
props: #{props_json}
})
"""
|> raw
end
]
end
defp random_id do
for _ <- 1..10, into: "", do: <<Enum.random('0123456789abcdef')>>
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment