diff --git a/CHANGELOG.md b/CHANGELOG.md index fdc962d..914f0e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,98 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.18.0 - 2026-03-06 + +> **Note:** Many of the features in this release were backported from +> [live_vue](https://github.com/Valian/live_vue) by +> [Jakub Skalecki](https://github.com/Valian). A huge thank you to Jakub +> for the excellent architecture and implementation that served as the +> foundation for props diffing, composables, streams support, Vite +> integration, Igniter installer, and more. + +### Breaking Changes + +- **esbuild removed** — LiveSvelte no longer supports esbuild. Migrate to + Vite + `phoenix_vite`. See the [Upgrade Guide](guides/upgrade_guide.html) + for step-by-step instructions. + +- **live_json / live_json_props removed** — The `live_json` hex dependency + and `live_json_props` attribute have been removed. Replace with `props={...}`; + built-in JSON Patch diffing provides the same optimization by default. + +- **phoenix_vite now required** — Add `{:phoenix_vite, "~> 0.4"}` to your + `mix.exs` dependencies. + +### Added + +- **Vite integration** — Full Vite build pipeline replaces esbuild. + `phoenix_vite` handles client and SSR builds with a single `vite.config.mjs`. + +- **Vite HMR** — Hot Module Replacement for Svelte components and CSS in + development. Zero extra config when using `phoenix_vite` — just run + `mix phx.server`. + +- **ViteJS SSR mode** — New `LiveSvelte.SSR.ViteJS` backend sends SSR render + requests to the Vite dev server over HTTP. New `.svelte` files are picked up + immediately without running `mix assets.build`. + +- **SSR telemetry** — SSR render events emit telemetry under + `[:live_svelte, :ssr, :render, :start]` / `[:live_svelte, :ssr, :render, :stop]` + for performance monitoring. + +- **Igniter installer** — `mix live_svelte.install` (powered by + [Igniter](https://hex.pm/packages/igniter)) sets up LiveSvelte automatically + in an existing Phoenix project, including Vite config, endpoint changes, and + JavaScript entrypoint. + +- **Props diffing (JSON Patch)** — Server-side props are diffed using RFC 6902 + JSON Patch, sending only changed values to the client. Enabled by default; + disable with `config :live_svelte, enable_props_diff: false`. + +- **Svelte encoder** — New `LiveSvelte.Encoder` protocol for custom JSON + serialization of Svelte props. Replaces the need for `@derive Jason.Encoder` + on Elixir structs. + +- **Streams support** — Phoenix `stream/3` now works with Svelte components. + Stream operations are sent as JSON Patch ops via the `data-streams-diff` + attribute. + +- **Live form support** — New `useLiveForm` composable for Ecto + changeset-backed forms in Svelte with server-side validation feedback. + +- **File upload support** — New `useLiveUpload` composable for Phoenix + LiveView file uploads with progress tracking. + +- **Event reply** — New `useEventReply` composable for handling `push_reply` + responses from the server. + +- **Live navigation** — New `useLiveNavigation` composable exposing `patch()` + and `navigate()` for client-side routing from Svelte. + +- **TypeScript rewrite** — Library source (`assets/js/live_svelte/*.ts`) is + now written in TypeScript with full type definitions exported via + `package.json`. + +- **Hot-apply new components** — New `.svelte` files added during development + are automatically discovered by the ViteJS SSR mode and Vite plugin without + restarting the Phoenix server. + +- **CI/CD** — GitHub Actions workflows for Elixir (tests + Coveralls coverage + reporting) and frontend (Vitest unit tests). + +- **Documentation** — `ex_doc` integration; all public modules now have + HexDocs entries. + +- **New examples** — Drag & Drop, Rich Text Editor (Editor.js), Runed state + management, Svelte Stores. ### Removed -- **live_json** — The `live_json` hex dependency and `live_json_props` feature have been removed from the library and example project. Use built-in props diffing and JSON Patch (see Configuration) for efficient prop updates instead. +- **esbuild** — Removed `esbuild` mix dependency, `assets/build.js`, and all + related `config :esbuild` configuration. + +- **live_json / live_json_props** — Removed in favor of built-in props + diffing (enabled by default). ## 0.17.4 - 2026-02-18 diff --git a/example_project/mix.exs b/example_project/mix.exs index 3beb6ed..680bd16 100644 --- a/example_project/mix.exs +++ b/example_project/mix.exs @@ -4,8 +4,9 @@ defmodule Example.MixProject do def project do [ app: :example, - version: "0.1.0", - elixir: "~> 1.16", + name: "LiveSvelte Example", + version: "0.18.0", + elixir: "~> 1.18", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, listeners: [Phoenix.CodeReloader], @@ -37,7 +38,7 @@ defmodule Example.MixProject do {:gettext, "~> 0.20"}, {:json_diff_ex, "~> 0.6", override: true}, {:live_svelte, path: ".."}, - # {:live_svelte, "~> 0.17.4"}, + # {:live_svelte, "~> 0.18.0"}, {:phoenix, "~> 1.8.0"}, {:phoenix_ecto, "~> 4.4"}, {:phoenix_html, "~> 4.1"}, diff --git a/guides/upgrade_guide.md b/guides/upgrade_guide.md new file mode 100644 index 0000000..25dd105 --- /dev/null +++ b/guides/upgrade_guide.md @@ -0,0 +1,207 @@ +# Upgrade Guide + +## Upgrading to 0.18.0 + +Version 0.18.0 migrates the build toolchain from esbuild to Vite and removes +the `live_json` dependency. Follow the steps below to update your project. + +### 1. Replace esbuild with Vite + +#### `mix.exs` — swap deps + +```elixir +# Remove: +{:esbuild, "~> 0.8", runtime: Mix.env() == :dev} + +# Add: +{:phoenix_vite, "~> 0.4"} +``` + +Also remove any `config :esbuild` block from `config/config.exs`: + +```elixir +# Remove entirely: +config :esbuild, :default, + args: ~w(js/app.js --bundle ...), + cd: Path.expand("../assets", __DIR__), + env: %{"NODE_PATH" => ...} +``` + +Run `mix deps.get` after updating `mix.exs`. + +#### `assets/package.json` — add Vite and Svelte plugin + +```json +{ + "devDependencies": { + "vite": "^6.0.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/vite": "^4.0.0" + } +} +``` + +Run `npm install` (or `cd assets && npm install`) after updating. + +#### Create `assets/vite.config.mjs` + +Delete the old `assets/build.js` and create `assets/vite.config.mjs`: + +```javascript +import { defineConfig } from "vite" +import { svelte } from "@sveltejs/vite-plugin-svelte" +import liveSveltePlugin from "live_svelte/vitePlugin" +import tailwindcss from "@tailwindcss/vite" + +export default defineConfig({ + server: { + host: "127.0.0.1", + port: 5173, + strictPort: true, + cors: { origin: "http://localhost:4000" }, + }, + optimizeDeps: { + include: ["live_svelte", "phoenix", "phoenix_html", "phoenix_live_view"], + }, + ssr: { noExternal: process.env.NODE_ENV === "production" ? true : undefined }, + build: { + manifest: false, + ssrManifest: false, + rollupOptions: { input: ["js/app.js", "css/app.css"] }, + outDir: "../priv/static", + emptyOutDir: true, + }, + plugins: [ + tailwindcss(), + svelte({ compilerOptions: { css: "injected" } }), + liveSveltePlugin({ entrypoint: "./js/server.js" }), + ], +}) +``` + +The `liveSveltePlugin` is exported from `live_svelte/vitePlugin` and handles +SSR component discovery automatically — no separate SSR build config is needed. + +#### Update `config/dev.exs` — replace esbuild watcher with Vite + +```elixir +# Remove from watchers: +esbuild: {Esbuild, :install_and_run, [:default, ~w(--bundle ...)]} + +# Update the endpoint config block: +config :my_app, MyAppWeb.Endpoint, + http: [ip: {127, 0, 0, 1}, port: 4000], + check_origin: false, + code_reloader: true, + debug_errors: true, + secret_key_base: "...", + # Assets are now served by the Vite dev server on port 5173: + static_url: [host: "localhost", port: 5173], + watchers: [ + tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}, + vite: {PhoenixVite.Npm, :run, [:vite, ~w(dev)]} + ] +``` + +Setting `static_url` to the Vite dev server port is important — it ensures +LiveView generates asset URLs that point to the Vite dev server (which provides +HMR) rather than Phoenix's static file serving. + +Add ViteJS SSR for development at the bottom of `config/dev.exs`. This means +new `.svelte` files are discovered automatically without needing to rebuild assets +after every addition: + +```elixir +config :live_svelte, ssr_module: LiveSvelte.SSR.ViteJS, vite_host: "http://localhost:5173" +``` + +Production continues to use NodeJS SSR with the pre-built `priv/svelte/server.js` +(the default), so no production config change is needed. + +#### Update `lib/my_app_web/endpoint.ex` + +Import `PhoenixVite.Plug` and add the favicon plug. The favicon plug proxies the +browser favicon request to the Vite dev server in development: + +```elixir +defmodule MyAppWeb.Endpoint do + use Phoenix.Endpoint, otp_app: :my_app + import PhoenixVite.Plug # <-- add this + + # ... socket declarations ... + + # Add before Plug.Static: + plug :favicon, dev_server: {PhoenixVite.Components, :has_vite_watcher?, [__MODULE__]} + + plug Plug.Static, + at: "/", + from: :my_app, + gzip: false, + only: MyAppWeb.static_paths() + + # ... rest of plugs unchanged ... +end +``` + +#### Update `lib/my_app_web/components/layouts/root.html.heex` + +Replace the static asset tags with `PhoenixVite.Components.assets`. This single +component handles both development (proxying to the Vite dev server with HMR) +and production (emitting hashed ` + +<%# Replace with: %> + static_url(@conn, p) end} +/> +``` + +Replace `:my_app` and `MyAppWeb.Endpoint` with your own OTP app name and +endpoint module. + +### 2. Remove live_json (if used) + +Remove the dependency from `mix.exs`: + +```elixir +# Remove: +{:live_json, "~> 0.4"} +``` + +In your LiveViews, replace the `live_json_props` attribute with the standard +`props` attribute. Props diffing via JSON Patch is enabled by default in 0.18.0, +so payloads remain optimized — only changed values are sent over the wire: + +```heex +<%# Before: %> +<.svelte name="MyComponent" live_json_props={@json_props} socket={@socket} /> + +<%# After: %> +<.svelte name="MyComponent" props={@my_props} socket={@socket} /> +``` + +If you want to disable props diffing globally (not recommended): + +```elixir +# config/config.exs +config :live_svelte, enable_props_diff: false +``` + +### 3. Verify the upgrade + +After completing the steps above: + +```bash +mix deps.get +cd assets && npm install && cd .. +mix phx.server # Should start Phoenix + Vite dev server together +mix test # Library unit tests +cd example_project && mix assets.build && mix test --only phoenix_test +``` diff --git a/mix.exs b/mix.exs index de2bd8e..27f163d 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule LiveSvelte.MixProject do use Mix.Project - @version "0.17.4" + @version "0.18.0" @repo_url "https://github.com/woutdp/live_svelte" def project do @@ -38,6 +38,7 @@ defmodule LiveSvelte.MixProject do # Getting Started "guides/installation.md": [title: "Installation"], + "guides/upgrade_guide.md": [title: "Upgrade Guide"], "guides/basic_usage.md": [title: "Basic Usage"], # Core Usage @@ -59,7 +60,7 @@ defmodule LiveSvelte.MixProject do "guides/troubleshooting.md": [title: "Troubleshooting"] ], groups_for_extras: [ - "Getting Started": ~r/guides\/(installation|basic_usage)/, + "Getting Started": ~r/guides\/(installation|upgrade_guide|basic_usage)/, "Core Usage": ~r/guides\/(forms|uploads|streams|ssr|configuration)/, Reference: ~r/guides\/api_reference/, "Advanced Topics": ~r/guides\/(introduction|testing|deployment)/, diff --git a/package.json b/package.json index 8b8d5bb..c283983 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "live_svelte", - "version": "0.17.4", + "version": "0.18.0", "description": "", "license": "MIT", "types": "./assets/js/live_svelte/types.d.ts",