Store more information about the project's dependencies

This commit is contained in:
Jeroen Engels 2020-02-16 17:50:49 +01:00
parent a91d66ea60
commit c652a24d48
7 changed files with 185 additions and 55 deletions

View File

@ -10,13 +10,13 @@ module NoUnusedDependencies exposing (rule)
-}
import Dict exposing (Dict)
import Elm.Docs
import Elm.Package
import Elm.Project exposing (Project)
import Elm.Syntax.Import exposing (Import)
import Elm.Syntax.ModuleName exposing (ModuleName)
import Elm.Syntax.Node as Node exposing (Node)
import Elm.Syntax.Range exposing (Range)
import Review.Project
import Review.Rule as Rule exposing (Error, Rule)
import Set exposing (Set)
@ -65,7 +65,7 @@ moduleVisitorSchema schema =
|> Rule.withImportVisitor importVisitor
dependenciesVisitor : Dict String (List Elm.Docs.Module) -> ProjectContext -> ProjectContext
dependenciesVisitor : Dict String Review.Project.Dependency -> ProjectContext -> ProjectContext
dependenciesVisitor dependencies projectContext =
let
moduleNameToDependency : Dict String String
@ -73,9 +73,8 @@ dependenciesVisitor dependencies projectContext =
dependencies
|> Dict.toList
|> List.concatMap
(\( packageName, dependencyModules ) ->
List.map (\{ name } -> ( name, packageName ))
dependencyModules
(\( packageName, { modules } ) ->
List.map (\{ name } -> ( name, packageName )) modules
)
|> Dict.fromList
in

View File

@ -2,7 +2,7 @@ module Review.Project exposing
( Project, new
, ProjectModule, addModule, addParsedModule, removeModule, modules, filesThatFailedToParse, moduleGraph, precomputeModuleGraph
, withElmJson, elmJson
, withDependency, removeDependencies, dependencyModules
, Dependency, withDependency, removeDependencies, dependencies
)
{-| Represents the contents of the project to be analyzed. This information will
@ -33,7 +33,7 @@ in existing environments like the CLI tool.
# Project dependencies
@docs withDependency, removeDependencies, dependencyModules
@docs Dependency, withDependency, removeDependencies, dependencies
-}
@ -46,6 +46,7 @@ import Elm.Syntax.File exposing (File)
import Elm.Syntax.Module
import Elm.Syntax.ModuleName exposing (ModuleName)
import Elm.Syntax.Node as Node
import Elm.Version
import Graph exposing (Graph)
import Review.Dependencies
@ -62,7 +63,7 @@ type Project
{ modules : List ProjectModule
, filesThatFailedToParse : List { path : String, source : String }
, elmJson : Maybe { path : String, raw : String, project : Elm.Project.Project }
, dependencyModules : Dict String (List Elm.Docs.Module)
, dependencies : Dict String Dependency
, moduleGraph : Maybe (Graph ModuleName ())
}
@ -75,7 +76,7 @@ new =
{ modules = []
, filesThatFailedToParse = []
, elmJson = Nothing
, dependencyModules = Dict.empty
, dependencies = Dict.empty
, moduleGraph = Nothing
}
@ -302,6 +303,16 @@ elmJson (Project project) =
-- PROJECT DEPENDENCIES
{-| TODO Documentation
-}
type alias Dependency =
{ name : String
, version : Elm.Version.Version
, elmJson : Elm.Project.Project
, modules : List Elm.Docs.Module
}
{-| Add a dependency to the project. These will be available for rules to make
better assumptions on what is happening in the code.
@ -311,15 +322,15 @@ associativity of operators, which has an impact on the resulting AST when
parsing a file.
-}
withDependency : { r | packageName : String, modules : List Elm.Docs.Module } -> Project -> Project
withDependency : Dependency -> Project -> Project
withDependency dependency (Project project) =
Project
{ project
| dependencyModules =
| dependencies =
Dict.insert
dependency.packageName
dependency.modules
project.dependencyModules
dependency.name
dependency
project.dependencies
}
|> recomputeModuleGraphIfNeeded
@ -329,11 +340,12 @@ a project when they are changed, before re-adding them.
-}
removeDependencies : Project -> Project
removeDependencies (Project project) =
Project { project | dependencyModules = Dict.empty }
Project { project | dependencies = Dict.empty }
|> recomputeModuleGraphIfNeeded
{-| Get the modules for every dependency in the project.
{-| TODO Rewrite documentation
Get the modules for every dependency in the project.
This will give you a `Elm.Docs.Module` type from the
[`elm/project-metadata-utils`](https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.0/Elm-Docs)
@ -341,9 +353,9 @@ package, so you will need to install and use it to gain access to the dependency
information.
-}
dependencyModules : Project -> Dict String (List Elm.Docs.Module)
dependencyModules (Project project) =
project.dependencyModules
dependencies : Project -> Dict String Dependency
dependencies (Project project) =
project.dependencies

View File

@ -211,7 +211,6 @@ For more information on automatic fixing, read the documentation for [`Review.Fi
-}
import Dict exposing (Dict)
import Elm.Docs
import Elm.Project
import Elm.Syntax.Declaration as Declaration exposing (Declaration)
import Elm.Syntax.Expression as Expression exposing (Expression, Function)
@ -255,7 +254,7 @@ type ModuleRuleSchema configuration context
{ name : String
, initialContext : context
, elmJsonVisitors : List (Maybe Elm.Project.Project -> context -> context)
, dependenciesVisitors : List (Dict String (List Elm.Docs.Module) -> context -> context)
, dependenciesVisitors : List (Dict String Review.Project.Dependency -> context -> context)
, moduleDefinitionVisitors : List (Node Module -> context -> ( List Error, context ))
, commentsVisitors : List (List (Node String) -> context -> ( List Error, context ))
, importVisitors : List (Node Import -> context -> ( List Error, context ))
@ -562,7 +561,7 @@ runModuleRule ((ModuleRuleSchema schema) as moduleRuleSchema) previousCache proj
initialContext =
schema.initialContext
|> accumulateContext schema.elmJsonVisitors (Review.Project.elmJson project |> Maybe.map .project)
|> accumulateContext schema.dependenciesVisitors (Review.Project.dependencyModules project)
|> accumulateContext schema.dependenciesVisitors (Review.Project.dependencies project)
startCache : ModuleRuleCache moduleContext
startCache =
@ -678,7 +677,7 @@ type ProjectRuleSchema projectContext moduleContext
}
, moduleVisitorSchema : ModuleRuleSchema {} moduleContext -> ModuleRuleSchema { hasAtLeastOneVisitor : () } moduleContext
, elmJsonVisitors : List (Maybe { elmJsonKey : ElmJsonKey, project : Elm.Project.Project } -> projectContext -> projectContext)
, dependenciesVisitors : List (Dict String (List Elm.Docs.Module) -> projectContext -> projectContext)
, dependenciesVisitors : List (Dict String Review.Project.Dependency -> projectContext -> projectContext)
, finalEvaluationFns : List (projectContext -> List Error)
, traversalType : TraversalType
}
@ -748,7 +747,7 @@ withProjectElmJsonVisitor visitor (ProjectRuleSchema schema) =
{-| TODO documentation
-}
withProjectDependenciesVisitor :
(Dict String (List Elm.Docs.Module) -> projectContext -> projectContext)
(Dict String Review.Project.Dependency -> projectContext -> projectContext)
-> ProjectRuleSchema projectContext moduleContext
-> ProjectRuleSchema projectContext moduleContext
withProjectDependenciesVisitor visitor (ProjectRuleSchema schema) =
@ -827,7 +826,7 @@ runProjectRule ((ProjectRuleSchema schema) as wrappedSchema) startCache project
Nothing ->
Nothing
)
|> accumulateContext schema.dependenciesVisitors (Review.Project.dependencyModules project)
|> accumulateContext schema.dependenciesVisitors (Review.Project.dependencies project)
modules : Dict ModuleName ProjectModule
modules =
@ -1472,7 +1471,7 @@ withModuleElmJsonVisitor visitor (ModuleRuleSchema schema) =
{-| TODO
-}
withModuleDependenciesVisitor :
(Dict String (List Elm.Docs.Module) -> moduleContext -> moduleContext)
(Dict String Review.Project.Dependency -> moduleContext -> moduleContext)
-> ModuleRuleSchema { anything | withModuleDependenciesVisitor : () } moduleContext
-> ModuleRuleSchema { anything | withModuleDependenciesVisitor : () } moduleContext
withModuleDependenciesVisitor visitor (ModuleRuleSchema schema) =

View File

@ -33,6 +33,7 @@ import Elm.Syntax.Node as Node exposing (Node(..))
import Elm.Syntax.Pattern as Pattern exposing (Pattern)
import Elm.Syntax.Range as Range
import NonemptyList exposing (Nonempty)
import Review.Project
import Review.Rule as Rule exposing (Direction, Error)
@ -48,7 +49,7 @@ type alias InnerContext =
{ scopes : Nonempty Scope
, importAliases : Dict String (List String)
, importedFunctionOrTypes : Dict String (List String)
, dependencies : Dict String Elm.Docs.Module
, dependenciesModules : Dict String Elm.Docs.Module
}
@ -75,7 +76,7 @@ initialContext =
{ scopes = NonemptyList.fromElement emptyScope
, importAliases = Dict.empty
, importedFunctionOrTypes = Dict.empty
, dependencies = Dict.empty
, dependenciesModules = Dict.empty
}
@ -150,18 +151,18 @@ pairWithNoErrors fn visited context =
-- DEPENDENCIES
dependenciesVisitor : Dict String (List Elm.Docs.Module) -> InnerContext -> InnerContext
dependenciesVisitor : Dict String Review.Project.Dependency -> InnerContext -> InnerContext
dependenciesVisitor dependencies innerContext =
let
dependencyModules : Dict String Elm.Docs.Module
dependencyModules =
dependenciesModules : Dict String Elm.Docs.Module
dependenciesModules =
dependencies
|> Dict.values
|> List.concatMap identity
|> List.concatMap .modules
|> List.map (\dependencyModule -> ( dependencyModule.name, dependencyModule ))
|> Dict.fromList
in
{ innerContext | dependencies = dependencyModules }
{ innerContext | dependenciesModules = dependenciesModules }
|> registerPrelude
@ -367,7 +368,7 @@ registerExposed import_ innerContext =
module_ : Elm.Docs.Module
module_ =
Dict.get (getModuleName moduleName) innerContext.dependencies
Dict.get (getModuleName moduleName) innerContext.dependenciesModules
|> Maybe.withDefault
{ name = getModuleName moduleName
, comment = ""

View File

@ -40,6 +40,7 @@ import Elm.Syntax.Signature exposing (Signature)
import Elm.Syntax.TypeAnnotation as TypeAnnotation exposing (TypeAnnotation)
import Elm.Type
import NonemptyList exposing (Nonempty)
import Review.Project
import Review.Rule as Rule exposing (Direction, Error)
@ -71,7 +72,7 @@ type ProjectContext
type alias InnerProjectContext =
{ dependencies : Dict String Elm.Docs.Module
{ dependenciesModules : Dict String Elm.Docs.Module
, modules : Dict ModuleName Elm.Docs.Module
}
@ -84,7 +85,7 @@ type alias InnerModuleContext =
{ scopes : Nonempty Scope
, importAliases : Dict String (List String)
, importedFunctionOrTypes : Dict String (List String)
, dependencies : Dict String Elm.Docs.Module
, dependenciesModules : Dict String Elm.Docs.Module
, modules : Dict ModuleName Elm.Docs.Module
, exposesEverything : Bool
, exposedNames : Dict String { range : Range, exposedElement : ExposedElement }
@ -125,7 +126,7 @@ type alias ModuleSetterGetter context =
initProjectContext : ProjectContext
initProjectContext =
ProjectContext
{ dependencies = Dict.empty
{ dependenciesModules = Dict.empty
, modules = Dict.empty
}
@ -135,7 +136,7 @@ fromProjectToModule (ProjectContext projectContext) =
{ scopes = NonemptyList.fromElement emptyScope
, importAliases = Dict.empty
, importedFunctionOrTypes = Dict.empty
, dependencies = projectContext.dependencies
, dependenciesModules = projectContext.dependenciesModules
, modules = projectContext.modules
, exposesEverything = False
, exposedNames = Dict.empty
@ -151,7 +152,7 @@ fromProjectToModule (ProjectContext projectContext) =
fromModuleToProject : Node ModuleName -> ModuleContext -> ProjectContext
fromModuleToProject moduleName (ModuleContext moduleContext) =
ProjectContext
{ dependencies = moduleContext.dependencies
{ dependenciesModules = moduleContext.dependenciesModules
, modules =
Dict.insert (Node.value moduleName)
{ name = String.join "." (Node.value moduleName)
@ -168,7 +169,7 @@ fromModuleToProject moduleName (ModuleContext moduleContext) =
foldProjectContexts : ProjectContext -> ProjectContext -> ProjectContext
foldProjectContexts (ProjectContext a) (ProjectContext b) =
ProjectContext
{ dependencies = a.dependencies
{ dependenciesModules = a.dependenciesModules
, modules = Dict.union a.modules b.modules
}
@ -284,18 +285,18 @@ pairWithNoErrors fn visited context =
-- DEPENDENCIES
dependenciesVisitor : Dict String (List Elm.Docs.Module) -> InnerProjectContext -> InnerProjectContext
dependenciesVisitor : Dict String Review.Project.Dependency -> InnerProjectContext -> InnerProjectContext
dependenciesVisitor dependencies innerContext =
let
dependencyModules : Dict String Elm.Docs.Module
dependencyModules =
dependenciesModules : Dict String Elm.Docs.Module
dependenciesModules =
dependencies
|> Dict.values
|> List.concatMap identity
|> List.concatMap .modules
|> List.map (\dependencyModule -> ( dependencyModule.name, dependencyModule ))
|> Dict.fromList
in
{ innerContext | dependencies = dependencyModules }
{ innerContext | dependenciesModules = dependenciesModules }
registerPrelude : InnerModuleContext -> InnerModuleContext
@ -644,7 +645,7 @@ registerImportExposed import_ innerContext =
module_ : Elm.Docs.Module
module_ =
(case Dict.get (getModuleName moduleName) innerContext.dependencies of
(case Dict.get (getModuleName moduleName) innerContext.dependenciesModules of
Just m ->
Just m

View File

@ -1,12 +1,27 @@
module Dependencies exposing (elmCore, elmHtml)
import Elm.Docs
import Elm.Project
import Elm.Type as Type
import Elm.Version
import Json.Decode as Decode
import Review.Project
elmCore : { packageName : String, modules : List Elm.Docs.Module }
createElmJson : String -> Elm.Project.Project
createElmJson rawElmJson =
case Decode.decodeString Elm.Project.decoder rawElmJson of
Ok elmJson ->
elmJson
Err _ ->
createElmJson rawElmJson
elmCore : Review.Project.Dependency
elmCore =
{ packageName = "elm/core"
{ name = "elm/core"
, version = Elm.Version.one
, modules =
[ { name = "Basics"
, comment = ""
@ -66,12 +81,54 @@ elmCore =
]
}
]
, elmJson = createElmJson """
{
"type": "package",
"name": "elm/core",
"summary": "Elm's standard libraries",
"license": "BSD-3-Clause",
"version": "1.0.4",
"exposed-modules": {
"Primitives": [
"Basics",
"String",
"Char",
"Bitwise",
"Tuple"
],
"Collections": [
"List",
"Dict",
"Set",
"Array"
],
"Error Handling": [
"Maybe",
"Result"
],
"Debug": [
"Debug"
],
"Effects": [
"Platform.Cmd",
"Platform.Sub",
"Platform",
"Process",
"Task"
]
},
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {},
"test-dependencies": {}
}
"""
}
elmHtml : { packageName : String, modules : List Elm.Docs.Module }
elmHtml : Review.Project.Dependency
elmHtml =
{ packageName = "elm/html"
{ name = "elm/html"
, version = Elm.Version.one
, modules =
[ { name = "Html"
, comment = ""
@ -93,4 +150,31 @@ elmHtml =
, binops = []
}
]
, elmJson = createElmJson """
{
"type": "package",
"name": "elm/html",
"summary": "Fast HTML, rendered with virtual DOM diffing",
"license": "BSD-3-Clause",
"version": "1.0.0",
"exposed-modules": {
"HTML": [
"Html",
"Html.Attributes",
"Html.Events"
],
"Optimize": [
"Html.Keyed",
"Html.Lazy"
]
},
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm/core": "1.0.0 <= v < 2.0.0",
"elm/json": "1.0.0 <= v < 2.0.0",
"elm/virtual-dom": "1.0.0 <= v < 2.0.0"
},
"test-dependencies": {}
}
"""
}

View File

@ -1,7 +1,7 @@
module NoUnusedDependenciesTest exposing (all)
import Elm.Docs
import Elm.Project
import Elm.Version
import Json.Decode as Decode
import NoUnusedDependencies exposing (rule)
import Review.Project as Project exposing (Project)
@ -76,9 +76,10 @@ packageElmJson =
}"""
packageWithFoo : { packageName : String, modules : List Elm.Docs.Module }
packageWithFoo : Project.Dependency
packageWithFoo =
{ packageName = "author/package-with-foo"
{ name = "author/package-with-foo"
, version = Elm.Version.one
, modules =
[ { name = "Foo"
, comment = ""
@ -88,12 +89,29 @@ packageWithFoo =
, binops = []
}
]
, elmJson = .project <| createElmJson """
{
"type": "package",
"name": "author/package-with-foo",
"summary": "Summary",
"license": "BSD-3-Clause",
"version": "1.0.0",
"exposed-modules": [
"Foo"
],
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm/core": "1.0.0 <= v < 2.0.0"
},
"test-dependencies": {}
}"""
}
packageWithBar : { packageName : String, modules : List Elm.Docs.Module }
packageWithBar : Project.Dependency
packageWithBar =
{ packageName = "author/package-with-bar"
{ name = "author/package-with-bar"
, version = Elm.Version.one
, modules =
[ { name = "Bar"
, comment = ""
@ -103,6 +121,22 @@ packageWithBar =
, binops = []
}
]
, elmJson = .project <| createElmJson """
{
"type": "package",
"name": "author/package-with-bar",
"summary": "Summary",
"license": "BSD-3-Clause",
"version": "1.0.0",
"exposed-modules": [
"Bar"
],
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm/core": "1.0.0 <= v < 2.0.0"
},
"test-dependencies": {}
}"""
}