defmodule SelectoComponents.Colocated do
@moduledoc """
Configuration and utilities for colocated JavaScript and hooks in SelectoComponents.
This module provides functions to:
- List all available colocated hooks
- Generate JavaScript imports for the host application
- Configure Phoenix LiveView colocated assets
"""
@hooks [
{"SelectoComponents.Components.TreeBuilder", "TreeBuilder", "components/tree_builder.ex"},
{"SelectoComponents.Views.Graph.Component", "GraphView", "views/graph/component.ex"}
]
@doc """
Returns a list of all available colocated hooks.
Each hook is a tuple of {module_name, hook_name, file_path}
"""
def hooks, do: @hooks
@doc """
Generates JavaScript code to import and export all colocated hooks.
This is used by the setup task to create the hooks index file.
"""
def generate_hooks_index do
"""
// Auto-generated by SelectoComponents
// This file exports all colocated hooks from selecto_components
#{Enum.map_join(hooks(), "\n", &generate_hook_export/1)}
"""
end
defp generate_hook_export({module_name, _hook_name, _file_path}) do
"""
export { default as #{module_to_hook_name(module_name)} } from "phoenix-live-view:#{module_name}";
"""
end
defp module_to_hook_name(module_name) do
module_name
|> String.split(".")
|> List.last()
|> then(fn name -> name <> "Hook" end)
end
@doc """
Returns configuration for the host application's app.js file.
This includes the necessary imports and hook registrations.
"""
def app_js_config do
hooks_map =
Enum.map(hooks(), fn {module_name, _, _} ->
hook_var = module_to_hook_name(module_name)
~s| "#{module_name}": #{hook_var}|
end)
|> Enum.join(",\n")
"""
// Import selecto_components colocated hooks
#{Enum.map_join(hooks(), "\n", &generate_import/1)}
// In your LiveSocket configuration, add these hooks:
const liveSocket = new LiveSocket("/live", Socket, {
// ... other config ...
hooks: {
#{hooks_map}
}
})
"""
end
defp generate_import({module_name, _, _}) do
hook_var = module_to_hook_name(module_name)
~s|import { default as #{hook_var} } from "selecto_components/#{String.downcase(hook_var)}";|
end
@doc """
Checks if Phoenix LiveView supports colocated assets.
Returns {:ok, version} or {:error, reason}
"""
def check_phoenix_live_view_support do
case Application.spec(:phoenix_live_view, :vsn) do
nil ->
{:error, "Phoenix LiveView is not installed"}
version ->
version_string = to_string(version)
if Version.match?(version_string, ">= 0.20.0") do
{:ok, version_string}
else
{:error,
"Phoenix LiveView #{version_string} does not support colocated assets (requires >= 0.20.0)"}
end
end
end
@doc """
Returns the path where colocated hooks should be extracted.
"""
def hooks_output_path do
Path.join(["assets", "js", "selecto_components", "hooks"])
end
end