lue-bird / elm-review-upgrade / Upgrade

Reports when an outdated function/type can be replaced.

🔧 Running with --fix will automatically remove all the reported errors.

config =
    [ Upgrade.rule
        [ Upgrade.reference { old = ( "MyUtil", "findMap" ), new = ( "List.Extra", "findMap" ) }
        , Upgrade.application
            { oldName = ( "Array.Extra", "apply" )
            , oldArgumentNames = [ "functions", "arguments" ]
            , oldArgumentsToNew =
                \oldArguments ->
                    case oldArguments of
                        [ functions, arguments ] ->
                            Upgrade.call ( "Array.Extra", "andMap" )
                                [ arguments, functions ]
                                |> Just

                        _ ->
                            Nothing
            }
        ]
    ]

rule : List Upgrade -> Review.Rule.Rule

The rule performing the given Upgrade


type alias Upgrade =
Rope UpgradeSingle

Describes a bunch of transformations to your code. To create one:

To group a few of them together, use Upgrade.batch

batch : List Upgrade -> Upgrade

Group multiple individual Upgrades as one Upgrade.

Upgrade.rule
    [ testVersion1To2
    , elmcraftCoreExtraVersion1To2
    , myInternalAPIChange
    ]

testVersion1To2 : Upgrade
testVersion1To2 =
    Upgrade.batch
        [ Upgrade.reference { old = ( "Fuzz", "tuple" ), new = ( "Fuzz", "pair" ) }
        , Upgrade.reference { old = ( "Fuzz", "tuple3" ), new = ( "Fuzz", "triple" ) }
        , ..etc..
        ]

elmcraftCoreExtraVersion1To2 : Upgrade
elmcraftCoreExtraVersion1To2 =
    Upgrade.batch [ ... ]

myInternalAPIChange : Upgrade
myInternalAPIChange =
    Upgrade.batch [ ... ]

Helps keep the upgrades a bit more tidy, less List.concats and such.

function/value

reference : { old : ( String, String ), new : ( String, String ) } -> Upgrade

Upgrade only the name of the value/function. For example to replace every MyUtil.findMap with List.Extra.findMap:

Upgrade.reference { old = "MyUtil", "findMap" ), new = ( "List.Extra", "findMap" ) }

application : { oldName : ( String, String ), oldArgumentNames : List String, oldArgumentsToNew : List Elm.Syntax.Expression.Expression -> Maybe ReplacementPipeline } -> Upgrade

Flexible Upgrade for a transformation from usage of a given function/value reference to a pipeline or a call.

For example to describe the transformation

Expect.true onFalseDescription actualBool
--> Expect.equal True actualBool |> Expect.onFail onFalseDescription

as an Upgrade.application:

Upgrade.application
    { oldName = ( "Expect", "true" )
    , oldArgumentNames = [ "onFalseDescription", "actualBool" ]
    , oldArgumentsToNew =
        \oldArguments ->
            case oldArguments of
                [ onFalseDescriptionArgument, boolArgument ] ->
                    Upgrade.call ( "Expect", "equal" )
                        [ Elm.CodeGen.fqVal [ "Basics" ] "True"
                        , boolArgument
                        ]
                        |> Upgrade.pipeInto ( "Expect", "onFail" )
                            [ onFalseDescriptionArgument ]
                        |> Just

                _ ->
                    Nothing
    }

Here's another example:

Array.Extra.call funs arguments
--> Array.Extra.andMap arguments funs

as an Upgrade.application:

Upgrade.application
    { oldName = ( "Array.Extra", "call" )
    , oldArgumentNames = [ "functions", "arguments" ]
    , oldArgumentsToNew =
        \oldArguments ->
            case oldArguments of
                [ functionsArgument, argumentsArgument ] ->
                    Upgrade.application ( "Array.Extra", "andMap" )
                        [ argumentsArgument, functionsArgument ]
                        |> Just

                _ ->
                    Nothing
    }

You can also use any expression as arguments to the functions in the pipeline. To construct these, use elm-syntax-dsl or elm-syntax directly.

call : ( String, String ) -> List Elm.Syntax.Expression.Expression -> ReplacementPipeline

Construct an application as the transformed replacement value of an Upgrade.application. Use pipeInto if you want to use its result as the input of a pipeline.

pipeInto : ( String, String ) -> List Elm.Syntax.Expression.Expression -> ReplacementPipeline -> ReplacementPipeline

Extend the transformed value by |> anotherFunction plus arguments. For example to get

List.map mapper |> List.reverse

→

Upgrade.call ( "List", "map" ) [ mapperArgument ]
    |> Upgrade.pipeInto ( "List", "reverse" ) []


type alias ReplacementPipeline =
( { name : ( String
, String )
, arguments : List Elm.Syntax.Expression.Expression }
, List { name : ( String
, String )
, arguments : List Elm.Syntax.Expression.Expression } 
)

A bunch of functions run in sequence after an initial call. Use call to start one and pipeInto to |> it further.

type

typeReference : { old : ( String, String ), new : ( String, String ) } -> Upgrade

Upgrade only the name of the type. For example to replace every Web.ProgramConfig with Web.Program.Config:

Upgrade.typeReference
    { old = "Web", "ProgramConfig" )
    , new = ( "Web.Program", "Config" )
    }

type_ : { oldName : ( String, String ), oldArgumentsToNew : List Elm.Syntax.TypeAnnotation.TypeAnnotation -> Maybe Elm.Syntax.TypeAnnotation.TypeAnnotation } -> Upgrade

Flexible Upgrade for a transformation of a given type constructor to an equivalent type.

For example to do describe the transformation

Endo from to
--> (from -> to) -> from -> to

as a Upgrade.type_:

Upgrade.type_
    { oldName = ( "Endo", "Endo" )
    , oldArgumentsToNew =
        \oldArguments ->
            case oldArguments of
                [ from, to ] ->
                    Elm.CodeGen.funAnn
                        (Elm.CodeGen.funAnn from to)
                        (Elm.CodeGen.funAnn from to)
                        |> Just

                _ ->
                    Nothing
    }

You can use any types in the replacement type. To construct these, use elm-syntax-dsl or elm-syntax directly.

safe internals


type UpgradeSingle
    = Application ({ oldName : ( String, String ), oldArgumentNames : List String, oldArgumentsToNew : List Elm.Syntax.Expression.Expression -> Maybe ReplacementPipeline })
    | Type ({ oldName : ( String, String ), oldArgumentsToNew : List Elm.Syntax.TypeAnnotation.TypeAnnotation -> Maybe Elm.Syntax.TypeAnnotation.TypeAnnotation })

An upgrade for a single function/value/type. A bunch of them are one Upgrade