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.
|
|
|
|
|
|
|
|
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
|
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
|