SiriusStarr / elm-review-no-unsorted / NoUnsortedTopLevelDeclarations

Review Rule

rule : RuleConfig r -> Review.Rule.Rule

Reports top-level declarations that are not in the "proper" order.

🔧 Running with --fix will automatically sort the declarations.

The proper order of declarations is specified in the rule configuration. See the Configuration section below for more information.

config =
    [ NoUnsortedTopLevelDeclarations.rule
        (NoUnsortedTopLevelDeclarations.sortTopLevelDeclarations
            |> NoUnsortedTopLevelDeclarations.portsFirst
            |> NoUnsortedTopLevelDeclarations.exposedOrderWithPrivateLast
            |> NoUnsortedTopLevelDeclarations.alphabetically
        )
    ]

Fail

module A exposing
    ( A, a
    , Z
    )

{-|

@docs A, a
@docs Z

-}

type A
    = A

z =
    zed

type alias Z =
    A

a =
    foo

b =
    bar

Success

module A exposing
    ( A, a
    , Z
    )

{-|

@docs A, a
@docs Z

-}

type A
    = A

a =
    foo

type alias Z =
    A

b =
    bar

z =
    zed

When (not) to enable this rule

This rule is useful when you want to ensure that your top-level declarations are in a consistent, predictable order.

This rule is not useful when you want to be able to write top-level declarations in varying orders throughout your codebase, e.g. if you want to emphasize what is most important on a case-by-case basis.

Try it out

You can try this rule out by running the following command:

elm-review --template SiriusStarr/elm-review-no-unsorted/example --rules NoUnsortedTopLevelDeclarations

Configuration


type RuleConfig r

Configuration for this rule. Create a new one with sortTopLevelDeclarations and use orderings to create a hierarchy of sorting.

sortTopLevelDeclarations : RuleConfig { noAlphabetical : (), noDependency : (), noExposed : (), noHelper : (), noType : (), noPort : () }

Create a new RuleConfig. Use the various orderings to then specify primary and fallback orderings.

Orderings

alphabetically : RuleConfig { r | noAlphabetical : () } -> RuleConfig r

Sort declarations alphabetically. Note that this decapitalizes the first letter before performing the comparison so as to treat types and functions the same. For example, the following is sorted alphabetically:

type A
    = A

a =
    foo

b =
    bar

z =
    zed

type alias Z =
    A

exposedOrderWithPrivateLast : RuleConfig { r | noExposed : () } -> RuleConfig r

Sort TLDs in the order they are exposed by the module, with private TLDs coming after all those that are exposed. For example, the following is sorted by this and then alphabetically:

module A exposing
    ( A, a
    , Z
    )

{-|

@docs A, a
@docs Z

-}

type A
    = A

a =
    foo

type alias Z =
    A

b =
    bar

z =
    zed

exposedOrderWithPrivateFirst : RuleConfig { r | noExposed : () } -> RuleConfig r

Sort TLDs in the order they are exposed by the module, with private TLDs coming before all those that are exposed. For example, the following is sorted by this and then alphabetically:

module A exposing
    ( A, a
    , Z
    )

{-|

@docs A, a
@docs Z

-}

b =
    bar

z =
    zed

type A
    = A

a =
    foo

type alias Z =
    A

typesFirst : RuleConfig { r | noType : () } -> RuleConfig r

Sort TLDs so that types and type aliases always come before functions (and ports, if they haven't been sorted already). For example, the following is sorted by this order and then alphabetically:

type A
    = A

type alias Z =
    A

a =
    foo

b =
    bar

z =
    zed

typesLast : RuleConfig { r | noType : () } -> RuleConfig r

Sort TLDs so that types and type aliases always come after functions (and ports, if they haven't been sorted already). For example, the following is sorted by this order and then alphabetically:

a =
    foo

b =
    bar

z =
    zed

type A
    = A

type alias Z =
    A

portsFirst : RuleConfig { r | noPort : () } -> RuleConfig r

Sort TLDs so that ports always come before functions (and types, if they haven't been sorted already). For example, the following is sorted by this order and then alphabetically:

port sendMessage : String -> Cmd msg

type A
    = A

a =
    foo

b =
    bar

type alias Z =
    A

z =
    zed

portsLast : RuleConfig { r | noPort : () } -> RuleConfig r

Sort TLDs so that ports always come after functions (and types, if they haven't been sorted already). For example, the following is sorted by this order and then alphabetically:

type A
    = A

a =
    foo

b =
    bar

type alias Z =
    A

z =
    zed

port sendMessage : String -> Cmd msg

Glues

Glues provide a way to "stick" one declaration to another, i.e. to always sort one declaration alongside another. Note that glues will chain, i.e. if a is glued before b and b is glued after c, then the result will be c -> a -> b (sorted wherever c is sorted to). Glues behave in the following ways:

glueHelpersBefore : RuleConfig { r | noHelper : () } -> RuleConfig r

Helpers are unexposed functions that are used in exactly one other function. This glue attaches them immediately before the function they are used in.

For example:

foldrHelper : (a -> b -> b) -> b -> Int -> List a -> b
foldrHelper fn acc ctr ls =
    case ls of
        [] ->
            acc

        a :: r1 ->
            ...

{-| Reduce a list from the right.
-}
foldr : (a -> b -> b) -> b -> List a -> b
foldr fn acc ls =
    foldrHelper fn acc 0 ls

glueHelpersAfter : RuleConfig { r | noHelper : () } -> RuleConfig r

Helpers are unexposed functions that are used in exactly one other function. This glue attaches them immediately after the function they are used in.

For example:

{-| Reduce a list from the right.
-}
foldr : (a -> b -> b) -> b -> List a -> b
foldr fn acc ls =
    foldrHelper fn acc 0 ls

foldrHelper : (a -> b -> b) -> b -> Int -> List a -> b
foldrHelper fn acc ctr ls =
    case ls of
        [] ->
            acc

        a :: r1 ->
            ...

glueDependenciesBeforeFirstDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are unexposed functions that are used in multiple other functions. This glue attaches them immediately before the first function they are used in.

For example:

unwrap =
    some func

a x =
    unwrap x

b x =
    unwrap x

c x =
    unwrap x

glueDependenciesAfterFirstDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are unexposed functions that are used in multiple other functions. This glue attaches them immediately after the first function they are used in.

For example:

a x =
    unwrap x

unwrap =
    some func

b x =
    unwrap x

c x =
    unwrap x

glueDependenciesAfterLastDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are unexposed functions that are used in multiple other functions. This glue attaches them immediately after the last function they are used in.

For example:

a x =
    unwrap x

b x =
    unwrap x

c x =
    unwrap x

unwrap =
    some func

glueDependenciesBeforeLastDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are unexposed functions that are used in multiple other functions. This glue attaches them immediately before the last function they are used in.

For example:

a x =
    unwrap x

b x =
    unwrap x

unwrap =
    some func

c x =
    unwrap x