chore: added igniter installer

This commit is contained in:
Denis Donici 2026-03-04 09:30:26 +02:00 committed by Wout De Puysseleir
parent 19a8898fce
commit e57471bef8
12 changed files with 529 additions and 321 deletions

View file

@ -1,71 +0,0 @@
const esbuild = require("esbuild")
const sveltePlugin = require("esbuild-svelte")
const importGlobPlugin = require("esbuild-plugin-import-glob").default
const sveltePreprocess = require("svelte-preprocess")
const args = process.argv.slice(2)
const watch = args.includes("--watch")
const deploy = args.includes("--deploy")
let clientConditions = ["svelte", "browser"]
let serverConditions = ["svelte"]
if (!deploy) {
clientConditions.push("development")
serverConditions.push("development")
}
let optsClient = {
entryPoints: ["js/app.js"],
bundle: true,
minify: deploy,
conditions: clientConditions,
alias: {svelte: "svelte"},
outdir: "../priv/static/assets/js",
logLevel: "info",
sourcemap: watch ? "inline" : false,
tsconfig: "./tsconfig.json",
plugins: [
importGlobPlugin(),
sveltePlugin({
preprocess: sveltePreprocess(),
compilerOptions: {dev: !deploy, css: "injected", generate: "client"},
}),
],
}
let optsServer = {
entryPoints: ["js/server.js"],
platform: "node",
bundle: true,
minify: false,
target: "node19.6.1",
conditions: serverConditions,
alias: {svelte: "svelte"},
outdir: "../priv/svelte",
logLevel: "info",
sourcemap: watch ? "inline" : false,
tsconfig: "./tsconfig.json",
plugins: [
importGlobPlugin(),
sveltePlugin({
preprocess: sveltePreprocess(),
compilerOptions: {dev: !deploy, css: "injected", generate: "server"},
}),
],
}
if (watch) {
esbuild
.context(optsClient)
.then(ctx => ctx.watch())
.catch(_error => process.exit(1))
esbuild
.context(optsServer)
.then(ctx => ctx.watch())
.catch(_error => process.exit(1))
} else {
esbuild.build(optsClient)
esbuild.build(optsServer)
}

View file

@ -1,42 +0,0 @@
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
// to get started and then uncomment the line below.
// import "./user_socket.js"
// You can include dependencies in two ways.
//
// The simplest option is to put them in assets/vendor and
// import them using relative paths:
//
// import "../vendor/some-package.js"
//
// Alternatively, you can `npm install some-package --prefix assets` and import
// them using a path starting with the package name:
//
// import "some-package"
//
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import {getHooks} from "live_svelte"
import * as Components from "../svelte/**/*.svelte"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {hooks: getHooks(Components), params: {_csrf_token: csrfToken}})
// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
// connect if there are any LiveViews on the page
liveSocket.connect()
// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket

View file

@ -1,4 +0,0 @@
import * as Components from "../svelte/**/*.svelte"
import {getRender} from "live_svelte"
export const render = getRender(Components)

View file

@ -38,6 +38,7 @@
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.2.0", "ff3a5616e1bed6804de7773b92cbccfc0b0f473faf1f63d7daf1206c7aeaaa6f", [:mix], [], "hexpm", "adc313a5bf7136039f63cfd9668fde73bba0765e0614cba80c06ac9460ff3e96"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_test": {:hex, :phoenix_test, "0.9.1", "ac58a4d341c594ac57ce52a6ce643200084fad419a91b72896a44881fe84809c", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:lazy_html, "~> 0.1.7", [hex: :lazy_html, repo: "hexpm", optional: false]}, {:mime, ">= 1.0.0", [hex: :mime, repo: "hexpm", optional: true]}, {:phoenix, ">= 1.7.10", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "ed453394c0f8987aa58a06e2302e7dd4bc53cd2d25eff5a18c4a5775241ebe61"},
"phoenix_vite": {:hex, :phoenix_vite, "0.4.1", "16fc8b4acc7d4e26fab2bcfb10d125392fcfb5a4ba05dd3aea7f2bec048c5e71", [:mix], [{:bun, ">= 1.5.1 and < 2.0.0-0", [hex: :bun, repo: "hexpm", optional: true]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "f2bdb1802bc82f2fa93b24606cd25ebdd389bcf2afc55af804f0af3377410f0d"},
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},

View file

@ -1,15 +0,0 @@
defmodule LiveSvelte.Logger do
@moduledoc false
@doc false
def log_info(status), do: Mix.shell().info([status, :reset])
@doc false
def log_success(status), do: Mix.shell().info([:green, status, :reset])
@doc false
def log_warning(status), do: Mix.shell().info([:yellow, status, :reset])
@doc false
def log_error(status), do: Mix.shell().error([status, :reset])
end

View file

@ -1,23 +0,0 @@
defmodule Mix.Tasks.LiveSvelte.ConfigureEsbuild do
@moduledoc """
Creates Javascript files to be used by esbuild. Necessary for LiveSvelte to work.
"""
import LiveSvelte.Logger
def run(_) do
log_info("-- Configuring esbuild...")
Mix.Project.deps_paths(depth: 1)
|> Map.fetch!(:live_svelte)
|> Path.join("assets/copy/**/*.{js,json}")
|> Path.wildcard()
|> Enum.each(fn full_path ->
[_beginning, relative_path] = String.split(full_path, "copy", parts: 2)
Mix.Generator.copy_file(full_path, "assets" <> relative_path)
end)
Mix.Generator.create_directory("assets/svelte")
end
end

View file

@ -1,116 +0,0 @@
defmodule Mix.Tasks.LiveSvelte.ConfigurePhoenix do
@moduledoc """
Configures any necessary code changes inside Phoenix to make LiveSvelte work.
"""
import LiveSvelte.Logger
@watcher_regex ~r/watchers:\s\[(?!\s+node:)/
@esbuild_regex ~r/(?<!# )esbuild: {.*}/
@nodejs_regex ~r/children\s+=\s+\[(?!\s+\{NodeJS)/
@gitignore_regex ~r/\Z/
def run(_) do
log_info("-- Configuring Phoenix...")
try do
configure_dev_config()
configure_application()
configure_gitignore()
rescue
err ->
err
|> Exception.message()
|> log_error()
end
Mix.Task.run("format")
end
defp configure_dev_config() do
text = ~s"""
node: ["build.js", "--watch", cd: Path.expand("../assets", __DIR__)],\
"""
{path, file} = path_and_file("config/", "dev.exs")
File.read!(path)
|> insert(@watcher_regex, text, "'#{text}' in #{file}")
|> comment(@esbuild_regex, "old esbuild watcher in #{file}")
|> save(path)
end
defp configure_application() do
text = ~s"""
{NodeJS.Supervisor, [path: LiveSvelte.SSR.NodeJS.server_path(), pool_size: 4]},\
"""
{path, file} = path_and_file("lib/**/", "application.ex")
File.read!(path)
|> insert(@nodejs_regex, text, "'#{text}' in #{file}")
|> save(path)
end
defp configure_gitignore() do
text = ~s"""
# Ignore automatically generated Svelte files by the ~V sigil
/assets/svelte/_build/
# Ignore ssr build for svelte.
/priv/svelte/\
"""
{path, file} = path_and_file("", ".gitignore")
File.read!(path)
|> insert(@gitignore_regex, text, "'#{text}' in #{file}")
|> save(path)
end
defp path_and_file(wildcard, filename) do
{find_file("#{wildcard}#{filename}", filename), filename}
end
defp find_file(wildcard, file_name) do
with [path] <- Path.wildcard(wildcard) do
path
else
[] -> raise "Could not find #{file_name}"
[_ | _] -> raise "Found multiple #{file_name} files"
end
end
defp insert(source, regex, to_insert, name) do
case Regex.run(regex, source, return: :index) do
[{pos, len}] ->
log_success("Inserted #{name}")
insert_position(source, pos + len, to_insert)
nil ->
log_error("Could not insert #{name}, please do so yourself")
source
end
end
defp comment(source, regex, name) do
case Regex.run(regex, source, return: :index) do
[{pos, _len}] ->
log_success("Commented out #{name}")
insert_position(source, pos, "# ")
nil ->
log_warning("Could not comment out #{name}")
source
end
end
defp insert_position(source, position, to_insert) do
{head, tail} = String.split_at(source, position)
head <> to_insert <> tail
end
defp save(source, target_file), do: File.write!(target_file, source)
end

View file

@ -1,17 +0,0 @@
defmodule Mix.Tasks.LiveSvelte.InstallNpmDeps do
@moduledoc """
Installs npm dependencies for LiveSvelte.
"""
import LiveSvelte.Logger
def run(_) do
log_info("-- Installing npm dependencies...")
"npm install --prefix ./assets --save-dev esbuild esbuild-svelte svelte svelte-preprocess esbuild-plugin-import-glob &&
npm install --prefix ./assets --save ./deps/phoenix ./deps/phoenix_html ./deps/phoenix_live_view ./deps/live_svelte"
|> String.to_charlist()
|> :os.cmd()
|> IO.puts()
end
end

View file

@ -0,0 +1,497 @@
defmodule Mix.Tasks.LiveSvelte.Install do
@moduledoc """
Installer for LiveSvelte with Vite.
This task first installs Vite using the PhoenixVite installer,
then configures the project for LiveSvelte.
## Options
* `--bun` - Use Bun instead of Node.js/npm
## Examples
mix igniter.install live_svelte
mix igniter.install live_svelte --bun
"""
import Mix.Tasks.PhoenixVite.Install.Helper
with_igniter do
use Igniter.Mix.Task
alias Igniter.Libs.Phoenix
alias Igniter.Project.Config
@impl Igniter.Mix.Task
def info(_argv, _parent) do
%Igniter.Mix.Task.Info{
composes: ["phoenix_vite.install"],
schema: [bun: :boolean],
aliases: [b: :bun]
}
end
@impl Igniter.Mix.Task
def igniter(igniter) do
app_name = Igniter.Project.Application.app_name(igniter)
igniter
|> Igniter.compose_task("phoenix_vite.install", igniter.args.argv)
|> configure_environments(app_name)
|> add_live_svelte_to_html_helpers(app_name)
|> update_javascript_configuration()
|> configure_tailwind_for_svelte()
|> update_vite_configuration()
|> update_package_json_for_svelte()
|> create_svelte_files()
|> setup_ssr_for_production(app_name)
|> update_mix_aliases()
|> add_svelte_demo_route()
|> update_home_template()
|> update_gitignore()
|> create_ssr_vite_config()
end
# Configure environments (config.exs, dev.exs, prod.exs)
defp configure_environments(igniter, _app_name) do
igniter
|> Config.configure("config.exs", :live_svelte, [:ssr], true)
|> Config.configure("dev.exs", :live_svelte, [:ssr_module], {:code, Sourceror.parse_string!("LiveSvelte.SSR.ViteJS")})
|> Config.configure("dev.exs", :live_svelte, [:vite_host], "http://localhost:5173")
|> Config.configure("prod.exs", :live_svelte, [:ssr_module], {:code, Sourceror.parse_string!("LiveSvelte.SSR.NodeJS")})
|> Config.configure("prod.exs", :live_svelte, [:ssr], true)
end
# Add import LiveSvelte to html_helpers in lib/app_web.ex
defp add_live_svelte_to_html_helpers(igniter, _app_name) do
web_module = Phoenix.web_module(igniter)
web_folder = Macro.underscore(web_module)
web_file = Path.join(["lib", web_folder <> ".ex"])
Igniter.update_file(igniter, web_file, fn source ->
Rewrite.Source.update(source, :content, fn content ->
if String.contains?(content, "import LiveSvelte") do
content
else
# Add after the use Gettext or import ...Gettext line in html_helpers
web_module_name = web_module |> Module.split() |> Enum.join(".")
result =
String.replace(
content,
~r/(use Gettext, backend: #{Regex.escape(web_module_name)}\.Gettext)/,
"\\1\n\n import LiveSvelte"
)
# Fallback: try matching import ...Gettext pattern
if result == content do
String.replace(
content,
~r/(import #{Regex.escape(web_module_name)}\.Gettext)/,
"\\1\n\n import LiveSvelte"
)
else
result
end
end
end)
end)
end
# Update app.js to import getHooks and Components from live_svelte
defp update_javascript_configuration(igniter) do
Igniter.update_file(igniter, "assets/js/app.js", fn source ->
Rewrite.Source.update(source, :content, fn content ->
content
|> add_live_svelte_imports()
|> update_live_socket_hooks()
end)
end)
end
defp add_live_svelte_imports(content) do
if String.contains?(content, "import {getHooks} from \"live_svelte\"") do
content
else
String.replace(
content,
"import topbar from \"topbar\"",
~s(import topbar from "topbar"\nimport {getHooks} from "live_svelte"\nimport Components from "virtual:live-svelte-components")
)
end
end
defp update_live_socket_hooks(content) do
String.replace(
content,
"hooks: {...colocatedHooks},",
"hooks: {...colocatedHooks, ...getHooks(Components)},"
)
end
# Configure Tailwind to include Svelte files (add @source "../svelte"; to app.css)
defp configure_tailwind_for_svelte(igniter) do
if Igniter.exists?(igniter, "assets/css/app.css") do
Igniter.update_file(igniter, "assets/css/app.css", fn source ->
Rewrite.Source.update(source, :content, fn content ->
if String.contains?(content, "@source \"../svelte\";") do
content
else
String.replace(
content,
"@source \"../js\";",
~s(@source "../js";\n@source "../svelte";)
)
end
end)
end)
else
igniter
end
end
# Update vite.config.mjs to add Svelte plugin and liveSveltePlugin
defp update_vite_configuration(igniter) do
Igniter.update_file(igniter, "assets/vite.config.mjs", fn source ->
Rewrite.Source.update(source, :content, fn content ->
content
|> add_svelte_vite_imports()
|> update_vite_optimized_deps()
|> update_vite_plugins()
|> add_ssr_config()
end)
end)
end
defp add_svelte_vite_imports(content) do
if String.contains?(content, "import { svelte }") do
content
else
String.replace(
content,
"import { phoenixVitePlugin } from 'phoenix_vite'",
~s(import { svelte } from "@sveltejs/vite-plugin-svelte"\nimport liveSveltePlugin from "live_svelte/vitePlugin")
)
end
end
defp update_vite_optimized_deps(content) do
if String.contains?(content, "\"live_svelte\"") do
content
else
String.replace(
content,
~s(include: ["phoenix", "phoenix_html", "phoenix_live_view"],),
~s(include: ["live_svelte", "phoenix", "phoenix_html", "phoenix_live_view"],)
)
end
end
defp update_vite_plugins(content) do
if String.contains?(content, "svelte(") do
content
else
String.replace(
content,
~r/phoenixVitePlugin\(\{\s*pattern: \/\\.\(ex\|heex\)\$\/\s*\}\)/s,
"svelte({ compilerOptions: { css: \"injected\" } }),\n liveSveltePlugin({ entrypoint: \"./js/server.js\" })"
)
end
end
defp add_ssr_config(content) do
if String.contains?(content, "noExternal") do
content
else
String.replace(
content,
~r/build: \{/s,
"ssr: { noExternal: process.env.NODE_ENV === \"production\" ? true : undefined },\n build: {"
)
end
end
# Update package.json with Svelte dependencies
defp update_package_json_for_svelte(igniter) do
igniter
|> Igniter.move_file("assets/package.json", "package.json")
|> Igniter.update_file("package.json", fn source ->
Rewrite.Source.update(source, :content, fn content ->
content
|> add_svelte_dependency()
|> add_svelte_dev_dependencies()
end)
end)
end
defp add_svelte_dependency(content) do
if String.contains?(content, "\"live_svelte\"") do
content
else
# Add live_svelte to dependencies section
String.replace(
content,
~s("phoenix": "file:./deps/phoenix"),
~s("live_svelte": "file:./deps/live_svelte",\n "phoenix": "file:./deps/phoenix")
)
end
end
defp add_svelte_dev_dependencies(content) do
if String.contains?(content, "\"@sveltejs/vite-plugin-svelte\"") do
content
else
String.replace(
content,
~s("typescript":),
~s("@sveltejs/vite-plugin-svelte": "^5.0.0",\n "svelte": "^5.0.0",\n "typescript":)
)
end
end
# Create Svelte project files
defp create_svelte_files(igniter) do
web_module = Phoenix.web_module(igniter)
web_folder = Macro.underscore(web_module)
igniter
|> Igniter.mkdir("assets/svelte")
|> Igniter.mkdir("lib/#{web_folder}/live")
|> Igniter.create_new_file("assets/js/server.js", server_js_content())
|> Igniter.create_new_file(
"assets/svelte/.gitignore",
"# Ignore auto-generated Svelte files by ~V sigil\n_build/"
)
|> Igniter.create_new_file("assets/svelte/SvelteDemo.svelte", demo_svelte_content())
|> Igniter.create_new_file(
"lib/#{web_folder}/live/svelte_demo_live.ex",
demo_live_view_content(igniter)
)
end
# Setup NodeJS SSR supervisor in application.ex
defp setup_ssr_for_production(igniter, _app_name) do
app_module = igniter |> Igniter.Project.Application.app_name() |> to_string()
app_file = "lib/#{Macro.underscore(app_module)}/application.ex"
Igniter.update_file(igniter, app_file, fn source ->
Rewrite.Source.update(source, :content, fn content ->
if String.contains?(content, "children = [") and not String.contains?(content, "NodeJS.Supervisor") do
String.replace(
content,
~r/(children = \[\s*\n)/,
"\\1 {NodeJS.Supervisor, [path: LiveSvelte.SSR.NodeJS.server_path(), pool_size: 4]},\n"
)
else
content
end
end)
end)
end
# Update mix.exs aliases in the consumer app to use Vite
defp update_mix_aliases(igniter) do
bun? = igniter.args.options[:bun] || false
pm = if bun?, do: "bunx", else: "npx"
Igniter.update_file(igniter, "mix.exs", fn source ->
Rewrite.Source.update(source, :content, fn content ->
if String.contains?(content, "vite build") do
content
else
content
|> add_assets_js_alias(pm)
|> update_assets_deploy_alias(pm)
end
end)
end)
end
defp add_assets_js_alias(content, pm) do
if String.contains?(content, "\"assets.js\"") do
content
else
String.replace(
content,
~r/("assets\.setup":)/,
~s("assets.js": [\n "cmd --cd assets #{pm} vite build",\n "cmd --cd assets #{pm} vite build --config vite.ssr.config.js",\n "tailwind default"\n ],\n \\1)
)
end
end
defp update_assets_deploy_alias(content, pm) do
# Replace esbuild references in assets.deploy with Vite commands
String.replace(
content,
~r/"esbuild default[^"]*"/,
~s("cmd --cd assets #{pm} vite build --mode production", "cmd --cd assets #{pm} vite build --config vite.ssr.config.js --mode production")
)
end
# Add svelte_demo route to router
defp add_svelte_demo_route(igniter) do
web_module = Phoenix.web_module(igniter)
web_folder = Macro.underscore(web_module)
web_module_name = web_module |> Module.split() |> Enum.join(".")
router_file = Path.join(["lib", web_folder, "router.ex"])
Igniter.update_file(igniter, router_file, fn source ->
Rewrite.Source.update(source, :content, fn content ->
if String.contains?(content, "live \"/svelte_demo\"") do
content
else
if String.contains?(content, "live_dashboard") do
String.replace(
content,
~r/(live_dashboard.*)/,
"\\1\n live \"/svelte_demo\", #{web_module_name}.SvelteDemoLive"
)
else
String.replace(
content,
~r/(pipe_through :browser.*)/,
"\\1\n live \"/dev/svelte_demo\", #{web_module_name}.SvelteDemoLive"
)
end
end
end)
end)
end
# Update home template with LiveSvelte info
defp update_home_template(igniter) do
web_module = Phoenix.web_module(igniter)
web_folder = Macro.underscore(web_module)
home_template = Path.join(["lib", web_folder, "controllers", "page_html", "home.html.heex"])
Igniter.update_file(igniter, home_template, fn source ->
Rewrite.Source.update(source, :content, fn content ->
content
|> String.replace(
"Peace of mind from prototype to production.",
"End-to-end reactivity for your Live Svelte apps."
)
|> String.replace(
~s(<div class="flex">),
~s(<a href={~p"/svelte_demo"} class="btn btn-primary mt-4">Svelte Demo</a>\n <div class="flex">)
)
end)
end)
end
# Add gitignore entries for Svelte build artifacts
defp update_gitignore(igniter) do
Igniter.update_file(igniter, ".gitignore", fn source ->
Rewrite.Source.update(source, :content, fn content ->
content
|> add_gitignore_entry("/assets/svelte/_build/")
|> add_gitignore_entry("/priv/svelte/")
end)
end)
end
defp add_gitignore_entry(content, entry) do
if String.contains?(content, entry) do
content
else
content <> "\n#{entry}"
end
end
# Create the SSR Vite config file
defp create_ssr_vite_config(igniter) do
Igniter.create_new_file(igniter, "assets/vite.ssr.config.js", ssr_vite_config_content())
end
# Content helpers
defp server_js_content do
"""
import { getRender } from "live_svelte"
import Components from "virtual:live-svelte-components"
export const render = getRender(Components)
"""
end
defp ssr_vite_config_content do
"""
import { defineConfig } from "vite"
import { svelte } from "@sveltejs/vite-plugin-svelte"
export default defineConfig({
plugins: [svelte()],
ssr: { noExternal: true },
build: {
ssr: "./js/server.js",
outDir: "../priv/svelte",
rollupOptions: {
output: { entryFileNames: "server.js", format: "es" }
}
}
})
"""
end
defp demo_svelte_content do
"""
<script>
let { count, socket } = $props();
</script>
<div>
<h1>LiveSvelte Demo</h1>
<p>Count: {count}</p>
<button phx-click="increment">+</button>
<button phx-click="decrement">-</button>
</div>
"""
end
defp demo_live_view_content(igniter) do
web_module_name = Phoenix.web_module(igniter) |> Module.split() |> Enum.join(".")
"""
defmodule #{web_module_name}.SvelteDemoLive do
use #{web_module_name}, :live_view
@impl true
def render(assigns) do
~H\"\"\"
<.svelte name="SvelteDemo" props={%{count: @count}} socket={@socket} />
\"\"\"
end
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0)}
end
@impl true
def handle_event("increment", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
@impl true
def handle_event("decrement", _params, socket) do
{:noreply, update(socket, :count, &(&1 - 1))}
end
end
"""
end
else
use Mix.Task
@impl Mix.Task
def run(_argv) do
Mix.shell().error("""
The task 'live_svelte.install' requires igniter. Please install igniter and try again.
For more information, see: https://hexdocs.pm/igniter/readme.html#installation
""")
exit({:shutdown, 1})
end
end
end

View file

@ -1,18 +0,0 @@
defmodule Mix.Tasks.LiveSvelte.Setup do
@moduledoc """
Runs all setup tasks for LiveSvelte.
"""
import LiveSvelte.Logger
def run(_) do
[
"install_npm_deps",
"configure_phoenix",
"configure_esbuild"
]
|> Enum.each(&Mix.Task.run("live_svelte." <> &1))
log_success("live_svelte setup finished.")
end
end

View file

@ -45,7 +45,7 @@ defmodule LiveSvelte.MixProject do
GitHub: @repo_url
},
files:
~w(assets/copy assets/js lib mix.exs package.json .formatter.exs LICENSE.md README.md CHANGELOG.md)
~w(assets/copy/tsconfig.json assets/js lib mix.exs package.json .formatter.exs LICENSE.md README.md CHANGELOG.md)
]
end
@ -61,6 +61,8 @@ defmodule LiveSvelte.MixProject do
defp deps do
[
{:ex_doc, "~> 0.37.3", only: :dev, runtime: false},
{:igniter, "~> 0.6", optional: true},
{:phoenix_vite, "~> 0.4"},
{:jsonpatch, "~> 2.3"},
{:ecto, ">= 3.0.0", optional: true},
{:phoenix_ecto, ">= 4.0.0", optional: true},
@ -70,7 +72,7 @@ defmodule LiveSvelte.MixProject do
{:telemetry, "~> 0.4 or ~> 1.0"},
{:phoenix, ">= 1.7.0"},
{:phoenix_html, ">= 3.3.1"},
{:phoenix_live_view, ">= 0.18.0"}
{:phoenix_live_view, "~> 1.0"}
]
end

View file

@ -7,28 +7,42 @@
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
"esbuild": {:hex, :esbuild, "0.6.1", "a774bfa7b4512a1211bf15880b462be12a4c48ed753a170c68c63b2c95888150", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "569f7409fb5a932211573fc20e2a930a0d5cf3377c5b4f6506c651b1783a1678"},
"ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"},
"finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"},
"fine": {:hex, :fine, "0.1.4", "b19a89c1476c7c57afb5f9314aed5960b5bc95d5277de4cb5ee8e1d1616ce379", [:mix], [], "hexpm", "be3324cc454a42d80951cf6023b9954e9ff27c6daa255483b3e8d608670303f5"},
"glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"igniter": {:hex, :igniter, "0.7.2", "81c132c0df95963c7a228f74a32d3348773743ed9651f24183bfce0fe6ff16d1", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "f4cab73ec31f4fb452de1a17037f8a08826105265aa2d76486fcb848189bef9b"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"jsonpatch": {:hex, :jsonpatch, "2.3.1", "49c380f458debbd2bc6e256daeab1081dc89624288f3d77ea83952229388d316", [:make, :mix], [], "hexpm", "06c3e4fff3574cc54d335041f6322fe1b72756e396dd472615ce350d3dd5e758"},
"lazy_html": {:hex, :lazy_html, "0.1.10", "ffe42a0b4e70859cf21a33e12a251e0c76c1dff76391609bd56702a0ef5bc429", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "50f67e5faa09d45a99c1ddf3fac004f051997877dc8974c5797bb5ccd8e27058"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
"nodejs": {:hex, :nodejs, "3.1.0", "904c07b81a7b6077af35784df32ab36c62bd2b96edb91bfd04c157c21956cfa5", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5.1", [hex: :poolboy, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.7", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "a6b4480f3f266abb5927be8afacfc7809feefd7d1337fa3ce957d0b98eeeae52"},
"phoenix": {:hex, :phoenix, "1.7.0", "cbed113bdc203e2ced75859011fe7e71eeebb6259cefa54de810d9c7048b5e22", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "8526139d4bd79ec97c5c3c8e69f6cd663597f782756cec874ba7da5429c93e34"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"nodejs": {:hex, :nodejs, "3.1.3", "8693fae9fbefa14fb99329292c226df4d4711acfa5a3fa4182dd8d3f779b30bf", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5.1", [hex: :poolboy, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.7", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e7751aad77ac55f8e6c5c07617378afd88d2e0c349d9db2ebb5273aae46ef6a9"},
"owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"},
"phoenix": {:hex, :phoenix, "1.8.4", "0387f84f00071cba8d71d930b9121b2fb3645197a9206c31b908d2e7902a4851", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c988b1cd3b084eebb13e6676d572597d387fa607dab258526637b4e6c4c08543"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.7.0", "75c4b9dfb3efdc42aec2bd5f8bccd978aca0651dbcbc7a3f362ea5d9d43153c6", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "1d75011e4254cb4ddf823e81823a9629559a1be93b4321a6a5f11a5306fbf4cc"},
"phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.15", "58137e648fca9da56d6e931c9c3001f895ff090291052035f395bc958b82f1a5", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "888dd8ea986bebbda741acc65aef788c384d13db91fea416461b2e96aa06a193"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
"phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"},
"phoenix_live_view": {:hex, :phoenix_live_view, "1.1.25", "abc1bdf7f148d7f9a003f149834cc858b24290c433b10ef6d1cbb1d6e9a211ca", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b8946e474799da1f874eab7e9ce107502c96ca318ed46d19f811f847df270865"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.2.0", "ff3a5616e1bed6804de7773b92cbccfc0b0f473faf1f63d7daf1206c7aeaaa6f", [:mix], [], "hexpm", "adc313a5bf7136039f63cfd9668fde73bba0765e0614cba80c06ac9460ff3e96"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
"plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"},
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
"phoenix_vite": {:hex, :phoenix_vite, "0.4.1", "16fc8b4acc7d4e26fab2bcfb10d125392fcfb5a4ba05dd3aea7f2bec048c5e71", [:mix], [{:bun, ">= 1.5.1 and < 2.0.0-0", [hex: :bun, repo: "hexpm", optional: true]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "f2bdb1802bc82f2fa93b24606cd25ebdd389bcf2afc55af804f0af3377410f0d"},
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},
"rewrite": {:hex, :rewrite, "1.2.0", "80220eb14010e175b67c939397e1a8cdaa2c32db6e2e0a9d5e23e45c0414ce21", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "a1cd702bbb9d51613ab21091f04a386d750fc6f4516b81900df082d78b2d8c50"},
"sourceror": {:hex, :sourceror, "1.11.0", "df2cdaffdc323e804009ff50b50bb31e6f2d6e116d936ccf22981f592594d624", [:mix], [], "hexpm", "6e26f572bdfc21d7ad397f596b4cfbbf31d7112126fe3e902c120947073231a8"},
"spitfire": {:hex, :spitfire, "0.3.8", "4151d539b11eab51903fafcf0eed50d64247ce732fcba24680f5428db406c5f6", [:mix], [], "hexpm", "4328ee4af65d41ea1a7874808e36c1c7fc4a7dafb932f006c07aa1847e0c9dd5"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"websock": {:hex, :websock, "0.4.3", "184ac396bdcd3dfceb5b74c17d221af659dd559a95b1b92041ecb51c9b728093", [:mix], [], "hexpm", "5e4dd85f305f43fd3d3e25d70bec4a45228dfed60f0f3b072d8eddff335539cf"},
"websock_adapter": {:hex, :websock_adapter, "0.4.5", "30038a3715067f51a9580562c05a3a8d501126030336ffc6edb53bf57d6d2d26", [:mix], [{:bandit, "~> 0.6", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.4", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "1d9812dc7e703c205049426fd4fe0852a247a825f91b099e53dc96f68bafe4c8"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.9", "43dc3ba6d89ef5dec5b1d0a39698436a1e856d000d84bf31a3149862b01a287f", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5534d5c9adad3c18a0f58a9371220d75a803bf0b9a3d87e6fe072faaeed76a08"},
}