elm-review/tests/NoExposingEverything.elm

138 lines
4.3 KiB
Elm
Raw Normal View History

2020-04-08 01:14:03 +03:00
module NoExposingEverything exposing (rule)
{-|
@docs rule
-}
2020-12-25 19:23:07 +03:00
import Elm.Syntax.Declaration as Declaration exposing (Declaration)
2020-04-08 01:14:03 +03:00
import Elm.Syntax.Exposing as Exposing
2020-12-25 19:23:07 +03:00
import Elm.Syntax.Expression as Expression
2020-04-08 01:14:03 +03:00
import Elm.Syntax.Module as Module exposing (Module)
2020-12-25 19:23:07 +03:00
import Elm.Syntax.Node as Node exposing (Node(..))
import Elm.Syntax.Range exposing (Range)
import Review.Fix as Fix
2020-04-08 01:14:03 +03:00
import Review.Rule as Rule exposing (Error, Rule)
{-| Forbids exporting everything from a module.
2021-08-19 21:50:50 +03:00
🔧 Running with `--fix` will automatically fix all the reported errors.
2020-04-08 01:14:03 +03:00
Modules should have hidden implementation details with an explicit API so that the module is used in a proper and controlled way.
The users of this module should not have to know about what is inside a module it is using, and they shouldn't need to access its internal details.
Therefore, the API should be explicitly defined and ideally as small as possible.
config =
[ NoExposingEverything.rule
]
If you would like to expose everything in your tests, you can configure the rule
in the following manner:
config =
[ NoExposingEverything.rule
|> Rule.ignoreErrorsForDirectories [ "tests/" ]
]
## Fail
module A exposing (..)
## Success
module A exposing (B(..), C, d)
2020-08-09 19:55:15 +03:00
## Try it out
You can try this rule out by running the following command:
```bash
2020-09-22 20:40:30 +03:00
elm-review --template jfmengels/elm-review-common/example --rules NoExposingEverything
2020-08-09 19:55:15 +03:00
```
2020-04-08 01:14:03 +03:00
-}
rule : Rule
rule =
2020-12-25 19:23:07 +03:00
Rule.newModuleRuleSchema "NoExposingEverything" ExposingOk
|> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor
|> Rule.withDeclarationListVisitor declarationListVisitor
|> Rule.providesFixesForModuleRule
2020-06-28 08:49:27 +03:00
|> Rule.fromModuleRuleSchema
2020-04-08 01:14:03 +03:00
2020-12-25 19:23:07 +03:00
type ExposingContext
= ExposingOk
| ExposingAll Range
moduleDefinitionVisitor : Node Module -> ExposingContext -> ( List (Error {}), ExposingContext )
moduleDefinitionVisitor moduleNode _ =
2020-04-08 01:14:03 +03:00
case Module.exposingList <| Node.value moduleNode of
Exposing.All range ->
2020-12-25 19:23:07 +03:00
( [], ExposingAll range )
2020-04-08 01:14:03 +03:00
Exposing.Explicit _ ->
2020-12-25 19:23:07 +03:00
( [], ExposingOk )
declarationListVisitor : List (Node Declaration) -> ExposingContext -> ( List (Error {}), ExposingContext )
declarationListVisitor declarations context =
case context of
ExposingAll range ->
( [ Rule.errorWithFix
{ message = "Module exposes everything implicitly \"(..)\""
, details =
2021-04-06 18:32:30 +03:00
[ "Modules should have hidden implementation details with an explicit API so that the module is used in a proper and controlled way. The users of this module should not have to know about what is inside a module it is using, and they shouldn't need to access its internal details. Therefore, the API should be explicitly defined and ideally as small as possible."
2020-12-25 19:23:07 +03:00
]
}
{ start = { row = range.start.row, column = range.start.column - 1 }
, end = { row = range.end.row, column = range.end.column + 1 }
}
[ exposingDeclarationList declarations
|> String.join ", "
|> Fix.replaceRangeBy range
]
]
, context
)
_ ->
( [], context )
exposingDeclarationList : List (Node Declaration) -> List String
exposingDeclarationList declarations =
List.map exposingDeclarationName declarations
exposingDeclarationName : Node Declaration -> String
exposingDeclarationName (Node _ declaration) =
case declaration of
Declaration.AliasDeclaration { name } ->
Node.value name
Declaration.CustomTypeDeclaration { name } ->
Node.value name ++ "(..)"
Declaration.FunctionDeclaration function ->
functionDeclarationName function
Declaration.InfixDeclaration { operator } ->
"(" ++ Node.value operator ++ ")"
Declaration.PortDeclaration { name } ->
Node.value name
Declaration.Destructuring _ _ ->
""
functionDeclarationName : Expression.Function -> String
functionDeclarationName { declaration } =
declaration |> Node.value |> .name |> Node.value