Add Documentation.ReadmeLinksPointToCurrentVersion rule

This commit is contained in:
Jeroen Engels 2020-04-04 00:31:27 +02:00
parent 1143afeb31
commit a932f65fe9
3 changed files with 257 additions and 2 deletions

View File

@ -18,5 +18,7 @@
"elm-explorations/test": "1.2.2 <= v < 2.0.0",
"stil4m/elm-syntax": "7.1.0 <= v < 8.0.0"
},
"test-dependencies": {}
}
"test-dependencies": {
"elm/regex": "1.0.0 <= v < 2.0.0"
}
}

View File

@ -0,0 +1,114 @@
module Documentation.ReadmeLinksPointToCurrentVersion exposing (rule)
{-| Reports links to this project's package documentation on <https://package.elm-lang.org/>,
where the version is set to `latest` or a different version than the current version of the package.
**NOTE**: Just make sure to run tests between bumping the Elm version and publishing the package,
otherwise the link for a given version could link to a previous one.
-}
import Elm.Package
import Elm.Project
import Elm.Syntax.Range exposing (Range)
import Elm.Version
import Regex exposing (Regex)
import Review.Rule as Rule exposing (Error, Rule)
rule : Rule
rule =
Rule.newProjectRuleSchema "ReadmeLinksPointToCurrentVersion" initialProjectContext
|> Rule.withElmJsonProjectVisitor elmJsonVisitor
|> Rule.withReadmeProjectVisitor readmeVisitor
|> Rule.fromProjectRuleSchema
type alias ProjectContext =
Maybe
{ projectName : String
, version : String
}
initialProjectContext : ProjectContext
initialProjectContext =
Nothing
-- elm.json VISITOR
elmJsonVisitor : Maybe { a | project : Elm.Project.Project } -> ProjectContext -> ( List nothing, ProjectContext )
elmJsonVisitor maybeProject _ =
case maybeProject |> Maybe.map .project of
Just (Elm.Project.Package pkg) ->
( []
, Just
{ projectName = Elm.Package.toString pkg.name
, version = Elm.Version.toString pkg.version
}
)
_ ->
( [], Nothing )
-- README VISITOR
readmeVisitor : Maybe { readmeKey : Rule.ReadmeKey, content : String } -> ProjectContext -> ( List (Error scope), ProjectContext )
readmeVisitor maybeReadme maybeContext =
case ( maybeReadme, maybeContext ) of
( Just { readmeKey, content }, Just context ) ->
( findRangeForSubstring context readmeKey content, maybeContext )
_ ->
( [], maybeContext )
linkRegex : Regex
linkRegex =
Regex.fromString "]\\(https://package\\.elm-lang\\.org/packages/([\\w-]+/[\\w-]+)/(\\w+(\\.\\w+\\.\\w+)?)(.*)\\)"
|> Maybe.withDefault Regex.never
findRangeForSubstring : { projectName : String, version : String } -> Rule.ReadmeKey -> String -> List (Error scope)
findRangeForSubstring context readmeKey content =
content
|> String.lines
|> List.indexedMap Tuple.pair
|> List.concatMap
(\( row, line ) ->
Regex.find linkRegex line
|> List.filterMap (notAMatch context readmeKey row)
)
notAMatch : { projectName : String, version : String } -> Rule.ReadmeKey -> Int -> Regex.Match -> Maybe (Error scope)
notAMatch { projectName, version } readmeKey row match =
case match.submatches of
(Just authorAndPackage) :: (Just linkVersion) :: _ ->
if authorAndPackage == projectName && linkVersion /= version then
Rule.errorForReadme readmeKey
{ message = "Link does not point to the current version of the package"
, details = [ "I suggest to run elm-review in fix mode" ]
}
{ start =
{ row = row + 1
, column = match.index + 3
}
, end =
{ row = row + 1
, column = match.index + String.length match.match
}
}
|> Just
else
Nothing
_ ->
Nothing

View File

@ -0,0 +1,139 @@
module Documentation.ReadmeLinksPointToCurrentVersionTest exposing (all)
import Documentation.ReadmeLinksPointToCurrentVersion exposing (rule)
import Elm.Project
import Json.Decode as Decode
import Review.Project as Project exposing (Project)
import Review.Test exposing (ReviewResult)
import Test exposing (Test, describe, test)
testRule : Project -> ReviewResult
testRule project =
"""module SomeModule exposing (a)
a = 1"""
|> Review.Test.runWithProjectData project rule
createElmJson : String -> { path : String, raw : String, project : Elm.Project.Project }
createElmJson rawElmJson =
case Decode.decodeString Elm.Project.decoder rawElmJson of
Ok elmJson ->
{ path = "elm.json"
, raw = rawElmJson
, project = elmJson
}
Err _ ->
Debug.todo "Invalid elm.json supplied to test"
packageElmJson : String -> String
packageElmJson name =
"""
{
"type": "package",
"name": \""""
++ name
++ """",
"summary": "Summary",
"license": "BSD-3-Clause",
"version": "1.2.3",
"exposed-modules": [
"Exposed"
],
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm/core": "1.0.0 <= v < 2.0.0"
},
"test-dependencies": {}
}"""
message : String
message =
"Link does not point to the current version of the package"
details : List String
details =
[ "I suggest to run elm-review in fix mode" ]
readmeWithLink : String -> String
readmeWithLink link =
"""
# My project
- [my project's thing](""" ++ link ++ """)
Don't report:
- [this](https://package.elm-lang.org/packages/author/other-package/latest/Module-Name)
- [this](https://package.elm-lang.org/packages/author/other-package/1.2.2/Module-Name)
- [this](https://package.elm-lang.org/packages/other-author/package/latest/Module-Name)
- [this](https://package.elm-lang.org/packages/other-author/package/1.2.4/Module-Name)
"""
all : Test
all =
describe "ReadmeLinksPointToCurrentVersion"
[ test "should not report an error if there is no elm.json file" <|
\() ->
Project.new
|> Project.addReadme { path = "README.md", content = readmeWithLink "https://package.elm-lang.org/packages/author/package/1.2.4/Module-Name" }
|> testRule
|> Review.Test.expectNoErrors
, test "should not report an error if there is no README file" <|
\() ->
Project.new
|> Project.addElmJson (createElmJson <| packageElmJson "author/package")
|> testRule
|> Review.Test.expectNoErrors
, test "should not report an error if all the links point to the current project use the correct version" <|
\() ->
Project.new
|> Project.addElmJson (createElmJson <| packageElmJson "author/package")
|> Project.addReadme { path = "README.md", content = readmeWithLink "https://package.elm-lang.org/packages/author/package/1.2.3/Module-Name" }
|> testRule
|> Review.Test.expectNoErrors
, test "should report an error if a link points to a different version" <|
\() ->
Project.new
|> Project.addElmJson (createElmJson <| packageElmJson "author/package")
|> Project.addReadme { path = "README.md", content = readmeWithLink "https://package.elm-lang.org/packages/author/package/1.2.4/Module-Name" }
|> testRule
|> Review.Test.expectErrorsForReadme
[ Review.Test.error
{ message = message
, details = details
, under = "https://package.elm-lang.org/packages/author/package/1.2.4/Module-Name"
}
]
, test "should report an error if a link points to latest" <|
\() ->
Project.new
|> Project.addElmJson (createElmJson <| packageElmJson "author/package")
|> Project.addReadme { path = "README.md", content = readmeWithLink "https://package.elm-lang.org/packages/author/package/latest/Module-Name" }
|> testRule
|> Review.Test.expectErrorsForReadme
[ Review.Test.error
{ message = message
, details = details
, under = "https://package.elm-lang.org/packages/author/package/latest/Module-Name"
}
]
, test "should report an error even if the author or package name contains a dash or digit" <|
\() ->
Project.new
|> Project.addElmJson (createElmJson <| packageElmJson "au-tho5r/pack-age1")
|> Project.addReadme { path = "README.md", content = readmeWithLink "https://package.elm-lang.org/packages/au-tho5r/pack-age1/latest/Module-Name" }
|> testRule
|> Review.Test.expectErrorsForReadme
[ Review.Test.error
{ message = message
, details = details
, under = "https://package.elm-lang.org/packages/au-tho5r/pack-age1/latest/Module-Name"
}
|> Review.Test.whenFixed (readmeWithLink "https://package.elm-lang.org/packages/au-tho5r/pack-age1/1.2.3/Module-Name")
]
]