phoenix_live_reload

repository·main·Indexed 43 days ago

https://github.com/phoenixframework/phoenix_live_reload

A development tool for Phoenix applications that provides live-reloading of assets (JS, CSS), templates, and views. It features server log streaming to the browser console for Elixir v1.15+, editor integration via PLUG_EDITOR to jump to HEEx definitions, and configurable filesystem watching patterns and intervals.

Tokens
3.6K
Snippets
12
Records
17
Agent score
55%

What's inside phoenix_live_reload

  1. Understand the difference between phoenix_live_reload and Phoenix.CodeReloader

    main

    It is important to distinguish between these two tools:

    • Phoenix.CodeReloader: Recompiles Elixir code in the lib directory. Changes to Elixir files are reloaded on the next request.
    • phoenix_live_reload: Injects JavaScript into your page to provide a WebSocket connection. It triggers browser reloads for templates, views, and JavaScript/CSS changes. If an Elixir file changes, it is recompiled and then the page is reloaded via the WebSocket. For assets like CSS/JS, it allows reloading without a full page refresh.
  2. Jump to HEEx function definitions using PLUG_EDITOR

    main

    You can click on HTML elements in the browser to jump directly to the corresponding HEEx source code in your editor.

    1. Set the PLUG_EDITOR environment variable using a protocol URL supported by your editor (e.g., vscode://file/__FILE__:__LINE__).
    2. In your JavaScript, use reloader.enableEditorShortcuts({caller: "c", definition: "d"}) inside the "phx:live_reload:attached" callback.
    3. While holding the specified key (c for caller, d for definition), click the element to open the editor.
    # Example for VS Code
    export PLUG_EDITOR="vscode://file/__FILE__:__LINE__"
    window.addEventListener("phx:live_reload:attached", ({detail: reloader}) => {
      // Open configured PLUG_EDITOR at the clicked element's HEEx caller or
      // function component definition. Disable with reloader.disableEditorShortcuts()
      reloader.enableEditorShortcuts({caller: "c", definition: "d"})
      window.liveReloader = reloader
    })
  3. Stream server logs to the web console

    main

    For Elixir v1.15+, you can stream server logs directly to the browser's web console.

    1. Enable the feature in config/dev.exs by setting web_console_logger: true within the live_reload config.
    2. In your client-side JavaScript, listen for the "phx:live_reload:attached" event and call reloader.enableServerLogs().

    Each log line is emitted as a "phx:live_reload:log" event on the window object, providing level, message, and metadata (like pid, line, and file).

    # config/dev.exs
    config :my_app, MyAppWeb.Endpoint,
      live_reload: [
        interval: 1000,
        patterns: [...],
        web_console_logger: true
      ]
    // Client-side JavaScript
    window.addEventListener("phx:live_reload:attached", ({detail: reloader}) => {
      // enable server log streaming to client.
      // disable with reloader.disableServerLogs()
      reloader.enableServerLogs()
    })
    
    window.addEventListener("phx:live_reload:log", ({detail: { level, message, metadata }}) => {
      // level is one of "debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"
      // message is the log message string
      // metadata is a map of log metadata, including by default: pid, line, file
    })
  4. Configure live_reload interval and patterns

    main

    In your config/dev.exs, you can configure the live_reload options for your Endpoint. You can specify a custom interval (in milliseconds) and a list of patterns to watch. The default interval is 100ms.

    # Watch static and templates for browser reloading.
    config :my_app, MyAppWeb.Endpoint,
      live_reload: [
        interval: 1000,
        patterns: [
          ~r"\.(ex|exs|heex)",
          ~r"\.(js|css)$"
        ]
      ]
  5. Stream logs to the web console

    main

    To enable streaming server logs directly to your browser's web console, enable the web_console_logger in your endpoint configuration.

    When enabled, the phoenix:live_reload channel subscribes to log events and pushes them to the client via the log event. Each log message includes:

    • level: The log level as a string.
    • msg: The log message.
    • file: The file where the log originated.
    • line: The line number.
    • pid: The process ID.
    • metadata: Any associated metadata.
    # In your endpoint configuration
    config :my_app, MyAppWeb.Endpoint, 
      live_reload: %{
        web_console_logger: true
      }
  6. Configure filesystem watching backends and directories

    main

    The project uses FileSystem to watch for changes. You can specify a :backend in config/config.exs.

    Available backends:

    • Default (OS-specific via FileSystem): Linux (inotify), Windows (inotify-win), Mac OS X (fsevents), FreeBSD/OpenBSD/BSD (inotify).
    • :fs_poll: A polling backend available on all operating systems.

    You can also explicitly define dirs (relative to project root) to watch to improve efficiency, and provide backend_opts like interval.

    config :phoenix_live_reload,
      dirs: [
        "priv/static",
        "priv/gettext",
        "lib/example_web/live",
        "lib/example_web/views",
        "lib/example_web/templates"
      ],
      backend: :fs_poll,
      backend_opts: [
        interval: 500
      ]
  7. Enable live reloading for sibling apps in an umbrella project

    main

    In an umbrella application, if you want to trigger reloads based on code changes in sibling applications, you must perform two steps:

    1. Set the reloadable_apps option on your endpoint to ensure the sibling code is recompiled.
    2. Configure the :dirs option for Phoenix.LiveReloader to include the directories of the sibling applications.
    3. Ensure your :patterns in the live_reload config match the file types in those sibling applications.

    Example configuration in config/dev.exs:

    root_path =
      __ENV__.file
      |> Path.dirname()
      |> Path.join("..")
      |> Path.expand()
    
    config :phoenix_live_reload, :dirs, [
      Path.join([root_path, "apps", "app1"]),
      Path.join([root_path, "apps", "app2"]),
    ]
  8. Setup Phoenix LiveReloader in your Endpoint

    main

    To enable live-reloading in development, add the Phoenix.LiveReloader.Socket and the Phoenix.LiveReloader plug within a code_reloading? block in your endpoint.ex file. This ensures live-reloading is only active during development when code reloading is enabled.

    if code_reloading? do
      socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
      plug Phoenix.CodeReloader
      plug Phoenix.LiveReloader
    end
  9. Use low-level editor opening APIs

    main

    If you need custom event handling for opening the editor, you can use reloader.openEditorAtCaller(target) and reloader.openEditorAtDef(target) directly.

    // Example of manual implementation
    let keyDown
    window.addEventListener("keydown", e => keyDown = e.key)
    window.addEventListener("keyup", e => keyDown = null)
    window.addEventListener("click", e => {
      if(keyDown === "c"){
        e.preventDefault()
        e.stopImmediatePropagation()
        reloader.openEditorAtCaller(e.target)
      } else if(keyDown === "d"){
        e.preventDefault()
        e.stopImmediatePropagation()
        reloader.openEditorAtDef(e.target)
      }
    }, true)