Forbid the use of exposed elements (functions, values or types) that are never used in your project.
🔧 Running with --fix
will automatically remove all the reported errors,
except for the ones reported when using reportUnusedProductionExports
.
It won't automatically remove unused modules though.
If the project is a package and the module that declared the element is exposed, then nothing will be reported.
The behavior of the rule also depends on whether the analyzed module is exposing elements explicitly (module X exposing (A, b)
)
or exposing everything (module X exposing (..)
).
When exposing elements explicitly, the rule will report and remove elements from the
exposing clause (exposing (used, unused)
to exposing (used)
) when they're never used in other Elm files of the project.
When exposing all, the rule will report and remove the declaration of elements
if they're used neither in the file they're declared in nor in any other files,
making it act somewhat like NoUnused.Variables
,
complementing it because NoUnused.Variables
doesn't report
top-level declarations when the module is exposing everything.
rule : Review.Rule.Rule
Report functions and types that are exposed from a module but that are never used in other modules. Also reports when a module is entirely unused.
config =
[ NoUnused.Exports.rule
]
This is equivalent to NoUnused.Exports.toRule NoUnused.Exports.defaults
.
This rule can be configured to report more unused elements than the default configuration.
Configuration for the rule. Use defaults
to get a default configuration and use toRule
to turn it into a rule.
You can change the configuration using reportUnusedProductionExports
.
defaults : Configuration
Default configuration. This will only report exported elements that are never used in other modules.
reportUnusedProductionExports : { isProductionFile : { moduleName : Elm.Syntax.ModuleName.ModuleName, filePath : String, isInSourceDirectories : Basics.Bool } -> Basics.Bool, exceptionsAre : List Exception } -> Configuration -> Configuration
Configures the rule to report elements defined in production code but only used in non-production files.
import NoUnused.Exports exposing (annotatedBy)
config =
[ NoUnused.Exports.defaults
|> NoUnused.Exports.reportUnusedProductionExports
{ isProductionFile =
\{ moduleName, filePath, isInSourceDirectories } ->
isInSourceDirectories
&& not (String.endsWith "/Example.elm" filePath)
, exceptionsAre = [ annotatedBy "@test-helper" ]
}
|> NoUnused.Exports.toRule
]
Elements reported using this configuration won't be automatically fixed as they require removing the code that uses the element.
This function needs to know two things:
Which files are considered to be production files, which is determined by a function that you provide.
Generally, production files are in the "source-directories"
, which is indicated by
isInSourceDirectories
(given as an argument to the function) being True
. If you want to exclude
more files, you can use the filePath
or moduleName
of the Elm module, whichever is more practical for you to use.
filePath
is relative to the folder containing the elm.json
file and is written in a UNIX format (/
, no \
).
How to identify exceptions. See Exception
for more information.
Predicate to identify exceptions (that shouldn't be reported) for elements defined in production code that are only used in non-production code.
A problem with reporting these elements is that it's going to produce false positives, as there are legitimate use-cases for exporting these elements, hence the need for the rule to be able to identify them.
For instance, while it's generally discouraged, you might want to test the internals of an API (to make sure some properties hold given very specific situations). In this case, your module then needs to expose a way to gain insight to the internals.
Another example is giving the means for tests to create opaque types that are impossible or very hard to create in a test environment. This can be the case for types that can only be created through the decoding of an HTTP request.
Note that another common way to handle these use-cases is to move the internals to another module that exposes everything while making sure only specific production modules import it.
annotatedBy : String -> Exception
Prevents reporting usages of elements that contain a specific tag in their documentation.
Given the following configuration
NoUnused.Exports.defaults
|> NoUnused.Exports.reportUnusedProductionExports
{ isProductionFile = isProductionFile
, exceptionsAre = [ annotatedBy "@test-helper" ]
}
|> NoUnused.Exports.toRule
any element that has @test-helper
in its documentation will not be reported as unused (as long as its used at least once in the project):
{-| @test-helper
-}
someFunction input =
doSomethingComplexWith input
A recommended practice is to have annotations start with @
.
You can use this function several times to define multiple annotations.
suffixedBy : String -> Exception
Prevents reporting usages of elements whose name end with a specific string.
Given the following configuration
NoUnused.Exports.defaults
|> NoUnused.Exports.reportUnusedProductionExports
{ isProductionFile = isProductionFile
, exceptionsAre = [ suffixedBy "_FOR_TESTS" ]
}
|> NoUnused.Exports.toRule
any element that ends with "_FOR_TESTS"
will not be reported as unused (as long as its used at least once in the project):
someFunction_FOR_TESTS input =
doSomethingComplexWith input
You can use this function several times to define multiple suffixes.
prefixedBy : String -> Exception
Prevents reporting usages of elements whose name start with a specific string.
Given the following configuration
NoUnused.Exports.defaults
|> NoUnused.Exports.reportUnusedProductionExports
{ isProductionFile = isProductionFile
, exceptionsAre = [ prefixedBy "test_" ]
}
|> NoUnused.Exports.toRule
any element that starts with "test_"
will not be reported as unused (as long as its used at least once in the project):
test_someFunction input =
doSomethingComplexWith input
You can use this function several times to define multiple prefixes.
definedInModule : ({ moduleName : Elm.Syntax.ModuleName.ModuleName, filePath : String } -> Basics.Bool) -> Exception
Prevents reporting usages of elements in some modules.
Given the following configuration
NoUnused.Exports.defaults
|> NoUnused.Exports.reportUnusedProductionExports
{ isProductionFile = isProductionFile
, exceptionsAre =
[ definedInModule
(\{ moduleName, filePath } ->
List.member "Util" moduleName
|| String.startsWith "src/test-helpers/" filePath
)
]
}
|> NoUnused.Exports.toRule
no elements from modules named *.Util.*
or modules inside src/test-helpers/
will be reported.
The provided filePath
is relative to the project's elm.json
and is in a UNIX style (/
, no \
).
You can try this rule out by running the following commands:
Using the default configuration:
elm-review --template jfmengels/elm-review-unused/example --rules NoUnused.Exports
Using reportUnusedProductionExports
with the following configuration:
NoUnused.Exports.defaults
|> NoUnused.Exports.reportUnusedProductionExports
{ isProductionFile = \{ moduleName, filePath, isInSourceDirectories } -> isInSourceDirectories
, exceptionsAre = [ annotatedBy "