glinter
A linter for the Gleam programming language. It parses Gleam source files into ASTs using glance and checks them against a configurable set of rules. Many rules are based on the official Gleam conventions.
Installation
gleam add --dev glinterUsage
# Lint the src/ directory (default)
gleam run -m glinter
# Lint specific files or directories
gleam run -m glinter src/myapp/ test/
# JSON output
gleam run -m glinter --format json
# Show stats (files, lines, timing)
gleam run -m glinter --stats
# Lint a different project (resolves gleam.toml and paths relative to project dir)
gleam run -m glinter --project /path/to/my/project
The exit code is 1 if any issues are found, 0 otherwise.
Multi-Package Projects
For monorepos with multiple Gleam packages, add glinter as a dev dependency to one package (e.g. server) and lint each package separately using --project:
# From any directory, lint each package
gleam run -m glinter --project server
gleam run -m glinter --project client
gleam run -m glinter --project shared
Each package uses its own gleam.toml for configuration. You can wrap this in a script to lint all packages at once:
#!/bin/sh
# bin/lint
set -e
for pkg in server client shared; do
echo "Linting $pkg..."
gleam run -m glinter --project "$pkg"
doneRules
Code Quality
- avoid_panic (error): flags uses of
panic - avoid_todo (error): flags uses of
todo - echo (warning): flags uses of
echo(debug output) - assert_ok_pattern (warning): flags
let assertassignments - unwrap_used (warning): flags
result.unwrap,option.unwrap, and lazy variants - todo_without_message (warning): flags
todowithout a descriptive message - panic_without_message (warning): flags
panicwithout a descriptive message - string_inspect (warning): flags
string.inspectusage (debug output)
Error Handling
- error_context_lost (warning): flags
result.replace_error(which always discards the original error) andresult.map_errorcalls where the callback discards the original error withfn(_) { ... } - stringly_typed_error (warning): flags functions with
Result(x, String)return types — use a custom error type instead - thrown_away_error (warning): flags
Error(_)patterns in case expressions that discard the error value. Can be noisy in codebases with many fallback patterns — disable withthrown_away_error = "off"in config.
Type Annotations
- missing_type_annotation (warning): flags functions missing return type annotations or with untyped parameters
Style
- discarded_result (warning): flags
let _ = expr(discarded results) - short_variable_name (warning): flags single-character variable names in let bindings
- unnecessary_variable (warning): flags
let x = expr; x(assigned then immediately returned) - redundant_case (warning): flags
caseexpressions with a single branch and no guard - prefer_guard_clause (warning): flags
case bool { True -> ... False -> ... }patterns that could usebool.guard - unnecessary_string_concatenation (warning): flags concatenation with an empty string (
x <> "") and concatenation of two string literals ("foo" <> "bar") - trailing_underscore (warning): flags function names ending with
_
Complexity
- deep_nesting (warning): flags nesting deeper than 5 levels
- function_complexity (off): flags functions with more than 10 branching nodes. Off by default — branch count doesn't correlate with readability (routers, state machines, parsers are naturally branchy). Enable with
function_complexity = "warning"in config. - module_complexity (off): flags modules with more than 100 total branching nodes. Off by default — large cohesive modules are idiomatic Gleam. Enable with
module_complexity = "warning"in config.
Labels
- label_possible (warning): flags unlabeled parameters in functions with 2+ parameters
- missing_labels (warning): flags calls to same-module functions that omit defined labels
Imports
- unqualified_import (warning): flags unqualified function/constant imports (e.g.
import mod.{func}). Gleam convention is to use qualified access (mod.func). Constructor imports (Some,None,Ok, etc.) and type imports are not flagged. - duplicate_import (warning): flags importing the same module more than once in a file
Cross-Module
- unused_exports (warning): flags
pubfunctions, constants, and types never referenced from another module. Test files count as consumers,mainis excluded.
FFI
- ffi_usage (off): flags use of Gleam's private JS data API in
.mjsfiles — numeric property access (value[0],tuple.0), internal constructor checks ($constructor), runtime imports (gleam.mjs), and internal helpers (makeError,isEqual,CustomType, etc.). These representations can change between compiler versions. Off by default — enable if your project includes JS FFI. Scans.mjsfiles in configured source directories only. Community feedback and PRs welcome to improve pattern detection.
Configuration
Configuration lives in your project's gleam.toml under the [tools.glinter] key:
[tools.glinter]
stats = true # show file count, line count, and timing after each run
include = ["src/", "test/"] # directories to lint (default: ["src/"])
exclude = ["src/server/sql.gleam"] # skip generated files entirely
[tools.glinter.rules]
avoid_panic = "error"
avoid_todo = "error"
echo = "warning"
assert_ok_pattern = "warning"
discarded_result = "warning"
short_variable_name = "warning"
unnecessary_variable = "warning"
redundant_case = "warning"
unwrap_used = "warning"
deep_nesting = "warning"
function_complexity = "off" # off by default
module_complexity = "off" # off by default
prefer_guard_clause = "warning"
missing_labels = "warning"
label_possible = "warning"
unused_exports = "warning"
missing_type_annotation = "warning"
todo_without_message = "warning"
unqualified_import = "warning"
panic_without_message = "warning"
string_inspect = "warning"
duplicate_import = "warning"
unnecessary_string_concatenation = "warning"
trailing_underscore = "warning"
error_context_lost = "warning"
stringly_typed_error = "warning"
thrown_away_error = "warning"
ffi_usage = "off" # off by default
Each rule can be set to "error", "warning", or "off".
Excluding Files
Skip files entirely (useful for generated code). Supports globs:
[tools.glinter]
exclude = ["src/server/sql.gleam", "src/generated/**/*.gleam"]Ignoring Rules Per File
Suppress specific rules for files where they don't make sense. Also supports globs:
[tools.glinter.ignore]
"src/my_complex_module.gleam" = ["deep_nesting", "function_complexity"]
"test/**/*.gleam" = ["assert_ok_pattern", "short_variable_name", "missing_type_annotation", "label_possible", "missing_labels", "unqualified_import"]Output Formats
Text (default)
src/app.gleam:12: [error] avoid_panic: Use of 'panic' is discouraged
src/app.gleam:25: [warning] echo: Use of 'echo' is discouragedJSON
gleam run -m glinter --format json{
"results": [
{
"rule": "avoid_panic",
"severity": "error",
"file": "src/app.gleam",
"line": 12,
"message": "Use of 'panic' is discouraged"
}
],
"summary": {
"total": 1,
"errors": 1,
"warnings": 0
}
}
When --stats is enabled, a stats object is included:
{
"stats": {
"files": 23,
"lines": 2544,
"elapsed_ms": 45
}
}Running Tests
gleam testLicense
MIT