Git Product home page Git Product logo

elm-review-imports's Introduction

elm-review-imports

elm package elm-review 2.10 elm 0.19 Tests

Provides elm-review rules to enforce consistent import aliases.

Provided rules

Configuration

import NoInconsistentAliases
import NoModuleOnExposedNames
import Review.Rule exposing (Rule)

config : List Rule
config =
    [ NoInconsistentAliases.config
        [ ( "Html.Attributes", "Attr" )
        , ( "Json.Decode", "Decode" )
        , ( "Json.Encode", "Encode" )
        ]
        |> NoInconsistentAliases.noMissingAliases
        |> NoInconsistentAliases.rule
    , NoModuleOnExposedNames.rule
    ]

Try it out

You can try the example configuration above out by running the following command:

elm-review --template sparksp/elm-review-imports/example

elm-review-imports's People

Contributors

dependabot[bot] avatar sparksp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

elm-review-imports's Issues

Enforce preferred names

Changing an alias across your whole project is menial and time consuming. You will be able to configure the rule with a mapping of modules to aliases to be enforced. We will offer code fixes to apply the correct aliases.

Draft configuration

config : List Rule
config =
    [ NoInconsistentAliases.config
        [ ( "Html.Attributes", "Attr" )
        , ( "Json.Encode", "Encode" )
        , ( "Json.Decode", "Decode" )
        ]
        |> NoInconsistentAliases.rule
    ]

Shadowing when aliasing a type with the same name

Proposed fix:

-- (fix) NoModuleOnExposedNames: Module used on exposed type `Dict`.

type alias Dict k v =
    Internal.Dict k v
    ^^^^^^^^^^^^^

It is not necessary to use the module here as `Dict` was exposed on import.

You should remove the module from this call, or remove the name from the import
.. exposing list.

Except it is in fact necessary to do that (and exposing the type is done to import the constructors/variants, so can't be avoided)

Enforce consistency for every alias

We will check every import alias across the project to ensure they are aliased consistently, and report any that are inconsistent. We will try to report only the errant aliases. For example, if you've imported Html.Attributes as Attr fifteen times and as A once then we'll report just that one.

Draft configuration

config : List Rule
config =
    [ NoInconsistentAliases.config []
        |> NoInconsistentAliases.discoverAliases
        |> NoInconsistentAliases.rule
    ]

Discussion: Allow multiple aliases?

At GlobalWebIndex we have our own custom ConsistentImports rule.

It allows multiple aliases (we need this because of us having a convention import List.Extra as List, and Html.Attributes.Extra.autocomplete colliding with Html.Attributes.autocomplete, which makes us need to import the Extra as Attrs_ instead of Attrs in the modules that use the autocomplete value) - described more in the Elm Slack discussion.

We wanted to publish our rule, then found out this package exists, so we'd love to not divide the ecosystem.
Would you consider allowing multiple aliases for a given imported module in elm-review-imports?

Enforce use of an alias when one is known

If you've established an alias for a module somewhere in your project and then try to use it bare in another file (without an alias) then we will report the bare import and try to offer a fix.

--- [File 1] Pass: Alias is established
import Html.Attributes as Attr

--- [File 2] Fail: No alias has been used
import Html.Attributes

container children =
    Html.div [ Html.Attributes.class "container" ] children

--- [File 3] Pass: Alias is not necessary
import Html.Attributes exposing (class)

view = div [ class "flex" ] []

Draft configuration

config : List Rule
config =
    [ NoInconsistentAliases.config []
        |> NoInconsistentAliases.discoverAliases
        |> NoInconsistentAliases.noMissingAliases
        |> NoInconsistentAliases.rule
    ]

Alternate way to configure consistent alias rule

Currently the consistent alias rule takes a list of module names and the alias they should each use. What if instead the rule took a function with the type signature ModuleName -> Alias where Alias is

type Alias 
    = DontCare
    | AlwaysNoAlias
    | AlwaysAlias String

Edit: To elaborate on why this is useful, in the code we have at work, we have a lot of modules and I'd like to be able to do something like, all elm-ui modules shouldn't be aliased. Or all modules under Shared.* should use the last part of their module name as an alias.

Applying the fix doesn't always update instances of Module.Constructor

Given two modules

module Main exposing (main)

import Example
import Html


main : Html.Html msg
main =
    Html.text <|
        case Just Example.Foo of
            Just Example.Foo ->
                "just foo"

            Just Example.Bar ->
                "just bar"

            Nothing ->
                "nothing"

and

module Example exposing (FooBar(..))


type FooBar
    = Foo
    | Bar

and an elm-review config of

module ReviewConfig exposing (config)

import NoInconsistentAliases
import Review.Rule exposing (Rule)


config : List Rule
config =
    [ NoInconsistentAliases.config
        [ ( "Example", "E" ) ]
        |> NoInconsistentAliases.noMissingAliases
        |> NoInconsistentAliases.rule
    ]

applying the fix converts Main.elm to

module Main exposing (main)

import Example as E
import Html


main : Html.Html msg
main =
    Html.text <|
        case Just E.Foo of
            Just Example.Foo ->
                "foo"

            Just Example.Bar ->
                "bar"

            Nothing ->
                "nothing"

Expected

Just Example.Foo to be changed to Just E.Foo

Actual

Just Example.Foo isn't modified

Ensure Consistent Aliases

Based on some discussions with @jfmengels here's an outline of what this rule should achieve:

To ensure consistent use of import aliases throughout the project.

As a project evolves so might your code style, but as you move between files you should not need to keep track of what the "correct" alias is. This gets harder the bigger the project or team. Consider the following three imports. They're all valid, and could have been written by the same author at sometime:

--- [File 1]
import Html.Attributes as Attributes

--- [File 2]
import Html.Attributes as A

--- [File 3]
import Html.Attributes as Attr

Requirements

False negative on `Json.Decode` when `Json.Decode.Extra` import is also present

This is on:

  • jfmengels/elm-review 2.8.1
  • sparksp/elm-review-imports 1.0.1

The files below result in a false negative: the import Json.Decode as Json is not picked upon (should warn and offer a fix to rename the alias to as Decode):

Screenshot 2022-08-15 at 10 07 24

Note that if the import of Json.Decode.Extra in Main.elm is removed, the false negative disappears and the rule suddenly offers to rename the Json.Decode alias!


Review config:

module ReviewConfig exposing (config)

import NoInconsistentAliases
import Review.Rule exposing (Rule)

config : List Rule
config =
    [ NoInconsistentAliases.config
        [ ( "Json.Decode", "Decode" )
        , ( "Json.Decode.Extra", "Decode" )
        ]
        |> NoInconsistentAliases.noMissingAliases
        |> NoInconsistentAliases.rule
    ]

Main.elm:

module Main exposing (x)

import Json.Decode as Json
import Json.Decode.Extra as Json

x = 1

Collision detection with preferred aliases

What should we do if there's a collision of aliases within a module? Consider the following imports:

--[ src/Somefile.elm ]
import Html.Attributes as A
import Svg.Attributes as Attr

Scenario 1 - Pass

--[ review/src/ReviewConfig.elm ]
NoInconsistentAliases.config
    [ ( "Svg.Attributes", "Attr" )
    ]
    |> NoInconsistentAliases.rule

There's nothing to do here, we already meet the config.

Scenario 2 - Collision

--[ review/src/ReviewConfig.elm ]
NoInconsistentAliases.config
    [ ( "Html.Attributes", "Attr" )
    ]
    |> NoInconsistentAliases.rule

The rule says that A is wrong for Html.Attributes, but our preferred alias Attr is already taken. We don't have a preference for Svg.Attributes so we could report Html.Attributes and mention that the preferred alias is already taken. We would not be able to provide an automated fix for this.

Solution: Report Html.Attributes with no fix.

Scenario 3 - Fail

--[ review/src/ReviewConfig.elm ]
NoInconsistentAliases.config
    [ ( "Html.Attributes", "Attr" )
    , ( "Svg.Attributes", "SvgAttr" )
    ]
    |> NoInconsistentAliases.rule

The rule says that both aliases are wrong, however we can't offer a fix for Html.Attributes because the preferred alias is already taken. We can offer a fix for Svg.Attributes, which would clear the way to fix Html.Attributes.

Solution: Report Html.Attributes with no fix (as Scenario 2), and report Svg.Attributes with a fix (as normal).

Scenario 4 - Double Collision

--[ review/src/ReviewConfig.elm ]
NoInconsistentAliases.config
    [ ( "Html.Attributes", "Attr" )
    , ( "Svg.Attributes", "Attr" )
    ]
    |> NoInconsistentAliases.rule

The rule says that A is wrong for Html.Attributes, but our preferred alias Attr is already taken by another preferred alias. If the user fixes Html.Attributes then we will have a problem with Svg.Attributes instead.

Solution: No Report

Scenario 5 - Double Fail

--[ review/src/ReviewConfig.elm ]
NoInconsistentAliases.config
    [ ( "Html.Attributes", "Attributes" )
    , ( "Svg.Attributes", "Attributes" )
    ]
    |> NoInconsistentAliases.rule

The rule says that both aliases are wrong. We can report both of them and offer fixes for each - when a fix is applied for either then we'll be in the same situation as Scenario 4.

Solution: Report both aliases as normal.

Scenario 6 - Collision with bare module

--[ src/Somefile.elm ]
import Attr
import Html.Attributes as A
--[ review/src/ReviewConfig.elm ]
NoInconsistentAliases.config
    [ ( "Html.Attributes", "Attr" )
    ]
    |> NoInconsistentAliases.rule

The rule says that A is wrong for Html.Attributes, but our preferred alias Attr is already taken by a bare module import (no alias). If the user fixes Html.Attributes then they will have to alias module Attr instead.

Solution: No Report

Scenario 7 - Collision with bare module

--[ src/Somefile.elm ]
import Attributes
import Html.Attributes as A
--[ review/src/ReviewConfig.elm ]
NoInconsistentAliases.config
    [ ( "Attributes", "Attr" )
    , ( "Html.Attributes", "Attributes" )
    ]
    |> NoInconsistentAliases.rule

This is Scenario 3 again - the only difference is that Attributes is a bare module. We don't need to do anything different here - when Attributes is correctly aliased then a fix can be offered for Html.Attributes.

Solution: Report Attributes with a fix (as normal), and report Html.Attributes with no fix (as Scenario 2).

Summary

  1. Do not report a bad alias if the preferred alias is taken by another matching preferred alias.
  2. Do not report a bad alias if the preferred alias is taken by an import with no alias (singular, bare module).
  3. Report a bad alias if the preferred alias is taken by an aliased module with no preferred alias, but do not offer a fix.

Do not allow exposed names to be qualified

If a function or type has been exposed on import do not allow it to be called via the module name/alias. The user should either remove the call from the import's exposes or remove the module name from the call.

Failure

Here class is exposed but is still called by Attr.class.

import Html.Attributes as Attr exposing (class)

view children =
    div [ Attr.class "container" ] children

Success

-import Html.Attributes as Attr exposing (class)
+import Html.Attributes as Attr

 view children =
     div [ Attr.class "container" ] children

Success

 import Html.Attributes as Attr exposing (class)

 view children =
-    div [ Attr.class "container" ] children
+    div [ class "container" ] children

Failure

Here Attribute has been exposed but is still called by Html.Attribute.

import Html exposing (Html, Attribute)

container : List (Html.Attribute msg) -> List (Html msg) -> Html msg
container attributes children =
    div attributes children

Success

-import Html exposing (Html, Attribute)
+import Html exposing (Html)

 container : List (Html.Attribute msg) -> List (Html msg) -> Html msg
 container attributes children =
     div attributes children

Success

 import Html exposing (Html, Attribute)

-container : List (Html.Attribute msg) -> List (Html msg) -> Html msg
+container : List (Attribute msg) -> List (Html msg) -> Html msg
 container attributes children =
     div attributes children

NoModuleOnExposedName fix can introduce name collision

In the following rule, NoModuleOnExposedName will suggest a fix by replacing convert : Color -> Element.Color with convert : Color -> Color which will lead to a compile error. It should instead suggest removing Color from exposing or not suggest any fix in this case.

import Element exposing (Color)

type Color = Red | Green | Blue

convert : Color -> Element.Color
convert color =
    ...

Edit: I think the correct behavior would be to not show an error at all. Consider this case:

import Subcomponent exposing (Msg(..))

type Msg = A | B

foo : Subcomponent.Msg
foo = 
   MsgVariantInSubComponent

Subcomponent.Msg can't be changed to Msg due to a name collision but exposing (Msg(..)) can't be removed either because of MsgVariantInSubComponent.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.