Packages
igniter
0.6.23
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/igniter/project/igniter_config.ex
defmodule Igniter.Project.IgniterConfig do
@configs [
module_location: [
type: {:in, [:outside_matching_folder, :inside_matching_folder]},
default: :outside_matching_folder,
doc: """
- `:outside_matching_folder`, modules will be placed in a folder exactly matching their path.
- `:inside_matching_folder`, modules who's name matches an existing folder will be placed inside that folder,
or moved there if the folder is created.
"""
],
extensions: [
type:
{:list,
{:or,
[
{:behaviour, Igniter.Extension},
{:tuple, [{:behaviour, Igniter.Extension}, :keyword_list]}
]}},
default: [],
doc: "A list of extensions to use in the project."
],
deps_location: [
type: {:or, [:last_list_literal, {:tagged_tuple, :variable, :atom}, :mfa]},
default: :last_list_literal,
doc: """
The strategy for finding the `deps` list to add new dependencies to, in your `deps/0` function in `mix.exs`
- `:last_list_literal` expects your deps function to return a literal list which will be prepended to
- `{:variable, :name}` expects to find an assignment from the given variable to a list literal, i.e `deps = [...]`, and prepends to that
- `:mfa` will call the given mfa with the igniter and the zipper within the `deps/0` function. It should return `{:ok, zipper}`
at the position where the dep should be prepended, or :error if the location could not be found.
"""
],
source_folders: [
type: {:list, :string},
default: ["lib", "test/support"],
doc: "A list of folders to manage elixir files in."
],
dont_move_files: [
type: {:list, :any},
doc:
"A list of strings or regexes. Any files that equal (in the case of strings) or match (in the case of regexes) will not be moved."
]
]
def configs do
Keyword.update(@configs, :dont_move_files, [], fn options ->
# We use this trick to inject values that cannot be stored in a module constant as of Erlang/OTP 28
Keyword.merge(options,
default: [
~r"lib/mix"
],
quoted_default:
quote do
[~r"lib/mix"]
end
)
end)
end
docs =
Enum.map_join(@configs, "\n", fn {name, config} ->
"- `#{name}` - \n#{config[:doc]}"
end)
@moduledoc """
Tools for reading and modifying the `.igniter.exs` file.
The command `mix igniter.setup` will generate this file, as well
as keep it up to date with any new configurations. You can run this
command at any time to update the file without overriding your own config.
If the file does not exist, all values are considered to have their default value.
## Options
#{docs}
"""
def get(igniter, config) do
igniter.assigns[:igniter_exs][config] || configs()[config][:default]
end
def add_extension(igniter, extension) do
extension =
case extension do
{mod, opts} -> {mod, opts}
mod -> {mod, []}
end
quoted =
extension
|> Macro.escape()
|> Sourceror.to_string()
|> Sourceror.parse_string!()
igniter
|> setup()
|> Igniter.update_elixir_file(".igniter.exs", fn zipper ->
rightmost = Igniter.Code.Common.rightmost(zipper)
if Igniter.Code.List.list?(rightmost) do
Igniter.Code.Keyword.set_keyword_key(
zipper,
:extensions,
[quoted],
fn zipper ->
case Igniter.Code.List.move_to_list_item(zipper, fn zipper ->
if Igniter.Code.Tuple.tuple?(zipper) do
with {:ok, item} <- Igniter.Code.Tuple.tuple_elem(zipper, 0),
true <- Igniter.Code.Common.nodes_equal?(item, elem(extension, 0)) do
true
else
_ ->
false
end
else
Igniter.Code.Common.nodes_equal?(zipper, elem(extension, 0))
end
end) do
{:ok, _} ->
{:ok, zipper}
_ ->
Igniter.Code.List.prepend_to_list(zipper, quoted)
end
end
)
else
{:warning,
"Failed to modify `.igniter.exs` when adding the extension #{inspect(extension)} because its last return value is not a list literal."}
end
end)
end
def configure(igniter, key, value) do
value =
value
|> Macro.escape()
|> Sourceror.to_string()
|> Sourceror.parse_string!()
igniter
|> setup()
|> Igniter.update_elixir_file(".igniter.exs", fn zipper ->
rightmost = Igniter.Code.Common.rightmost(zipper)
if Igniter.Code.List.list?(rightmost) do
Igniter.Code.Keyword.set_keyword_key(
zipper,
key,
[value],
fn zipper ->
{:ok, Igniter.Code.Common.replace_code(zipper, value)}
end
)
else
{:warning, "Failed to modify `.igniter.exs` when configuring #{inspect(key)}"}
end
end)
end
def dont_move_file_pattern(igniter, pattern) do
quoted =
case pattern do
%Regex{} = pattern ->
Sourceror.parse_string!(inspect(pattern))
pattern ->
pattern
|> Macro.escape()
|> Sourceror.to_string()
|> Sourceror.parse_string!()
end
igniter
|> setup()
|> Igniter.update_elixir_file(".igniter.exs", fn zipper ->
rightmost = Igniter.Code.Common.rightmost(zipper)
if Igniter.Code.List.list?(rightmost) do
Igniter.Code.Keyword.set_keyword_key(
zipper,
:dont_move_files,
[quoted],
fn zipper ->
Igniter.Code.List.prepend_new_to_list(zipper, quoted)
end
)
else
{:warning,
"Failed to modify `.igniter.exs` when adding the ignore module pattern #{inspect(pattern)} because its last return value is not a list literal."}
end
end)
end
def setup(igniter) do
Igniter.create_or_update_elixir_file(
igniter,
".igniter.exs",
"""
# This is a configuration file for igniter.
# For option documentation, see https://hexdocs.pm/igniter/Igniter.Project.IgniterConfig.html
# To keep it up to date, use `mix igniter.setup`
[]
""",
&{:ok, &1}
)
|> Igniter.update_elixir_file(
".igniter.exs",
fn zipper ->
rightmost = Igniter.Code.Common.rightmost(zipper)
if Igniter.Code.List.list?(rightmost) do
Enum.reduce_while(configs(), {:ok, zipper}, fn {name, config}, {:ok, zipper} ->
default =
config[:quoted_default] ||
quote do
unquote(Macro.escape(config[:default]))
end
set_result =
Igniter.Code.Common.within(zipper, fn zipper ->
Igniter.Code.Keyword.set_keyword_key(
zipper,
name,
default,
fn zipper ->
{:ok, zipper}
end
)
end)
# when we have a way to comment ahead of a keyword item
# we should comment the docs
case set_result do
{:ok, zipper} ->
{:cont, {:ok, zipper}}
:error ->
{:halt,
{:warning,
"Failed to modify `.igniter.exs` while trying to set default value for #{name}: #{inspect(default)}"}}
end
end)
else
{:warning,
"Failed to modify `.igniter.exs` because its last return value is not a list literal"}
end
end
)
end
end