Current section
Files
Jump to
Current section
Files
lib/mix/tasks/phia.install.ex
defmodule Mix.Tasks.Phia.Install do
@moduledoc """
Sets up PhiaUI in the current Phoenix project.
Injects the PhiaUI TailwindCSS v4 `@theme` block into `assets/css/app.css`,
creates `assets/js/phia_hooks/index.js`, and registers PhiaUI hooks in
`assets/js/app.js`.
## Usage
mix phia.install
## Behaviour
- Detects the current app name via `Mix.Project.config()[:app]`.
- Prepends the `@theme { ... }` block from `priv/templates/theme/theme.css`
into `assets/css/app.css`.
- Creates `assets/js/phia_hooks/index.js` with PhiaHooks export.
- Adds PhiaHooks import and registration to `assets/js/app.js`.
- **Idempotent**: all steps are safe to run multiple times.
- Prints confirmation messages via `Mix.shell().info/1`.
## Options
--help Print this help message
"""
use Mix.Task
@shortdoc "Installs PhiaUI theme tokens, hooks, and app.js wiring"
@css_marker "/* PhiaUI Theme — do not remove this line */"
@js_marker "// phia_hooks_registered"
@hooks_content """
// PhiaUI JS Hooks — auto-generated by mix phia.install
// Add your custom PhiaUI hooks here or import from sub-modules.
const PhiaHooks = {}
export default PhiaHooks
"""
@impl Mix.Task
def run(["--help"]) do
Mix.shell().info(@moduledoc)
end
def run(_args) do
name = app_name()
root = File.cwd!()
css_path = Path.join([root, "assets/css/app.css"])
if File.exists?(css_path) do
inject_theme(css_path)
Mix.shell().info([:green, "* inject ", :reset, css_path])
else
Mix.shell().error("Could not find #{css_path}. Run from your Phoenix project root.")
end
inject_hooks(root)
Mix.shell().info([:green, "* create ", :reset, "assets/js/phia_hooks/index.js"])
inject_app_js(root)
Mix.shell().info([:green, "* update ", :reset, "assets/js/app.js"])
Mix.shell().info([:green, "✓ PhiaUI installed for ", :reset, inspect(name)])
end
@doc """
Returns the current Mix project's application name.
## Example
iex> Mix.Tasks.Phia.Install.app_name()
:phia_ui
"""
@spec app_name() :: atom()
def app_name, do: Mix.Project.config()[:app]
@doc """
Injects the PhiaUI theme block into the CSS file at `path`.
Skips silently if the marker is already present (idempotent).
"""
@spec inject_theme(Path.t()) :: :ok | :already_installed
def inject_theme(path) do
current = File.read!(path)
if String.contains?(current, @css_marker) do
:already_installed
else
theme_block = build_theme_block()
File.write!(path, theme_block <> current)
:ok
end
end
@doc """
Creates `assets/js/phia_hooks/index.js` under `root` with PhiaHooks export.
Uses `Mix.Generator.create_file/3` for interactive conflict handling: if the
file already exists, the user is prompted before any overwrite.
"""
@spec inject_hooks(Path.t()) :: :ok | nil
def inject_hooks(root) do
hooks_dir = Path.join([root, "assets", "js", "phia_hooks"])
hooks_path = Path.join(hooks_dir, "index.js")
File.mkdir_p!(hooks_dir)
Mix.Generator.create_file(hooks_path, @hooks_content)
end
@doc """
Adds PhiaHooks import and registration to `assets/js/app.js` under `root`.
Skips if the idempotency marker is already present.
"""
@spec inject_app_js(Path.t()) :: :ok | :already_installed | :not_found
def inject_app_js(root) do
app_js = Path.join([root, "assets", "js", "app.js"])
if File.exists?(app_js) do
current = File.read!(app_js)
if String.contains?(current, @js_marker) do
:already_installed
else
injection =
"#{@js_marker}\n" <>
"import PhiaHooks from './phia_hooks/index.js'\n\n"
File.write!(app_js, injection <> current)
:ok
end
else
:not_found
end
end
# ---------------------------------------------------------------------------
# Private helpers
# ---------------------------------------------------------------------------
defp build_theme_block do
template_path = template_path()
theme_css = File.read!(template_path)
@css_marker <> "\n" <> theme_css <> "\n"
end
defp template_path do
# When running inside the phia_ui library itself, use priv/ directly.
# When installed as a dep, use Application.app_dir/2.
local = Path.join([File.cwd!(), "priv/templates/theme/theme.css"])
if File.exists?(local) do
local
else
Application.app_dir(:phia_ui, "priv/templates/theme/theme.css")
end
end
end