defmodule Mix.Tasks.Vite.Install do @moduledoc """ Installs Vite. $ mix vite.install $ mix vite.install --using bun By default, it installs #{Vite.latest_version()} but you can configure it in your config files, such as: config :vite, :version, "#{Vite.latest_version()}" ## Options * `--using` - change the package management * `--no-config` - does not install vite config * `--if-missing` - install only if the given version does not exist ## Assets Whenever Vite is installed, a default vite configuration will be placed in a new `assets/vite.config.js` file. See the [vite documentation](https://vitejs.dev/config) on configuration options. """ @shortdoc "Installs vite" @compile {:no_warn_undefined, Mix} use Mix.Task @impl true def run(args) do valid_options = [ config: :boolean, using: :string, if_missing: :boolean ] opts = case OptionParser.parse_head!(args, strict: valid_options) do {opts, []} -> opts {_, _} -> Mix.raise(""" Invalid arguments to vite.install, expected one of: mix vite.install mix vite.install --using bun """) end if Keyword.get(opts, :config, true) do vite_config_path = Path.expand("assets/vite.config.mjs") config_exists? = Enum.any?(["mjs", "cjs", "js", "ts"], fn ext -> path = Path.expand("assets/vite.config.#{ext}") File.exists?(path) end) prepare_package_json() if not config_exists? do File.write!(vite_config_path, """ // See the Vite configuration guide for advanced usage // https://vitejs.dev/config import { defineConfig } from "vite"; export default defineConfig(({ command }) => { const isDev = command !== "build"; if (isDev) { // Terminate the watcher when Phoenix quits process.stdin.on("close", () => { process.exit(0); }); process.stdin.resume(); } return { resolve: { preserveSymlinks: true, }, plugins: [], publicDir: "../priv/public", build: { target: "esnext", // build for recent browsers outDir: "../priv/static", // emit assets to priv/static emptyOutDir: true, sourcemap: isDev, // enable source map in dev build manifest: true, rollupOptions: { input: "./js/app.js", }, }, }; }); """) end end if opts[:if_missing] && latest_version?() do :ok else Vite.install(Keyword.get(opts, :using, "npm")) end end def prepare_package_json() do case File.read("assets/package.json") do {:ok, pkg} -> File.write!( "assets/package.json", Jason.decode!(pkg) |> Map.update("type", "module", fn val -> val end) |> Map.update("workspaces", %{}, fn val -> ["../deps/*" | val] end) ) {:error, _} -> :ok end end defp latest_version?() do version = Vite.configured_version() match?(^version, Vite.installed_version()) end end