Can run this with
❯ elm-review --extract --report=json --rules ModuleUsage | jq -r '.extracts.ModuleUsage'
module ModuleUsage exposing (..) | |
import Dict exposing (Dict) | |
import Elm.Syntax.Expression as Expression exposing (Expression) | |
import Elm.Syntax.Import exposing (Import) | |
import Elm.Syntax.ModuleName exposing (ModuleName) | |
import Elm.Syntax.Node as Node exposing (Node) | |
import Json.Encode as Encode | |
import Review.ModuleNameLookupTable as ModuleNameLookupTable exposing (ModuleNameLookupTable) | |
import Review.Project.Dependency as Dependency exposing (Dependency) | |
import Review.Rule as Rule exposing (Rule) | |
import Set exposing (Set) | |
rule : Rule | |
rule = | |
Rule.newProjectRuleSchema "ModuleUsage" initContext | |
|> Rule.withDependenciesProjectVisitor dependencyVisitor | |
|> Rule.withModuleVisitor | |
(moduleVisitor | |
>> Rule.withExpressionEnterVisitor z | |
) | |
|> Rule.withModuleContextUsingContextCreator | |
{ fromProjectToModule = fromProjectToModule | |
, fromModuleToProject = fromModuleToProject | |
, foldProjectContexts = foldProjectContexts | |
} | |
|> Rule.withDataExtractor dataExtractor | |
|> Rule.fromProjectRuleSchema | |
z : Node Expression -> ModuleContext -> ( List (Rule.Error {}), ModuleContext ) | |
z node context = | |
case Node.value node of | |
Expression.FunctionOrValue _ identifier -> | |
case ModuleNameLookupTable.moduleNameFor context.lookupTable node of | |
Just m -> | |
( [] | |
, { context | |
| counts = | |
unionWith (\x y -> x + y) | |
context.counts | |
(Dict.singleton m 1) | |
, functionsOrValues = | |
unionWith (\x y -> x ++ y) | |
context.functionsOrValues | |
(Dict.singleton context.moduleName [ ( m, identifier ) ]) | |
} | |
) | |
Nothing -> | |
( [] | |
, { context | |
| counts = | |
unionWith (\x y -> x + y) | |
context.counts | |
(Dict.singleton [ "<MISSED>" ] 1) | |
} | |
) | |
_ -> | |
( [], context ) | |
type alias ProjectContext = | |
{ imports : Dict ModuleName (List ModuleName) | |
, dependencyModules : Set ModuleName | |
, counts : Dict ModuleName Int | |
, functionsOrValues : Dict ModuleName (List ( ModuleName, String )) | |
} | |
type alias ModuleContext = | |
{ imports : List ModuleName | |
, dependencyModules : Set ModuleName | |
, lookupTable : ModuleNameLookupTable | |
, counts : Dict ModuleName Int | |
, functionsOrValues : Dict ModuleName (List ( ModuleName, String )) | |
, moduleName : ModuleName | |
} | |
initContext : ProjectContext | |
initContext = | |
{ imports = Dict.empty | |
, dependencyModules = Set.empty | |
, counts = Dict.empty | |
, functionsOrValues = Dict.empty | |
} | |
dependencyVisitor : Dict String Dependency -> ProjectContext -> ( List never, ProjectContext ) | |
dependencyVisitor ds context = | |
Dict.values ds | |
|> List.concatMap Dependency.modules | |
|> List.map (String.split "." << .name) | |
|> Set.fromList | |
|> (\dModules -> ( [], { context | dependencyModules = dModules } )) | |
fromProjectToModule : Rule.ContextCreator ProjectContext ModuleContext | |
fromProjectToModule = | |
Rule.initContextCreator | |
(\moduleName lookupTable x projectContext -> | |
{ imports = [] | |
, dependencyModules = projectContext.dependencyModules | |
, lookupTable = lookupTable | |
, counts = projectContext.counts | |
, functionsOrValues = projectContext.functionsOrValues | |
, moduleName = moduleName | |
} | |
) | |
|> Rule.withModuleName | |
|> Rule.withModuleNameLookupTable | |
|> Rule.withFullAst | |
fromModuleToProject : Rule.ContextCreator ModuleContext ProjectContext | |
fromModuleToProject = | |
Rule.initContextCreator | |
(\moduleName moduleContext -> | |
{ imports = | |
if not (List.isEmpty moduleContext.imports) then | |
moduleContext.imports | |
|> List.sort | |
|> Dict.singleton moduleName | |
else | |
Dict.empty | |
, dependencyModules = moduleContext.dependencyModules | |
, counts = moduleContext.counts | |
, functionsOrValues = moduleContext.functionsOrValues | |
} | |
) | |
|> Rule.withModuleName | |
unionWith : (b -> b -> b) -> Dict comparable b -> Dict comparable b -> Dict comparable b | |
unionWith f a b = | |
-- (k ->a -> Dict k a -> Dict k a) -> (k -> a -> a -> Dict k a -> Dict k a) -> (k -> a -> Dict k a -> Dict k a) | |
-- -> Dict k a -> Dict k a -> Dict k a -> Dict k a | |
Dict.merge | |
(\k v d -> Dict.insert k v d) | |
(\k v1 v2 d -> Dict.insert k (f v1 v2) d) | |
(\k v d -> Dict.insert k v d) | |
a | |
b | |
Dict.empty | |
foldProjectContexts : ProjectContext -> ProjectContext -> ProjectContext | |
foldProjectContexts newContext previousContext = | |
{ imports = Dict.union newContext.imports previousContext.imports | |
, dependencyModules = previousContext.dependencyModules | |
, counts = unionWith (\x y -> x + y) newContext.counts previousContext.counts | |
, functionsOrValues = unionWith (\x y -> x ++ y) newContext.functionsOrValues previousContext.functionsOrValues | |
} | |
moduleVisitor : | |
Rule.ModuleRuleSchema {} ModuleContext | |
-> Rule.ModuleRuleSchema { hasAtLeastOneVisitor : () } ModuleContext | |
moduleVisitor schema = | |
schema | |
|> Rule.withImportVisitor importVisitor | |
dataExtractor : ProjectContext -> Encode.Value | |
dataExtractor projectContext = | |
-- projectContext.counts | |
-- |> Encode.dict unwrapModuleName Encode.int | |
projectContext.functionsOrValues | |
|> Encode.dict unwrapModuleName | |
(Encode.list | |
(\( m, s ) -> | |
Encode.object | |
[ ( "module", Encode.string (unwrapModuleName m) ) | |
, ( "functionOrValue", Encode.string s ) | |
] | |
) | |
) | |
-- |> Encode.int | |
-- (Encode.list (unwrapModuleName >> Encode.string)) | |
-- -- Encode.object | |
-- [ ( "json", | |
-- ] | |
-- [ ( "json", Encode.dict unwrapModuleName (Encode.list (unwrapModuleName >> Encode.string)) projectContext.imports ) | |
-- ] | |
unwrapModuleName : ModuleName -> String | |
unwrapModuleName = | |
String.join "." | |
importVisitor : Node Import -> ModuleContext -> ( List never, ModuleContext ) | |
importVisitor imp context = | |
let | |
moduleName : ModuleName | |
moduleName = | |
Node.value imp | |
|> .moduleName | |
|> Node.value | |
in | |
( [] | |
, { context | |
| imports = moduleName :: context.imports | |
} | |
) |