defmodule Mix.Tasks.Wac.Install do @moduledoc """ Generates schemas, migrations, and contexts for users with WebauthnComponents as the primary authentication mechanism. ## Options By default, contexts, schemas, tests, and web modules are generated or modified by `wac.install`. You may opt out of one or more generators with the following options: - `--no-contexts`: Do not generate context modules. - `--no-schemas`: Do not generate schema & migration modules. - `--no-tests`: Do not generate test modules & scripts. - `--no-web`: Do not generate the authentication LiveView, the session controller, or session hooks. - `--no-router`: Do not modify the router. ## Templates Within the [`webauthn_components`](https://github.com/liveshowy/webauthn_components) repo, the following templates are used by `wac.install` to scaffold the modules needed to support Passkeys in a LiveView application: #{for dir <- File.ls!("templates") |> Enum.sort(), file <- Path.join(["templates", dir]) |> File.ls!() |> Enum.sort() do "\n- `templates/#{dir}/#{file}`" end} """ use Mix.Task alias Wac.Gen.Contexts alias Wac.Gen.Controllers alias Wac.Gen.Schemas alias Wac.Gen.LiveViews alias Wac.Gen.SessionHooks alias Wac.Gen.Migrations alias Wac.Gen.Router alias Wac.Gen.Tests alias Wac.Gen.Fixtures alias Wac.Gen.Javascript alias Wac.Gen.Components alias Wac.Gen.AppHtml alias Wac.Gen.Misc @version Mix.Project.config()[:version] @shortdoc "Generates a user schema." @mix_task "wac.install" @switches [ contexts: :boolean, schemas: :boolean, tests: :boolean, web: :boolean, router: :boolean ] @default_opts [ contexts: true, schemas: true, tests: true, web: true, router: true ] @requirements ["app.start"] @impl Mix.Task def run([version]) when version in ~w(-v --version) do Mix.shell().info("WebauthnComponents Schemas generator v#{@version}") end def run([version]) when version in ~w(-h --help) do Mix.Tasks.Help.run([@mix_task]) end def run(args) do if Mix.Project.umbrella?() do Mix.shell().error("Sorry, umbrella projects are currently unsupported.") end case OptionParser.parse(args, strict: @switches) do {flags, _args, []} -> opts = Keyword.merge(@default_opts, flags) dirname = File.cwd!() |> Path.basename() dirname_camelized = Macro.camelize(dirname) web_dirname = dirname <> "_web" web_dirname_camelized = Macro.camelize(web_dirname) assigns = [ app_snake_case: dirname, app_pascal_case: Module.concat([dirname_camelized]), web_snake_case: web_dirname, web_pascal_case: Module.concat([web_dirname_camelized]) ] if opts[:contexts] do Contexts.copy_templates(assigns) Misc.copy_templates(assigns) end if opts[:schemas] do Schemas.copy_templates(assigns) Migrations.copy_templates(assigns) end if opts[:tests] do Tests.copy_templates(assigns) Fixtures.copy_templates(assigns) end if opts[:web] do Controllers.copy_templates(assigns) LiveViews.copy_templates(assigns) AppHtml.update_app_html(assigns) AppHtml.update_page_html(assigns) SessionHooks.copy_templates(assigns) Components.copy_templates(assigns) Javascript.inject_hooks() end if opts[:router] do Router.update_router(assigns) end success_message = """ ✅ Successfully scaffolded WebauthnComponents for #{inspect(assigns[:app_pascal_case])} 📚 Resources - Repo:\thttps://github.com/liveshowy/webauthn_components - Hex:\thttps://hex.pm/packages/webauthn_components - Docs:\thttps://hexdocs.pm/webauthn_components/readme.html """ IO.puts(success_message) {_parsed, _args, errors} -> invalid_opts = errors |> Enum.map_join(", ", &elem(&1, 0)) Mix.Tasks.Help.run([@mix_task]) Mix.shell().error("Invalid option(s): #{invalid_opts}") end end end