mirror of
https://github.com/ash-project/igniter.git
synced 2024-09-17 03:52:51 +12:00
improvement: add CI/build and get it passing locally
This commit is contained in:
parent
281ec0f656
commit
260d56532b
30 changed files with 755 additions and 261 deletions
21
.check.exs
Normal file
21
.check.exs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[
|
||||||
|
## all available options with default values (see `mix check` docs for description)
|
||||||
|
# parallel: true,
|
||||||
|
# skipped: true,
|
||||||
|
|
||||||
|
## list of tools (see `mix check` docs for defaults)
|
||||||
|
tools: [
|
||||||
|
## curated tools may be disabled (e.g. the check for compilation warnings)
|
||||||
|
# {:compiler, false},
|
||||||
|
|
||||||
|
## ...or adjusted (e.g. use one-line formatter for more compact credo output)
|
||||||
|
# {:credo, "mix credo --format oneline"},
|
||||||
|
|
||||||
|
{:doctor, false}
|
||||||
|
|
||||||
|
## custom new tools may be added (mix tasks or arbitrary commands)
|
||||||
|
# {:my_mix_task, command: "mix release", env: %{"MIX_ENV" => "prod"}},
|
||||||
|
# {:my_arbitrary_tool, command: "npm test", cd: "assets"},
|
||||||
|
# {:my_arbitrary_script, command: ["my_script", "argument with spaces"], cd: "scripts"}
|
||||||
|
]
|
||||||
|
]
|
184
.credo.exs
Normal file
184
.credo.exs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
# This file contains the configuration for Credo and you are probably reading
|
||||||
|
# this after creating it with `mix credo.gen.config`.
|
||||||
|
#
|
||||||
|
# If you find anything wrong or unclear in this file, please report an
|
||||||
|
# issue on GitHub: https://github.com/rrrene/credo/issues
|
||||||
|
#
|
||||||
|
%{
|
||||||
|
#
|
||||||
|
# You can have as many configs as you like in the `configs:` field.
|
||||||
|
configs: [
|
||||||
|
%{
|
||||||
|
#
|
||||||
|
# Run any config using `mix credo -C <name>`. If no config name is given
|
||||||
|
# "default" is used.
|
||||||
|
#
|
||||||
|
name: "default",
|
||||||
|
#
|
||||||
|
# These are the files included in the analysis:
|
||||||
|
files: %{
|
||||||
|
#
|
||||||
|
# You can give explicit globs or simply directories.
|
||||||
|
# In the latter case `**/*.{ex,exs}` will be used.
|
||||||
|
#
|
||||||
|
included: [
|
||||||
|
"lib/",
|
||||||
|
"src/",
|
||||||
|
"test/",
|
||||||
|
"web/",
|
||||||
|
"apps/*/lib/",
|
||||||
|
"apps/*/src/",
|
||||||
|
"apps/*/test/",
|
||||||
|
"apps/*/web/"
|
||||||
|
],
|
||||||
|
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
|
||||||
|
},
|
||||||
|
#
|
||||||
|
# Load and configure plugins here:
|
||||||
|
#
|
||||||
|
plugins: [],
|
||||||
|
#
|
||||||
|
# If you create your own checks, you must specify the source files for
|
||||||
|
# them here, so they can be loaded by Credo before running the analysis.
|
||||||
|
#
|
||||||
|
requires: [],
|
||||||
|
#
|
||||||
|
# If you want to enforce a style guide and need a more traditional linting
|
||||||
|
# experience, you can change `strict` to `true` below:
|
||||||
|
#
|
||||||
|
strict: false,
|
||||||
|
#
|
||||||
|
# To modify the timeout for parsing files, change this value:
|
||||||
|
#
|
||||||
|
parse_timeout: 5000,
|
||||||
|
#
|
||||||
|
# If you want to use uncolored output by default, you can change `color`
|
||||||
|
# to `false` below:
|
||||||
|
#
|
||||||
|
color: true,
|
||||||
|
#
|
||||||
|
# You can customize the parameters of any check by adding a second element
|
||||||
|
# to the tuple.
|
||||||
|
#
|
||||||
|
# To disable a check put `false` as second element:
|
||||||
|
#
|
||||||
|
# {Credo.Check.Design.DuplicatedCode, false}
|
||||||
|
#
|
||||||
|
checks: [
|
||||||
|
#
|
||||||
|
## Consistency Checks
|
||||||
|
#
|
||||||
|
{Credo.Check.Consistency.ExceptionNames, []},
|
||||||
|
{Credo.Check.Consistency.LineEndings, []},
|
||||||
|
{Credo.Check.Consistency.ParameterPatternMatching, []},
|
||||||
|
{Credo.Check.Consistency.SpaceAroundOperators, []},
|
||||||
|
{Credo.Check.Consistency.SpaceInParentheses, []},
|
||||||
|
{Credo.Check.Consistency.TabsOrSpaces, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Design Checks
|
||||||
|
#
|
||||||
|
# You can customize the priority of any check
|
||||||
|
# Priority values are: `low, normal, high, higher`
|
||||||
|
#
|
||||||
|
{Credo.Check.Design.AliasUsage, false},
|
||||||
|
# You can also customize the exit_status of each check.
|
||||||
|
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||||
|
# set this value to 0 (zero).
|
||||||
|
#
|
||||||
|
{Credo.Check.Design.TagTODO, [exit_status: 2]},
|
||||||
|
{Credo.Check.Design.TagFIXME, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Readability Checks
|
||||||
|
#
|
||||||
|
{Credo.Check.Readability.AliasOrder, []},
|
||||||
|
{Credo.Check.Readability.FunctionNames, []},
|
||||||
|
{Credo.Check.Readability.LargeNumbers, []},
|
||||||
|
{Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
|
||||||
|
{Credo.Check.Readability.ModuleAttributeNames, []},
|
||||||
|
{Credo.Check.Readability.ModuleDoc, []},
|
||||||
|
{Credo.Check.Readability.ModuleNames, []},
|
||||||
|
{Credo.Check.Readability.ParenthesesInCondition, []},
|
||||||
|
{Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
|
||||||
|
{Credo.Check.Readability.PredicateFunctionNames, []},
|
||||||
|
{Credo.Check.Readability.PreferImplicitTry, []},
|
||||||
|
{Credo.Check.Readability.RedundantBlankLines, []},
|
||||||
|
{Credo.Check.Readability.Semicolons, []},
|
||||||
|
{Credo.Check.Readability.SpaceAfterCommas, []},
|
||||||
|
{Credo.Check.Readability.StringSigils, []},
|
||||||
|
{Credo.Check.Readability.TrailingBlankLine, []},
|
||||||
|
{Credo.Check.Readability.TrailingWhiteSpace, []},
|
||||||
|
{Credo.Check.Readability.UnnecessaryAliasExpansion, []},
|
||||||
|
{Credo.Check.Readability.VariableNames, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Refactoring Opportunities
|
||||||
|
#
|
||||||
|
{Credo.Check.Refactor.CondStatements, []},
|
||||||
|
{Credo.Check.Refactor.CyclomaticComplexity, false},
|
||||||
|
{Credo.Check.Refactor.FunctionArity, [max_arity: 13]},
|
||||||
|
{Credo.Check.Refactor.LongQuoteBlocks, false},
|
||||||
|
{Credo.Check.Refactor.MapInto, false},
|
||||||
|
{Credo.Check.Refactor.MatchInCondition, []},
|
||||||
|
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
|
||||||
|
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
|
||||||
|
{Credo.Check.Refactor.Nesting, [max_nesting: 10]},
|
||||||
|
{Credo.Check.Refactor.UnlessWithElse, []},
|
||||||
|
{Credo.Check.Refactor.WithClauses, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Warnings
|
||||||
|
#
|
||||||
|
{Credo.Check.Warning.BoolOperationOnSameValues, []},
|
||||||
|
{Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
|
||||||
|
{Credo.Check.Warning.IExPry, []},
|
||||||
|
{Credo.Check.Warning.IoInspect, []},
|
||||||
|
{Credo.Check.Warning.LazyLogging, false},
|
||||||
|
{Credo.Check.Warning.MixEnv, false},
|
||||||
|
{Credo.Check.Warning.OperationOnSameValues, []},
|
||||||
|
{Credo.Check.Warning.OperationWithConstantResult, []},
|
||||||
|
{Credo.Check.Warning.RaiseInsideRescue, []},
|
||||||
|
{Credo.Check.Warning.UnusedEnumOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedFileOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedKeywordOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedListOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedPathOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedRegexOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedStringOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedTupleOperation, []},
|
||||||
|
{Credo.Check.Warning.UnsafeExec, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
# Checks scheduled for next check update (opt-in for now, just replace `false` with `[]`)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Controversial and experimental checks (opt-in, just replace `false` with `[]`)
|
||||||
|
#
|
||||||
|
{Credo.Check.Readability.StrictModuleLayout, false},
|
||||||
|
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
|
||||||
|
{Credo.Check.Consistency.UnusedVariableNames, false},
|
||||||
|
{Credo.Check.Design.DuplicatedCode, false},
|
||||||
|
{Credo.Check.Readability.AliasAs, false},
|
||||||
|
{Credo.Check.Readability.MultiAlias, false},
|
||||||
|
{Credo.Check.Readability.Specs, false},
|
||||||
|
{Credo.Check.Readability.SinglePipe, false},
|
||||||
|
{Credo.Check.Readability.WithCustomTaggedTuple, false},
|
||||||
|
{Credo.Check.Refactor.ABCSize, false},
|
||||||
|
{Credo.Check.Refactor.AppendSingleItem, false},
|
||||||
|
{Credo.Check.Refactor.DoubleBooleanNegation, false},
|
||||||
|
{Credo.Check.Refactor.ModuleDependencies, false},
|
||||||
|
{Credo.Check.Refactor.NegatedIsNil, false},
|
||||||
|
{Credo.Check.Refactor.PipeChainStart, false},
|
||||||
|
{Credo.Check.Refactor.VariableRebinding, false},
|
||||||
|
{Credo.Check.Warning.LeakyEnvironment, false},
|
||||||
|
{Credo.Check.Warning.MapGetUnsafePass, false},
|
||||||
|
{Credo.Check.Warning.UnsafeToAtom, false}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom checks can be created using `mix credo.gen.check`.
|
||||||
|
#
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,4 +1,9 @@
|
||||||
# Used by "mix format"
|
spark_locals_without_parens = [attributes: 1, decrypt_by_default: 1, on_decrypt: 1, vault: 1]
|
||||||
|
|
||||||
[
|
[
|
||||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||||
|
locals_without_parens: spark_locals_without_parens,
|
||||||
|
export: [
|
||||||
|
locals_without_parens: spark_locals_without_parens
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at zach@zachdaniel.dev. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
10
.github/CONTRIBUTING.md
vendored
Normal file
10
.github/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Contributing to Igniter
|
||||||
|
|
||||||
|
- We have a zero tolerance policy for failure to abide by our code of conduct. It is very standard, but please make sure
|
||||||
|
you have read it.
|
||||||
|
- Issues may be opened to propose new ideas, to ask questions, or to file bugs.
|
||||||
|
- Before working on a feature, please talk to the core team/the rest of the community via a proposal. We are
|
||||||
|
building something that needs to be cohesive and well thought out across all use cases. Our top priority is
|
||||||
|
supporting real life use cases like yours, but we have to make sure that we do that in a sustainable way. The
|
||||||
|
best compromise there is to make sure that discussions are centered around the _use case_ for a feature, rather
|
||||||
|
than the propsed feature itself.
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ""
|
||||||
|
labels: bug, needs review
|
||||||
|
assignees: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
A minimal set of resource definitions and calls that can reproduce the bug.
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
\*\* Runtime
|
||||||
|
|
||||||
|
- Elixir version
|
||||||
|
- Erlang version
|
||||||
|
- OS
|
||||||
|
- Igniter version
|
||||||
|
- any related extension versions
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
36
.github/ISSUE_TEMPLATE/proposal.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/proposal.md
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
name: Proposal
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement, needs review
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Express the feature either with a change to resource syntax, or with a change to the resource interface**
|
||||||
|
|
||||||
|
For example
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
attributes do
|
||||||
|
attribute :foo, :integer, bar: 10 # <- Adding `bar` here would cause <x>
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Or
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Api.read(:resource, bar: 10) # <- Adding `bar` here would cause <x>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
### Contributor checklist
|
||||||
|
|
||||||
|
- [ ] Bug fixes include regression tests
|
||||||
|
- [ ] Features include unit/acceptance tests
|
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: mix
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
14
.github/workflows/elixir.yml
vendored
Normal file
14
.github/workflows/elixir.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_call:
|
||||||
|
jobs:
|
||||||
|
ash-ci:
|
||||||
|
uses: ash-project/ash/.github/workflows/ash-ci.yml@main
|
||||||
|
secrets:
|
||||||
|
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
|
2
.tool-versions
Normal file
2
.tool-versions
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
erlang 26.0.2
|
||||||
|
elixir 1.16.2
|
1
FUNDING.yml
Normal file
1
FUNDING.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
github: zachdaniel
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Zachary Scott Daniel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
17
config/config.exs
Normal file
17
config/config.exs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import Config
|
||||||
|
|
||||||
|
if Mix.env() == :dev do
|
||||||
|
config :git_ops,
|
||||||
|
mix_project: Igniter.MixProject,
|
||||||
|
changelog_file: "CHANGELOG.md",
|
||||||
|
repository_url: "https://github.com/ash-project/igniter",
|
||||||
|
# Instructs the tool to manage your mix version in your `mix.exs` file
|
||||||
|
# See below for more information
|
||||||
|
manage_mix_version?: true,
|
||||||
|
# Instructs the tool to manage the version in your README.md
|
||||||
|
# Pass in `true` to use `"README.md"` or a string to customize
|
||||||
|
manage_readme_version: [
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
version_tag_prefix: "v"
|
||||||
|
end
|
7
lib/application.ex
Normal file
7
lib/application.ex
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule Igniter.Application do
|
||||||
|
@moduledoc "Codemods and tools for working with Application modules."
|
||||||
|
|
||||||
|
def app_name do
|
||||||
|
Mix.Project.config()[:app]
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,4 +1,5 @@
|
||||||
defmodule Igniter.Args do
|
defmodule Igniter.Args do
|
||||||
|
@moduledoc "Tools for validating and parsing command line arguments to tasks."
|
||||||
def validate_nth_present_and_underscored(igniter, argv, n, option, message) do
|
def validate_nth_present_and_underscored(igniter, argv, n, option, message) do
|
||||||
value = Enum.at(argv, n)
|
value = Enum.at(argv, n)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule Igniter.Common do
|
defmodule Igniter.Common do
|
||||||
@doc """
|
@moduledoc """
|
||||||
Common utilities for working with igniter, primarily with zippers.
|
Common utilities for working with igniter, primarily with `Sourceror.Zipper`.
|
||||||
"""
|
"""
|
||||||
alias Sourceror.Zipper
|
alias Sourceror.Zipper
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ defmodule Igniter.Common do
|
||||||
defp do_put_in_keyword(zipper, [key | rest], value, updater) do
|
defp do_put_in_keyword(zipper, [key | rest], value, updater) do
|
||||||
if node_matches_pattern?(zipper, value when is_list(value)) do
|
if node_matches_pattern?(zipper, value when is_list(value)) do
|
||||||
case move_to_list_item(zipper, fn item ->
|
case move_to_list_item(zipper, fn item ->
|
||||||
if is_tuple?(item) do
|
if tuple?(item) do
|
||||||
first_elem = tuple_elem(item, 0)
|
first_elem = tuple_elem(item, 0)
|
||||||
first_elem && node_matches_pattern?(first_elem, ^key)
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
end
|
end
|
||||||
|
@ -127,7 +127,7 @@ defmodule Igniter.Common do
|
||||||
def set_keyword_key(zipper, key, value, updater) do
|
def set_keyword_key(zipper, key, value, updater) do
|
||||||
if node_matches_pattern?(zipper, value when is_list(value)) do
|
if node_matches_pattern?(zipper, value when is_list(value)) do
|
||||||
case move_to_list_item(zipper, fn item ->
|
case move_to_list_item(zipper, fn item ->
|
||||||
if is_tuple?(item) do
|
if tuple?(item) do
|
||||||
first_elem = tuple_elem(item, 0)
|
first_elem = tuple_elem(item, 0)
|
||||||
first_elem && node_matches_pattern?(first_elem, ^key)
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
end
|
end
|
||||||
|
@ -176,7 +176,7 @@ defmodule Igniter.Common do
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> move_to_list_item(fn item ->
|
|> move_to_list_item(fn item ->
|
||||||
if is_tuple?(item) do
|
if tuple?(item) do
|
||||||
first_elem = tuple_elem(item, 0)
|
first_elem = tuple_elem(item, 0)
|
||||||
first_elem && node_matches_pattern?(first_elem, ^key)
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
end
|
end
|
||||||
|
@ -222,7 +222,7 @@ defmodule Igniter.Common do
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> move_to_list_item(fn item ->
|
|> move_to_list_item(fn item ->
|
||||||
if is_tuple?(item) do
|
if tuple?(item) do
|
||||||
first_elem = tuple_elem(item, 0)
|
first_elem = tuple_elem(item, 0)
|
||||||
first_elem && node_matches_pattern?(first_elem, ^key)
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
end
|
end
|
||||||
|
@ -284,11 +284,11 @@ defmodule Igniter.Common do
|
||||||
zipper
|
zipper
|
||||||
|> maybe_move_to_block()
|
|> maybe_move_to_block()
|
||||||
|> move_right(fn zipper ->
|
|> move_right(fn zipper ->
|
||||||
is_function_call(zipper, name, arity) && predicate.(zipper)
|
function_call?(zipper, name, arity) && predicate.(zipper)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_function_call(zipper, name, arity) do
|
def function_call?(zipper, name, arity) do
|
||||||
zipper
|
zipper
|
||||||
|> maybe_move_to_block()
|
|> maybe_move_to_block()
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|
@ -312,7 +312,7 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_nth_argument(zipper, index, func) do
|
def update_nth_argument(zipper, index, func) do
|
||||||
if is_pipeline?(zipper) do
|
if pipeline?(zipper) do
|
||||||
if index == 0 do
|
if index == 0 do
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|
@ -373,7 +373,7 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
|
|
||||||
def argument_matches_predicate?(zipper, index, func) do
|
def argument_matches_predicate?(zipper, index, func) do
|
||||||
if is_pipeline?(zipper) do
|
if pipeline?(zipper) do
|
||||||
if index == 0 do
|
if index == 0 do
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|
@ -438,7 +438,7 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_pipeline?(zipper) do
|
def pipeline?(zipper) do
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|
@ -448,6 +448,7 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# sobelow_skip ["DOS.StringToAtom"]
|
||||||
def move_to_module_using(zipper, module) do
|
def move_to_module_using(zipper, module) do
|
||||||
split_module =
|
split_module =
|
||||||
module
|
module
|
||||||
|
@ -660,7 +661,7 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_tuple?(item) do
|
def tuple?(item) do
|
||||||
item
|
item
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
defmodule Igniter.Config do
|
defmodule Igniter.Config do
|
||||||
|
@moduledoc "Codemods and utilities for configuring Elixir applications."
|
||||||
require Igniter.Common
|
require Igniter.Common
|
||||||
alias Igniter.Common
|
alias Igniter.Common
|
||||||
alias Sourceror.Zipper
|
alias Sourceror.Zipper
|
||||||
|
|
63
lib/deps.ex
63
lib/deps.ex
|
@ -1,36 +1,8 @@
|
||||||
defmodule Igniter.Deps do
|
defmodule Igniter.Deps do
|
||||||
|
@moduledoc "Codemods and utilities for managing dependencies declared in mix.exs"
|
||||||
require Igniter.Common
|
require Igniter.Common
|
||||||
alias Sourceror.Zipper
|
|
||||||
alias Igniter.Common
|
alias Igniter.Common
|
||||||
|
alias Sourceror.Zipper
|
||||||
def get_dependency_declaration(igniter, name) do
|
|
||||||
zipper =
|
|
||||||
igniter
|
|
||||||
|> Igniter.include_existing_elixir_file("mix.exs")
|
|
||||||
|> Map.get(:rewrite)
|
|
||||||
|> Rewrite.source!("mix.exs")
|
|
||||||
|> Rewrite.Source.get(:quoted)
|
|
||||||
|> Zipper.zip()
|
|
||||||
|
|
||||||
with {:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
|
|
||||||
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
|
|
||||||
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
|
|
||||||
{:ok, current_declaration} <-
|
|
||||||
Common.move_to_list_item(zipper, fn item ->
|
|
||||||
if Common.is_tuple?(item) do
|
|
||||||
first_elem = Common.tuple_elem(item, 0)
|
|
||||||
first_elem && Common.node_matches_pattern?(first_elem, ^name)
|
|
||||||
end
|
|
||||||
end) do
|
|
||||||
current_declaration
|
|
||||||
|> Zipper.subtree()
|
|
||||||
|> Zipper.node()
|
|
||||||
|> Sourceror.to_string()
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_dependency(igniter, name, version) do
|
def add_dependency(igniter, name, version) do
|
||||||
case get_dependency_declaration(igniter, name) do
|
case get_dependency_declaration(igniter, name) do
|
||||||
|
@ -60,6 +32,35 @@ defmodule Igniter.Deps do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_dependency_declaration(igniter, name) do
|
||||||
|
zipper =
|
||||||
|
igniter
|
||||||
|
|> Igniter.include_existing_elixir_file("mix.exs")
|
||||||
|
|> Map.get(:rewrite)
|
||||||
|
|> Rewrite.source!("mix.exs")
|
||||||
|
|> Rewrite.Source.get(:quoted)
|
||||||
|
|> Zipper.zip()
|
||||||
|
|
||||||
|
with {:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
|
||||||
|
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
|
||||||
|
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
|
||||||
|
{:ok, current_declaration} <-
|
||||||
|
Common.move_to_list_item(zipper, fn item ->
|
||||||
|
if Common.tuple?(item) do
|
||||||
|
first_elem = Common.tuple_elem(item, 0)
|
||||||
|
first_elem && Common.node_matches_pattern?(first_elem, ^name)
|
||||||
|
end
|
||||||
|
end) do
|
||||||
|
current_declaration
|
||||||
|
|> Zipper.subtree()
|
||||||
|
|> Zipper.node()
|
||||||
|
|> Sourceror.to_string()
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp remove_dependency(igniter, name) do
|
defp remove_dependency(igniter, name) do
|
||||||
igniter
|
igniter
|
||||||
|> Igniter.update_file("mix.exs", fn source ->
|
|> Igniter.update_file("mix.exs", fn source ->
|
||||||
|
@ -72,7 +73,7 @@ defmodule Igniter.Deps do
|
||||||
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
|
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
|
||||||
current_declaration_index when not is_nil(current_declaration_index) <-
|
current_declaration_index when not is_nil(current_declaration_index) <-
|
||||||
Common.find_list_item_index(zipper, fn item ->
|
Common.find_list_item_index(zipper, fn item ->
|
||||||
if Common.is_tuple?(item) do
|
if Common.tuple?(item) do
|
||||||
first_elem = Common.tuple_elem(item, 0)
|
first_elem = Common.tuple_elem(item, 0)
|
||||||
first_elem && Common.node_matches_pattern?(first_elem, ^name)
|
first_elem && Common.node_matches_pattern?(first_elem, ^name)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
defmodule Igniter.Formatter do
|
defmodule Igniter.Formatter do
|
||||||
|
@moduledoc "Codemods and utilities for interacting with `.formatter.exs` files"
|
||||||
alias Igniter.Common
|
alias Igniter.Common
|
||||||
alias Sourceror.Zipper
|
alias Sourceror.Zipper
|
||||||
|
|
||||||
|
|
185
lib/igniter.ex
185
lib/igniter.ex
|
@ -8,10 +8,10 @@ defmodule Igniter do
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
rewrite: Rewrite.t(),
|
rewrite: Rewrite.t(),
|
||||||
issues: [String.t()],
|
issues: [String.t()],
|
||||||
tasks: [{String.t() | list(STring.t())}]
|
tasks: [{String.t() | list(String.t())}]
|
||||||
}
|
}
|
||||||
|
|
||||||
def new() do
|
def new do
|
||||||
%__MODULE__{rewrite: Rewrite.new()}
|
%__MODULE__{rewrite: Rewrite.new()}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -121,6 +121,185 @@ defmodule Igniter do
|
||||||
|> format(path)
|
|> format(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def do_or_dry_run(igniter, argv, opts \\ []) do
|
||||||
|
igniter = %{igniter | issues: Enum.uniq(igniter.issues)}
|
||||||
|
title = opts[:title] || "Igniter"
|
||||||
|
|
||||||
|
sources =
|
||||||
|
igniter.rewrite
|
||||||
|
|> Rewrite.sources()
|
||||||
|
|
||||||
|
issues =
|
||||||
|
Enum.flat_map(sources, fn source ->
|
||||||
|
changed_issues =
|
||||||
|
if Rewrite.Source.file_changed?(source) do
|
||||||
|
["File has been changed since it was originally read."]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
issues = Enum.uniq(changed_issues ++ Rewrite.Source.issues(source))
|
||||||
|
|
||||||
|
case issues do
|
||||||
|
[] -> []
|
||||||
|
issues -> [{source, issues}]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
case issues do
|
||||||
|
[_ | _] ->
|
||||||
|
explain_issues(issues)
|
||||||
|
:issues
|
||||||
|
|
||||||
|
[] ->
|
||||||
|
case igniter do
|
||||||
|
%{issues: []} ->
|
||||||
|
result_of_dry_run =
|
||||||
|
sources
|
||||||
|
|> Enum.filter(fn source ->
|
||||||
|
Rewrite.Source.updated?(source)
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
[] ->
|
||||||
|
unless opts[:quiet_on_no_changes?] do
|
||||||
|
Mix.shell().info("\n#{title}: No proposed changes!\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
:dry_run_with_no_changes
|
||||||
|
|
||||||
|
sources ->
|
||||||
|
Mix.shell().info("\n#{title}: Proposed changes:\n")
|
||||||
|
|
||||||
|
Enum.each(sources, fn source ->
|
||||||
|
if Rewrite.Source.from?(source, :string) do
|
||||||
|
content_lines =
|
||||||
|
source
|
||||||
|
|> Rewrite.Source.get(:content)
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.with_index()
|
||||||
|
|
||||||
|
space_padding =
|
||||||
|
content_lines
|
||||||
|
|> Enum.map(&elem(&1, 1))
|
||||||
|
|> Enum.max()
|
||||||
|
|> to_string()
|
||||||
|
|> String.length()
|
||||||
|
|
||||||
|
diffish_looking_text =
|
||||||
|
Enum.map_join(content_lines, "\n", fn {line, line_number_minus_one} ->
|
||||||
|
line_number = line_number_minus_one + 1
|
||||||
|
|
||||||
|
"#{String.pad_trailing(to_string(line_number), space_padding)} #{IO.ANSI.yellow()}| #{IO.ANSI.green()}#{line}#{IO.ANSI.reset()}"
|
||||||
|
end)
|
||||||
|
|
||||||
|
Mix.shell().info("""
|
||||||
|
Create: #{Rewrite.Source.get(source, :path)}
|
||||||
|
|
||||||
|
#{diffish_looking_text}
|
||||||
|
""")
|
||||||
|
else
|
||||||
|
Mix.shell().info("""
|
||||||
|
Update: #{Rewrite.Source.get(source, :path)}
|
||||||
|
|
||||||
|
#{Rewrite.Source.diff(source)}
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
:dry_run_with_changes
|
||||||
|
end
|
||||||
|
|
||||||
|
if igniter.tasks != [] do
|
||||||
|
message =
|
||||||
|
if result_of_dry_run == :dry_run_with_no_changes do
|
||||||
|
"The following tasks will be run"
|
||||||
|
else
|
||||||
|
"The following tasks will be run after the above changes:"
|
||||||
|
end
|
||||||
|
|
||||||
|
Mix.shell().info("""
|
||||||
|
#{message}
|
||||||
|
|
||||||
|
#{Enum.map_join(igniter.tasks, "\n", fn {task, args} -> "* #{IO.ANSI.red()}#{task}#{IO.ANSI.yellow()} #{Enum.join(args, " ")}#{IO.ANSI.reset()}" end)}
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
|
if "--dry-run" in argv || result_of_dry_run == :dry_run_with_no_changes do
|
||||||
|
result_of_dry_run
|
||||||
|
else
|
||||||
|
if "--yes" in argv ||
|
||||||
|
Mix.shell().yes?(opts[:confirmation_message] || "Proceed with changes?") do
|
||||||
|
sources
|
||||||
|
|> Enum.any?(fn source ->
|
||||||
|
Rewrite.Source.updated?(source)
|
||||||
|
end)
|
||||||
|
|> if do
|
||||||
|
igniter.rewrite
|
||||||
|
|> Rewrite.write_all()
|
||||||
|
|> case do
|
||||||
|
{:ok, _result} ->
|
||||||
|
igniter.tasks
|
||||||
|
|> Enum.each(fn {task, args} ->
|
||||||
|
Mix.Task.run(task, args)
|
||||||
|
end)
|
||||||
|
|
||||||
|
:changes_made
|
||||||
|
|
||||||
|
{:error, error, rewrite} ->
|
||||||
|
igniter
|
||||||
|
|> Map.put(:rewrite, rewrite)
|
||||||
|
|> Igniter.add_issue(error)
|
||||||
|
|> igniter_issues()
|
||||||
|
|
||||||
|
:issues
|
||||||
|
end
|
||||||
|
else
|
||||||
|
:no_changes
|
||||||
|
end
|
||||||
|
else
|
||||||
|
:changes_aborted
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
igniter ->
|
||||||
|
igniter_issues(igniter)
|
||||||
|
:issues
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp igniter_issues(igniter) do
|
||||||
|
Mix.shell().info("Issues during code generation")
|
||||||
|
|
||||||
|
igniter.issues
|
||||||
|
|> Enum.map_join("\n", fn error ->
|
||||||
|
if is_binary(error) do
|
||||||
|
"* #{error}"
|
||||||
|
else
|
||||||
|
"* #{Exception.format(:error, error)}"
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Mix.shell().info()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp explain_issues(issues) do
|
||||||
|
Mix.shell().info("Igniter: Issues found in proposed changes:\n")
|
||||||
|
|
||||||
|
Enum.each(issues, fn {source, issues} ->
|
||||||
|
Mix.shell().info("Issues with #{Rewrite.Source.get(source, :path)}")
|
||||||
|
|
||||||
|
issues
|
||||||
|
|> Enum.map_join("\n", fn error ->
|
||||||
|
if is_binary(error) do
|
||||||
|
"* #{error}"
|
||||||
|
else
|
||||||
|
"* #{Exception.format(:error, error)}"
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Mix.shell().info()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp format(igniter, adding_path \\ nil) do
|
defp format(igniter, adding_path \\ nil) do
|
||||||
if adding_path && Path.basename(adding_path) == ".formatter.exs" do
|
if adding_path && Path.basename(adding_path) == ".formatter.exs" do
|
||||||
format(igniter)
|
format(igniter)
|
||||||
|
@ -179,6 +358,7 @@ defmodule Igniter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# sobelow_skip ["RCE.CodeModule"]
|
||||||
defp find_formatter_exs_file_options(path, formatter_exs_files) do
|
defp find_formatter_exs_file_options(path, formatter_exs_files) do
|
||||||
case Map.fetch(formatter_exs_files, path) do
|
case Map.fetch(formatter_exs_files, path) do
|
||||||
{:ok, source} ->
|
{:ok, source} ->
|
||||||
|
@ -249,6 +429,7 @@ defmodule Igniter do
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# sobelow_skip ["RCE.CodeModule"]
|
||||||
defp eval_file_with_keyword_list(path) do
|
defp eval_file_with_keyword_list(path) do
|
||||||
{opts, _} = Code.eval_file(path)
|
{opts, _} = Code.eval_file(path)
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
defmodule Igniter.Install do
|
defmodule Igniter.Install do
|
||||||
|
@moduledoc false
|
||||||
@option_schema [
|
@option_schema [
|
||||||
switches: [
|
switches: [
|
||||||
no_network: :boolean,
|
|
||||||
example: :boolean,
|
example: :boolean,
|
||||||
dry_run: :boolean
|
dry_run: :boolean,
|
||||||
|
yes: :boolean
|
||||||
],
|
],
|
||||||
aliases: [
|
aliases: [
|
||||||
d: :dry_run,
|
d: :dry_run,
|
||||||
n: :no_network,
|
e: :example,
|
||||||
e: :example
|
y: :yes
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
# only supports hex installation at the moment
|
# only supports hex installation at the moment
|
||||||
def install(install, argv) do
|
def install(install, argv) do
|
||||||
install_list =
|
install_list = install_list(install)
|
||||||
install
|
|
||||||
|> String.split(",")
|
|
||||||
|> Enum.map(&String.to_atom/1)
|
|
||||||
|
|
||||||
Application.ensure_all_started(:req)
|
Application.ensure_all_started(:req)
|
||||||
|
|
||||||
|
@ -69,7 +67,7 @@ defmodule Igniter.Install do
|
||||||
end
|
end
|
||||||
|
|
||||||
dependency_add_result =
|
dependency_add_result =
|
||||||
Igniter.Tasks.do_or_dry_run(igniter, argv,
|
Igniter.do_or_dry_run(igniter, argv,
|
||||||
title: "Fetching Dependency",
|
title: "Fetching Dependency",
|
||||||
quiet_on_no_changes?: true,
|
quiet_on_no_changes?: true,
|
||||||
confirmation_message: confirmation_message
|
confirmation_message: confirmation_message
|
||||||
|
@ -89,7 +87,7 @@ defmodule Igniter.Install do
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if install_dep_now? do
|
if install_dep_now? do
|
||||||
Igniter.Tasks.do_or_dry_run(igniter, (argv ++ ["--yes"]) -- ["--dry-run"],
|
Igniter.do_or_dry_run(igniter, (argv ++ ["--yes"]) -- ["--dry-run"],
|
||||||
title: "Fetching Dependency",
|
title: "Fetching Dependency",
|
||||||
quiet_on_no_changes?: true
|
quiet_on_no_changes?: true
|
||||||
)
|
)
|
||||||
|
@ -110,7 +108,7 @@ defmodule Igniter.Install do
|
||||||
end
|
end
|
||||||
|
|
||||||
all_tasks =
|
all_tasks =
|
||||||
Enum.filter(Mix.Task.load_all(), &Spark.implements_behaviour?(&1, Igniter.Mix.Task))
|
Enum.filter(Mix.Task.load_all(), &implements_behaviour?(&1, Igniter.Mix.Task))
|
||||||
|
|
||||||
install_list
|
install_list
|
||||||
|> Enum.flat_map(fn install ->
|
|> Enum.flat_map(fn install ->
|
||||||
|
@ -123,8 +121,44 @@ defmodule Igniter.Install do
|
||||||
|> Enum.reduce(Igniter.new(), fn task, igniter ->
|
|> Enum.reduce(Igniter.new(), fn task, igniter ->
|
||||||
Igniter.compose_task(igniter, task, argv)
|
Igniter.compose_task(igniter, task, argv)
|
||||||
end)
|
end)
|
||||||
|> Igniter.Tasks.do_or_dry_run(argv)
|
|> Igniter.do_or_dry_run(argv)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp implements_behaviour?(module, behaviour) do
|
||||||
|
:attributes
|
||||||
|
|> module.module_info()
|
||||||
|
|> Enum.any?(fn
|
||||||
|
{:behaviour, ^behaviour} ->
|
||||||
|
true
|
||||||
|
|
||||||
|
# optimizations, probably extremely minor but this is in a tight loop in some places
|
||||||
|
{:behaviour, [^behaviour | _]} ->
|
||||||
|
true
|
||||||
|
|
||||||
|
{:behaviour, [_, ^behaviour | _]} ->
|
||||||
|
true
|
||||||
|
|
||||||
|
{:behaviour, [_, _, ^behaviour | _]} ->
|
||||||
|
true
|
||||||
|
|
||||||
|
# never seen a module with three behaviours in real life, let alone four.
|
||||||
|
{:behaviour, behaviours} when is_list(behaviours) ->
|
||||||
|
module in behaviours
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end)
|
||||||
|
rescue
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# sobelow_skip ["DOS.StringToAtom"]
|
||||||
|
defp install_list(install) do
|
||||||
|
install
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.map(&String.to_atom/1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
defmodule Igniter.Mix.Task do
|
defmodule Igniter.Mix.Task do
|
||||||
|
@moduledoc "A behaviour for implementing a Mix task that is enriched to be composable with other Igniter tasks."
|
||||||
@callback supports_umbrella?() :: boolean()
|
@callback supports_umbrella?() :: boolean()
|
||||||
@callback igniter(igniter :: Igniter.t(), argv :: list(String.t())) :: Igniter.t()
|
@callback igniter(igniter :: Igniter.t(), argv :: list(String.t())) :: Igniter.t()
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ defmodule Igniter.Mix.Task do
|
||||||
|
|
||||||
Igniter.new()
|
Igniter.new()
|
||||||
|> igniter(argv)
|
|> igniter(argv)
|
||||||
|> Igniter.Tasks.do_or_dry_run(argv)
|
|> Igniter.do_or_dry_run(argv)
|
||||||
end
|
end
|
||||||
|
|
||||||
def supports_umbrella?, do: false
|
def supports_umbrella?, do: false
|
||||||
|
|
|
@ -1,7 +1,21 @@
|
||||||
defmodule Mix.Tasks.Igniter.Install do
|
defmodule Mix.Tasks.Igniter.Install do
|
||||||
|
@moduledoc """
|
||||||
|
Install a package or packages, and run any associated installers.
|
||||||
|
|
||||||
|
## Args
|
||||||
|
|
||||||
|
mix igniter.install package1,package2,package3
|
||||||
|
|
||||||
|
## Switches
|
||||||
|
|
||||||
|
* `--dry-run` - `d` - Run the task without making any changes.
|
||||||
|
* `--yes` - `y` - Automatically answer yes to any prompts.
|
||||||
|
* `--example` - `e` - Request that installed packages include initial example code.
|
||||||
|
"""
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@shortdoc "Install a package or packages, and run any associated installers."
|
||||||
def run([install | argv]) do
|
def run([install | argv]) do
|
||||||
Application.ensure_all_started([:rewrite])
|
Application.ensure_all_started([:rewrite])
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
defmodule Mix.Tasks.Spark.Install do
|
|
||||||
use Igniter.Mix.Task
|
|
||||||
|
|
||||||
def igniter(igniter, _argv) do
|
|
||||||
igniter
|
|
||||||
|> Igniter.Formatter.add_formatter_plugin(Spark.Formatter)
|
|
||||||
|> Igniter.Config.configure("config.exs", :spark, [:formatter, :remove_parens?], true, & &1)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +1,10 @@
|
||||||
defmodule Igniter.Module do
|
defmodule Igniter.Module do
|
||||||
|
@moduledoc "Codemods and tools for generating and working with Elixir modules"
|
||||||
def module_name(suffix) do
|
def module_name(suffix) do
|
||||||
Module.concat(module_name_prefix(), suffix)
|
Module.concat(module_name_prefix(), suffix)
|
||||||
end
|
end
|
||||||
|
|
||||||
def module_name_prefix() do
|
def module_name_prefix do
|
||||||
Mix.Project.get!()
|
Mix.Project.get!()
|
||||||
|> Module.split()
|
|> Module.split()
|
||||||
|> :lists.droplast()
|
|> :lists.droplast()
|
||||||
|
|
180
lib/tasks.ex
180
lib/tasks.ex
|
@ -1,180 +0,0 @@
|
||||||
defmodule Igniter.Tasks do
|
|
||||||
def app_name do
|
|
||||||
Mix.Project.config()[:app]
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_or_dry_run(igniter, argv, opts \\ []) do
|
|
||||||
igniter = %{igniter | issues: Enum.uniq(igniter.issues)}
|
|
||||||
title = opts[:title] || "Igniter"
|
|
||||||
|
|
||||||
sources =
|
|
||||||
igniter.rewrite
|
|
||||||
|> Rewrite.sources()
|
|
||||||
|
|
||||||
issues =
|
|
||||||
Enum.flat_map(sources, fn source ->
|
|
||||||
changed_issues =
|
|
||||||
if Rewrite.Source.file_changed?(source) do
|
|
||||||
["File has been changed since it was originally read."]
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
issues = Enum.uniq(changed_issues ++ Rewrite.Source.issues(source))
|
|
||||||
|
|
||||||
case issues do
|
|
||||||
[] -> []
|
|
||||||
issues -> [{source, issues}]
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
case issues do
|
|
||||||
[_ | _] ->
|
|
||||||
explain_issues(issues)
|
|
||||||
:issues
|
|
||||||
|
|
||||||
[] ->
|
|
||||||
if igniter.issues == [] do
|
|
||||||
result_of_dry_run =
|
|
||||||
sources
|
|
||||||
|> Enum.filter(fn source ->
|
|
||||||
Rewrite.Source.updated?(source)
|
|
||||||
end)
|
|
||||||
|> case do
|
|
||||||
[] ->
|
|
||||||
unless opts[:quiet_on_no_changes?] do
|
|
||||||
Mix.shell().info("\n#{title}: No proposed changes!\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
:dry_run_with_no_changes
|
|
||||||
|
|
||||||
sources ->
|
|
||||||
Mix.shell().info("\n#{title}: Proposed changes:\n")
|
|
||||||
|
|
||||||
Enum.each(sources, fn source ->
|
|
||||||
if Rewrite.Source.from?(source, :string) do
|
|
||||||
content_lines =
|
|
||||||
source
|
|
||||||
|> Rewrite.Source.get(:content)
|
|
||||||
|> String.split("\n")
|
|
||||||
|> Enum.with_index()
|
|
||||||
|
|
||||||
space_padding =
|
|
||||||
content_lines
|
|
||||||
|> Enum.map(&elem(&1, 1))
|
|
||||||
|> Enum.max()
|
|
||||||
|> to_string()
|
|
||||||
|> String.length()
|
|
||||||
|
|
||||||
diffish_looking_text =
|
|
||||||
Enum.map_join(content_lines, "\n", fn {line, line_number_minus_one} ->
|
|
||||||
line_number = line_number_minus_one + 1
|
|
||||||
|
|
||||||
"#{String.pad_trailing(to_string(line_number), space_padding)} #{IO.ANSI.yellow()}| #{IO.ANSI.green()}#{line}#{IO.ANSI.reset()}"
|
|
||||||
end)
|
|
||||||
|
|
||||||
Mix.shell().info("""
|
|
||||||
Create: #{Rewrite.Source.get(source, :path)}
|
|
||||||
|
|
||||||
#{diffish_looking_text}
|
|
||||||
""")
|
|
||||||
else
|
|
||||||
Mix.shell().info("""
|
|
||||||
Update: #{Rewrite.Source.get(source, :path)}
|
|
||||||
|
|
||||||
#{Rewrite.Source.diff(source)}
|
|
||||||
""")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
:dry_run_with_changes
|
|
||||||
end
|
|
||||||
|
|
||||||
if igniter.tasks != [] do
|
|
||||||
message =
|
|
||||||
if result_of_dry_run in [:dry_run_with_no_changes, :no_changes] do
|
|
||||||
"The following tasks will be run"
|
|
||||||
else
|
|
||||||
"The following tasks will be run after the above changes:"
|
|
||||||
end
|
|
||||||
|
|
||||||
Mix.shell().info("""
|
|
||||||
#{message}
|
|
||||||
|
|
||||||
#{Enum.map_join(igniter.tasks, "\n", fn {task, args} -> "* #{IO.ANSI.red()}#{task}#{IO.ANSI.yellow()} #{Enum.join(args, " ")}#{IO.ANSI.reset()}" end)}
|
|
||||||
""")
|
|
||||||
end
|
|
||||||
|
|
||||||
if "--dry-run" in argv || result_of_dry_run == :dry_run_with_no_changes do
|
|
||||||
result_of_dry_run
|
|
||||||
else
|
|
||||||
if "--yes" in argv ||
|
|
||||||
Mix.shell().yes?(opts[:confirmation_message] || "Proceed with changes?") do
|
|
||||||
sources
|
|
||||||
|> Enum.any?(fn source ->
|
|
||||||
Rewrite.Source.updated?(source)
|
|
||||||
end)
|
|
||||||
|> if do
|
|
||||||
igniter.rewrite
|
|
||||||
|> Rewrite.write_all()
|
|
||||||
|> case do
|
|
||||||
{:ok, _result} ->
|
|
||||||
igniter.tasks
|
|
||||||
|> Enum.each(fn {task, args} ->
|
|
||||||
Mix.Task.run(task, args)
|
|
||||||
end)
|
|
||||||
|
|
||||||
:changes_made
|
|
||||||
|
|
||||||
{:error, error} ->
|
|
||||||
igniter
|
|
||||||
|> Igniter.add_issue(error)
|
|
||||||
|> igniter_issues()
|
|
||||||
|
|
||||||
{:error, error}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
:no_changes
|
|
||||||
end
|
|
||||||
else
|
|
||||||
:changes_aborted
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
igniter_issues(igniter)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp igniter_issues(igniter) do
|
|
||||||
Mix.shell().info("Issues during code generation")
|
|
||||||
|
|
||||||
igniter.issues
|
|
||||||
|> Enum.map_join("\n", fn error ->
|
|
||||||
if is_binary(error) do
|
|
||||||
"* #{error}"
|
|
||||||
else
|
|
||||||
"* #{Exception.format(:error, error)}"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|> Mix.shell().info()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp explain_issues(issues) do
|
|
||||||
Mix.shell().info("Igniter: Issues found in proposed changes:\n")
|
|
||||||
|
|
||||||
Enum.each(issues, fn {source, issues} ->
|
|
||||||
Mix.shell().info("Issues with #{Rewrite.Source.get(source, :path)}")
|
|
||||||
|
|
||||||
issues
|
|
||||||
|> Enum.map_join("\n", fn error ->
|
|
||||||
if is_binary(error) do
|
|
||||||
"* #{error}"
|
|
||||||
else
|
|
||||||
"* #{Exception.format(:error, error)}"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|> Mix.shell().info()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
9
mix.exs
9
mix.exs
|
@ -9,6 +9,7 @@ defmodule Igniter.MixProject do
|
||||||
version: @version,
|
version: @version,
|
||||||
elixir: "~> 1.16",
|
elixir: "~> 1.16",
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
|
aliases: aliases(),
|
||||||
docs: docs(),
|
docs: docs(),
|
||||||
deps: deps()
|
deps: deps()
|
||||||
]
|
]
|
||||||
|
@ -52,7 +53,6 @@ defmodule Igniter.MixProject do
|
||||||
# Run "mix help deps" to learn about dependencies.
|
# Run "mix help deps" to learn about dependencies.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:spark, "~> 2.0"},
|
|
||||||
{:rewrite, "~> 0.9"},
|
{:rewrite, "~> 0.9"},
|
||||||
{:req, "~> 0.4"},
|
{:req, "~> 0.4"},
|
||||||
# Dev/Test dependencies
|
# Dev/Test dependencies
|
||||||
|
@ -70,4 +70,11 @@ defmodule Igniter.MixProject do
|
||||||
{:doctor, "~> 0.21", only: [:dev, :test]}
|
{:doctor, "~> 0.21", only: [:dev, :test]}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp aliases do
|
||||||
|
[
|
||||||
|
sobelow: "sobelow --skip",
|
||||||
|
credo: "credo --strict"
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
10
mix.lock
10
mix.lock
|
@ -1,19 +1,15 @@
|
||||||
%{
|
%{
|
||||||
"ash": {:hex, :ash, "3.0.7", "6c37e092f53b1b21eb89596f600a652b2a601f84378f44fd5dd1cdec72eb1cc2", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, ">= 0.8.1 and < 1.0.0-0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.1.18 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9288ddb50fe727096c6f63fd82c631de2505dcd29bdfa50b5dc13c865f0bf434"},
|
|
||||||
"benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"},
|
"benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"},
|
||||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||||
"castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"},
|
"castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"},
|
||||||
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
|
|
||||||
"credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"},
|
"credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"},
|
||||||
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
||||||
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
||||||
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
|
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
|
||||||
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
|
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
|
||||||
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
|
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
|
||||||
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
|
|
||||||
"eflame": {:hex, :eflame, "1.0.1", "0664d287e39eef3c413749254b3af5f4f8b00be71c1af67d325331c4890be0fc", [:mix], [], "hexpm", "e0b08854a66f9013129de0b008488f3411ae9b69b902187837f994d7a99cf04e"},
|
"eflame": {:hex, :eflame, "1.0.1", "0664d287e39eef3c413749254b3af5f4f8b00be71c1af67d325331c4890be0fc", [:mix], [], "hexpm", "e0b08854a66f9013129de0b008488f3411ae9b69b902187837f994d7a99cf04e"},
|
||||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
|
|
||||||
"ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"},
|
"ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"},
|
||||||
"ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"},
|
"ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"},
|
||||||
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
|
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
|
||||||
|
@ -23,7 +19,6 @@
|
||||||
"glob_ex": {:hex, :glob_ex, "0.1.7", "eae6b6377147fb712ac45b360e6dbba00346689a87f996672fe07e97d70597b1", [:mix], [], "hexpm", "decc1c21c0c73df3c9c994412716345c1692477b9470e337f628a7e08da0da6a"},
|
"glob_ex": {:hex, :glob_ex, "0.1.7", "eae6b6377147fb712ac45b360e6dbba00346689a87f996672fe07e97d70597b1", [:mix], [], "hexpm", "decc1c21c0c73df3c9c994412716345c1692477b9470e337f628a7e08da0da6a"},
|
||||||
"hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"},
|
"hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"},
|
||||||
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
||||||
"libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"},
|
|
||||||
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
|
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
|
||||||
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
|
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
|
||||||
|
@ -36,17 +31,12 @@
|
||||||
"nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"},
|
"nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
|
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
|
||||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||||
"reactor": {:hex, :reactor, "0.8.4", "344d02ba4a0010763851f4e4aa0ff190ebe7e392e3c27c6cd143dde077b986e7", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "49c1fd3c786603cec8140ce941c41c7ea72cc4411860ccdee9876c4ca2204f81"},
|
|
||||||
"req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"},
|
"req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"},
|
||||||
"rewrite": {:hex, :rewrite, "0.10.3", "1c998cceac960c3025a1701158d846dee94bc426d95abefd2b4a2e981835ea1c", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "d3ea3179de167ebda56bf81b7e5c2697256a0719fdcc2c0df65ea8173efe3563"},
|
"rewrite": {:hex, :rewrite, "0.10.3", "1c998cceac960c3025a1701158d846dee94bc426d95abefd2b4a2e981835ea1c", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "d3ea3179de167ebda56bf81b7e5c2697256a0719fdcc2c0df65ea8173efe3563"},
|
||||||
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
||||||
"sourceror": {:hex, :sourceror, "1.2.1", "b415255ad8bd05f0e859bb3d7ea617f6c2a4a405f2a534a231f229bd99b89f8b", [:mix], [], "hexpm", "e4d97087e67584a7585b5fe3d5a71bf8e7332f795dd1a44983d750003d5e750c"},
|
"sourceror": {:hex, :sourceror, "1.2.1", "b415255ad8bd05f0e859bb3d7ea617f6c2a4a405f2a534a231f229bd99b89f8b", [:mix], [], "hexpm", "e4d97087e67584a7585b5fe3d5a71bf8e7332f795dd1a44983d750003d5e750c"},
|
||||||
"spark": {:hex, :spark, "2.1.22", "a36400eede64c51af578de5fdb5a5aaa3e0811da44bcbe7545fce059bd2a990b", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "f764611d0b15ac132e72b2326539acc11fc4e63baa3e429f541bca292b5f7064"},
|
|
||||||
"splode": {:hex, :splode, "0.2.4", "71046334c39605095ca4bed5d008372e56454060997da14f9868534c17b84b53", [:mix], [], "hexpm", "ca3b95f0d8d4b482b5357954fec857abd0fa3ea509d623334c1328e7382044c2"},
|
|
||||||
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
|
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
|
||||||
"stream_data": {:hex, :stream_data, "1.0.0", "c1380747a4650902732696861d5cb66ad3cb1cc93f31c2c8498bf87cddbabe2d", [:mix], [], "hexpm", "acd53e27c66c617d466f42ec77a7f59e5751f6051583c621ccdb055b9690435d"},
|
|
||||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||||
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
|
|
||||||
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
||||||
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
|
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,3 +123,22 @@ defmodule Igniter.ConfigTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.LiveViewNative.Install do
|
||||||
|
use Igniter.Mix.Task
|
||||||
|
|
||||||
|
def igniter(igniter, argv) do
|
||||||
|
Igniter.new()
|
||||||
|
|> Igniter.Config.configure("fake.exs", :fake, [:foo], %{"b" => ["c", "d"]}, fn zipper ->
|
||||||
|
Igniter.Common.set_map_key(zipper, "b", ["c", "d"], fn zipper ->
|
||||||
|
zipper
|
||||||
|
|> Igniter.Common.prepend_new_to_list(zipper, "c")
|
||||||
|
|> Igniter.Common.prepend_new_to_list(zipper, "d")
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, new_zipper} -> new_zipper
|
||||||
|
_ -> zipper
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in a new issue