defmodule Nodelix do use Application require Logger alias Nodelix.VersionManager @moduledoc """ Nodelix is an installer and runner for [Node.js](https://nodejs.org/). > ### Warning {: .warning} > > This is a pre-release version. As such, anything _may_ change at any time, the public > API _should not_ be considered stable, and using a pinned version is _recommended_. ## Nodelix configuration There is one global configuration for the nodelix application: - `:cacerts_path` - the directory to find certificates for https connections ## Profiles You can define multiple nodelix profiles. There is a default empty profile which you can configure its args, current directory and environment: ```elixir config :nodelix, default: [ args: ~w( some-script.js --some-option ), cd: Path.expand("../assets", __DIR__), ], custom: [ args: ~w( another-script.js --another-option ), cd: Path.expand("../assets/scripts", __DIR__), env: [ NODE_DEBUG: "*" ] ] ``` The default current directory is your project's root. To use a profile other than `default`, you can use the `--profile` option: ```shell mix nodelix --profile custom ``` When `mix nodelix` is invoked, the task arguments will be appended to the ones configured in the profile. """ @doc false def start(_, _) do Supervisor.start_link([], strategy: :one_for_one) end @doc """ Returns the configuration for the given profile. Raises if the profile does not exist. """ def config_for!(profile) when is_atom(profile) do Application.get_env(:nodelix, profile) || raise ArgumentError, """ Unknown nodelix profile. Make sure the profile is defined in your config/config.exs file, such as: config :nodelix, #{profile}: [ args: ~w( some-script.js --some-option ), cd: Path.expand("../assets", __DIR__) ] """ end @doc """ Runs the given command with `args`. The given args will be appended to the configured args. The task output will be streamed directly to stdio. It returns the status of the underlying call. """ def run(version, profile, extra_args \\ []) when is_atom(profile) and is_list(extra_args) do config = config_for!(profile) args = (config[:args] || []) ++ extra_args if length(args) == 0, do: raise(ArgumentError, "No argument provided.") env = Keyword.get(config, :env, %{}) opts = [ cd: config[:cd] || File.cwd!(), env: env, into: IO.stream(:stdio, :line), stderr_to_stdout: true ] VersionManager.bin_path(:node, version) |> System.cmd(args, opts) |> elem(1) end @doc """ Same as `run/3` but using the latest known LTS version at the time of publishing. """ def run_lts(profile, extra_args \\ []) when is_atom(profile) and is_list(extra_args) do run(VersionManager.latest_lts_version(), profile, extra_args) end @doc """ Installs Node.js if the configured version is not available, and then runs `node`. Returns the same as `run/3`. """ def install_and_run(version, profile, args) do unless VersionManager.is_installed?(version) do VersionManager.install(version) end run(version, profile, args) end @doc """ Same as `install_and_run/3` but using the latest known LTS version at the time of publishing. """ def install_and_run_lts(profile, extra_args \\ []) when is_atom(profile) and is_list(extra_args) do install_and_run(VersionManager.latest_lts_version(), profile, extra_args) end end