From 8a4714ecafa79e951e7fd232b80c142088950abf Mon Sep 17 00:00:00 2001 From: Denis Donici Date: Fri, 10 Apr 2026 15:09:57 +0300 Subject: [PATCH] chore: update example project --- example_project/.gitignore | 2 +- example_project/assets/js/app.vite.js | 62 ------- example_project/assets/js/server.vite.js | 8 - example_project/assets/vendor/topbar.js | 152 ------------------ example_project/config/config.exs | 13 +- example_project/config/dev.exs | 1 - example_project/config/prod.exs | 5 + example_project/config/test.exs | 6 +- example_project/lib/example/application.ex | 10 +- example_project/mix.exs | 5 +- example_project/mix.lock | 1 - .../priv/static/.vite/manifest.json | 2 +- 12 files changed, 23 insertions(+), 244 deletions(-) delete mode 100644 example_project/assets/js/app.vite.js delete mode 100644 example_project/assets/js/server.vite.js delete mode 100644 example_project/assets/vendor/topbar.js diff --git a/example_project/.gitignore b/example_project/.gitignore index 04771d2..2581f8b 100644 --- a/example_project/.gitignore +++ b/example_project/.gitignore @@ -33,7 +33,7 @@ example-*.tar # In case you use Node.js/npm, you want to ignore these. npm-debug.log -/assets/node_modules/ +node_modules/ # Ignore automatically generated Svelte files by the ~V sigil /assets/svelte/_build/ diff --git a/example_project/assets/js/app.vite.js b/example_project/assets/js/app.vite.js deleted file mode 100644 index b63aaf4..0000000 --- a/example_project/assets/js/app.vite.js +++ /dev/null @@ -1,62 +0,0 @@ -// Client-side entry point for both Vite dev server (HMR) and production builds. -// Uses virtual:live-svelte-components (provided by liveSveltePlugin) instead of -// the esbuild-plugin-import-glob glob syntax used by the old esbuild app.js entry. - -import "phoenix_html" -import {Socket} from "phoenix" -import {LiveSocket} from "phoenix_live_view" -import topbar from "../vendor/topbar" -import {getHooks} from "live_svelte" -import Components from "virtual:live-svelte-components" - -function formatPayload(str) { - if (!str || str === "—") return "—" - try { - return JSON.stringify(JSON.parse(str), null, 2) - } catch { - return str - } -} - -const PropsDiffPayloadDisplay = { - mounted() { - this.updateDisplays() - const root = this.el - const diffOnEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='true']") - const diffOffEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='false']") - const observer = new MutationObserver(() => this.updateDisplays()) - if (diffOnEl) observer.observe(diffOnEl, {attributes: true, attributeFilter: ["data-props"]}) - if (diffOffEl) observer.observe(diffOffEl, {attributes: true, attributeFilter: ["data-props"]}) - this._observer = observer - }, - updated() { - this.updateDisplays() - }, - destroyed() { - this._observer?.disconnect() - }, - updateDisplays() { - const root = this.el - const diffOnEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='true']") - const diffOffEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='false']") - const preOn = root.querySelector("#payload-display-diff-on") - const preOff = root.querySelector("#payload-display-diff-off") - if (preOn) preOn.textContent = formatPayload(diffOnEl?.getAttribute("data-props") ?? "—") - if (preOff) preOff.textContent = formatPayload(diffOffEl?.getAttribute("data-props") ?? "—") - }, -} - -const Hooks = { - ...getHooks(Components), - PropsDiffPayloadDisplay, -} - -let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") -let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}}) - -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()) - -liveSocket.connect() -window.liveSocket = liveSocket diff --git a/example_project/assets/js/server.vite.js b/example_project/assets/js/server.vite.js deleted file mode 100644 index 0acf4d7..0000000 --- a/example_project/assets/js/server.vite.js +++ /dev/null @@ -1,8 +0,0 @@ -// Vite SSR entry point for liveSveltePlugin's /ssr_render endpoint. -// Uses Vite-native import.meta.glob instead of the esbuild-plugin-import-glob -// syntax used by server.js. Configured via vite.config.js: -// liveSveltePlugin({ entrypoint: './js/server.vite.js' }) -import { getRender } from "live_svelte" -import Components from "virtual:live-svelte-components" - -export const render = getRender(Components) diff --git a/example_project/assets/vendor/topbar.js b/example_project/assets/vendor/topbar.js deleted file mode 100644 index 3348eb8..0000000 --- a/example_project/assets/vendor/topbar.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * @license MIT - * topbar 2.0.0, 2023-02-04 - * https://buunguyen.github.io/topbar - * Copyright (c) 2021 Buu Nguyen - */ -;(function (window, document) { - "use strict" - - // https://gist.github.com/paulirish/1579671 - ;(function () { - var lastTime = 0 - var vendors = ["ms", "moz", "webkit", "o"] - for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { - window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"] - window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] || window[vendors[x] + "CancelRequestAnimationFrame"] - } - if (!window.requestAnimationFrame) - window.requestAnimationFrame = function (callback, element) { - var currTime = new Date().getTime() - var timeToCall = Math.max(0, 16 - (currTime - lastTime)) - var id = window.setTimeout(function () { - callback(currTime + timeToCall) - }, timeToCall) - lastTime = currTime + timeToCall - return id - } - if (!window.cancelAnimationFrame) - window.cancelAnimationFrame = function (id) { - clearTimeout(id) - } - })() - - var canvas, - currentProgress, - showing, - progressTimerId = null, - fadeTimerId = null, - delayTimerId = null, - addEvent = function (elem, type, handler) { - if (elem.addEventListener) elem.addEventListener(type, handler, false) - else if (elem.attachEvent) elem.attachEvent("on" + type, handler) - else elem["on" + type] = handler - }, - options = { - autoRun: true, - barThickness: 3, - barColors: { - 0: "rgba(26, 188, 156, .9)", - ".25": "rgba(52, 152, 219, .9)", - ".50": "rgba(241, 196, 15, .9)", - ".75": "rgba(230, 126, 34, .9)", - "1.0": "rgba(211, 84, 0, .9)", - }, - shadowBlur: 10, - shadowColor: "rgba(0, 0, 0, .6)", - className: null, - }, - repaint = function () { - canvas.width = window.innerWidth - canvas.height = options.barThickness * 5 // need space for shadow - - var ctx = canvas.getContext("2d") - ctx.shadowBlur = options.shadowBlur - ctx.shadowColor = options.shadowColor - - var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0) - for (var stop in options.barColors) lineGradient.addColorStop(stop, options.barColors[stop]) - ctx.lineWidth = options.barThickness - ctx.beginPath() - ctx.moveTo(0, options.barThickness / 2) - ctx.lineTo(Math.ceil(currentProgress * canvas.width), options.barThickness / 2) - ctx.strokeStyle = lineGradient - ctx.stroke() - }, - createCanvas = function () { - canvas = document.createElement("canvas") - var style = canvas.style - style.position = "fixed" - style.top = style.left = style.right = style.margin = style.padding = 0 - style.zIndex = 100001 - style.display = "none" - if (options.className) canvas.classList.add(options.className) - document.body.appendChild(canvas) - addEvent(window, "resize", repaint) - }, - topbar = { - config: function (opts) { - for (var key in opts) if (options.hasOwnProperty(key)) options[key] = opts[key] - }, - show: function (delay) { - if (showing) return - if (delay) { - if (delayTimerId) return - delayTimerId = setTimeout(() => topbar.show(), delay) - } else { - showing = true - if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId) - if (!canvas) createCanvas() - canvas.style.opacity = 1 - canvas.style.display = "block" - topbar.progress(0) - if (options.autoRun) { - ;(function loop() { - progressTimerId = window.requestAnimationFrame(loop) - topbar.progress("+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)) - })() - } - } - }, - progress: function (to) { - if (typeof to === "undefined") return currentProgress - if (typeof to === "string") { - to = (to.indexOf("+") >= 0 || to.indexOf("-") >= 0 ? currentProgress : 0) + parseFloat(to) - } - currentProgress = to > 1 ? 1 : to - repaint() - return currentProgress - }, - hide: function () { - clearTimeout(delayTimerId) - delayTimerId = null - if (!showing) return - showing = false - if (progressTimerId != null) { - window.cancelAnimationFrame(progressTimerId) - progressTimerId = null - } - ;(function loop() { - if (topbar.progress("+.1") >= 1) { - canvas.style.opacity -= 0.05 - if (canvas.style.opacity <= 0.05) { - canvas.style.display = "none" - fadeTimerId = null - return - } - } - fadeTimerId = window.requestAnimationFrame(loop) - })() - }, - } - - if (typeof module === "object" && typeof module.exports === "object") { - module.exports = topbar - } else if (typeof define === "function" && define.amd) { - define(function () { - return topbar - }) - } else { - this.topbar = topbar - } -}).call(this, window, document) diff --git a/example_project/config/config.exs b/example_project/config/config.exs index 64c1e15..fc62624 100644 --- a/example_project/config/config.exs +++ b/example_project/config/config.exs @@ -8,7 +8,7 @@ import Config config :phoenix_vite, PhoenixVite.Npm, - assets: [args: [], cd: __DIR__], + assets: [args: [], cd: Path.expand("..", __DIR__)], vite: [ args: ~w(exec -- vite), cd: Path.expand("../assets", __DIR__), @@ -38,17 +38,6 @@ config :example, ExampleWeb.Endpoint, # at the `config/runtime.exs`. config :example, Example.Mailer, adapter: Swoosh.Adapters.Local -# Configure tailwind (the version is required) -config :tailwind, - version: "4.1.10", - default: [ - args: ~w( - --input=css/app.css - --output=../priv/static/assets/app.css - ), - cd: Path.expand("../assets", __DIR__) - ] - # Configures Elixir's Logger config :logger, :console, format: "$time $metadata[$level] $message\n", diff --git a/example_project/config/dev.exs b/example_project/config/dev.exs index 604f371..01f6491 100644 --- a/example_project/config/dev.exs +++ b/example_project/config/dev.exs @@ -22,7 +22,6 @@ config :example, ExampleWeb.Endpoint, secret_key_base: "cZ19cfxyAQ7Nr/qlKBKxr/jRRgW6wk8MQEgJrMNFjfOPEo6hSY1v50sFb0vIjv3P", static_url: [host: "localhost", port: 5173], watchers: [ - tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}, vite: {PhoenixVite.Npm, :run, [:vite, ~w(dev)]} ] diff --git a/example_project/config/prod.exs b/example_project/config/prod.exs index 1f0a676..1d3a218 100644 --- a/example_project/config/prod.exs +++ b/example_project/config/prod.exs @@ -19,3 +19,8 @@ config :logger, level: :info # Runtime production configuration, including reading # of environment variables, is done on config/runtime.exs. + +# Production SSR via NodeJS +config :live_svelte, + ssr_module: LiveSvelte.SSR.NodeJS, + ssr: true diff --git a/example_project/config/test.exs b/example_project/config/test.exs index 4ecf92a..5b12bfe 100644 --- a/example_project/config/test.exs +++ b/example_project/config/test.exs @@ -27,7 +27,11 @@ config :phoenix_test, :endpoint, ExampleWeb.Endpoint # Disable LiveSvelte SSR in test so E2E tests run against the client-side bundle. # Otherwise server-rendered HTML can mask client-only bugs (e.g. hardcoded props). -config :live_svelte, ssr: false +# Explicit ssr_module so NodeJS.Supervisor starts (needed by phoenix_test SSR tests +# that temporarily enable SSR via Application.put_env/3). +config :live_svelte, + ssr: false, + ssr_module: LiveSvelte.SSR.NodeJS # In test we don't send emails. config :example, Example.Mailer, adapter: Swoosh.Adapters.Test diff --git a/example_project/lib/example/application.ex b/example_project/lib/example/application.ex index 09a7aa0..6d8a1ba 100644 --- a/example_project/lib/example/application.ex +++ b/example_project/lib/example/application.ex @@ -7,8 +7,14 @@ defmodule Example.Application do @impl true def start(_type, _args) do - children = [ - {NodeJS.Supervisor, [path: LiveSvelte.SSR.NodeJS.server_path(), pool_size: 4]}, + node_js_children = + if Application.get_env(:live_svelte, :ssr_module, nil) == LiveSvelte.SSR.NodeJS do + [{NodeJS.Supervisor, [path: LiveSvelte.SSR.NodeJS.server_path(), pool_size: 4]}] + else + [] + end + + children = node_js_children ++ [ # Start the Telemetry supervisor ExampleWeb.Telemetry, # Start the Ecto repository diff --git a/example_project/mix.exs b/example_project/mix.exs index 680bd16..153c220 100644 --- a/example_project/mix.exs +++ b/example_project/mix.exs @@ -53,8 +53,7 @@ defmodule Example.MixProject do {:wallaby, "~> 0.30", runtime: false, only: :test}, {:phoenix_test, "~> 0.9", only: :test}, {:phoenix_live_reload, "~> 1.2", only: :dev}, - {:phoenix_vite, "~> 0.4"}, - {:tailwind, "~> 0.3", runtime: Mix.env() == :dev} + {:phoenix_vite, "~> 0.4"} ] end @@ -70,7 +69,7 @@ defmodule Example.MixProject do "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.reset": ["ecto.drop", "ecto.setup"], test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"], - "assets.setup": ["phoenix_vite.npm assets install", "tailwind.install --if-missing"], + "assets.setup": ["phoenix_vite.npm assets install"], "assets.build": [ "phoenix_vite.npm vite build --manifest --emptyOutDir true", "phoenix_vite.npm vite build --ssrManifest --emptyOutDir false --ssr js/server.js --outDir ../priv/svelte" diff --git a/example_project/mix.lock b/example_project/mix.lock index e5a72cb..ca16ca9 100644 --- a/example_project/mix.lock +++ b/example_project/mix.lock @@ -43,7 +43,6 @@ "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "swoosh": {:hex, :swoosh, "1.21.0", "9f4fa629447774cfc9ad684d8a87a85384e8fce828b6390dd535dfbd43c9ee2a", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9127157bfb33b7e154d0f1ba4e888e14b08ede84e81dedcb318a2f33dbc6db51"}, - "tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"}, "telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"}, "telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"}, diff --git a/example_project/priv/static/.vite/manifest.json b/example_project/priv/static/.vite/manifest.json index 75140ec..c747f25 100644 --- a/example_project/priv/static/.vite/manifest.json +++ b/example_project/priv/static/.vite/manifest.json @@ -18,7 +18,7 @@ "isDynamicEntry": true }, "css/app.css": { - "file": "assets/app-CLO35oKD.css", + "file": "assets/app-DAL7h2-Q.css", "name": "app", "names": [ "app.css"