Initial commit
This commit is contained in:
362
skills/clj-kondo/QUICK_REFERENCE.md
Normal file
362
skills/clj-kondo/QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,362 @@
|
||||
# clj-kondo Quick Reference
|
||||
|
||||
## Command Line
|
||||
|
||||
```bash
|
||||
# Basic linting
|
||||
clj-kondo --lint <path>
|
||||
clj-kondo --lint src test
|
||||
|
||||
# Output formats
|
||||
clj-kondo --lint src --config '{:output {:format :json}}'
|
||||
clj-kondo --lint src --config '{:output {:format :edn}}'
|
||||
|
||||
# Cache dependencies
|
||||
clj-kondo --lint "$(clojure -Spath)" --dependencies --parallel --copy-configs
|
||||
|
||||
# Version
|
||||
clj-kondo --version
|
||||
```
|
||||
|
||||
## Configuration File Location
|
||||
|
||||
```
|
||||
.clj-kondo/config.edn # Project config
|
||||
~/.config/clj-kondo/config.edn # Global config
|
||||
```
|
||||
|
||||
## Basic Configuration
|
||||
|
||||
```clojure
|
||||
{:linters {:unused-binding {:level :warning}
|
||||
:unused-namespace {:level :warning}
|
||||
:unresolved-symbol {:level :error}
|
||||
:invalid-arity {:level :error}
|
||||
:misplaced-docstring {:level :warning}}
|
||||
:output {:pattern "{{LEVEL}} {{filename}}:{{row}}:{{col}} {{message}}"
|
||||
:exclude-files ["generated/" "resources/public/js/"]}}
|
||||
```
|
||||
|
||||
## Linter Levels
|
||||
|
||||
```clojure
|
||||
:off ; Disable
|
||||
:info ; Informational
|
||||
:warning ; Warning (default)
|
||||
:error ; Error (fails build)
|
||||
```
|
||||
|
||||
## Common Linter Configurations
|
||||
|
||||
### Disable Specific Linters
|
||||
|
||||
```clojure
|
||||
{:linters {:unused-binding {:level :off}
|
||||
:unused-private-var {:level :off}}}
|
||||
```
|
||||
|
||||
### Exclude Namespaces
|
||||
|
||||
```clojure
|
||||
{:linters {:unused-binding {:exclude-ns [myapp.test-helpers
|
||||
myapp.dev]}}}
|
||||
```
|
||||
|
||||
### Consistent Aliases
|
||||
|
||||
```clojure
|
||||
{:linters {:consistent-alias {:level :warning
|
||||
:aliases {clojure.string str
|
||||
clojure.set set
|
||||
clojure.java.io io}}}}
|
||||
```
|
||||
|
||||
### Refer :all
|
||||
|
||||
```clojure
|
||||
{:linters {:refer-all {:level :warning
|
||||
:exclude [clojure.test]}}}
|
||||
```
|
||||
|
||||
### Macro as Function
|
||||
|
||||
```clojure
|
||||
{:lint-as {myapp/my-macro clojure.core/let
|
||||
myapp/defroute compojure.core/defroutes}}
|
||||
```
|
||||
|
||||
## Inline Suppressions
|
||||
|
||||
```clojure
|
||||
;; Suppress for entire namespace
|
||||
(ns myapp.core
|
||||
{:clj-kondo/config '{:linters {:unused-binding {:level :off}}}})
|
||||
|
||||
;; Suppress specific linters for form
|
||||
#_{:clj-kondo/ignore [:unused-binding :unresolved-symbol]}
|
||||
(let [x 1] (undefined-fn))
|
||||
|
||||
;; Suppress all linters for form
|
||||
#_{:clj-kondo/ignore true}
|
||||
(problematic-code)
|
||||
|
||||
;; Suppress with underscore prefix
|
||||
(let [_unused-var 42] ;; No warning
|
||||
...)
|
||||
```
|
||||
|
||||
## Hook Types
|
||||
|
||||
### analyze-call Hook
|
||||
|
||||
Analyze function/macro calls:
|
||||
|
||||
```clojure
|
||||
;; config.edn
|
||||
{:hooks {:analyze-call {mylib/deprecated-fn hooks.my/warn-deprecated}}}
|
||||
|
||||
;; hooks/my.clj
|
||||
(ns hooks.my
|
||||
(:require [clj-kondo.hooks-api :as api]))
|
||||
|
||||
(defn warn-deprecated [{:keys [node]}]
|
||||
{:findings [{:message "This function is deprecated"
|
||||
:type :deprecated-api
|
||||
:row (api/row node)
|
||||
:col (api/col node)
|
||||
:level :warning}]})
|
||||
```
|
||||
|
||||
### macroexpand Hook
|
||||
|
||||
Transform macro for analysis:
|
||||
|
||||
```clojure
|
||||
;; config.edn
|
||||
{:hooks {:macroexpand {mylib/defentity hooks.my/expand-defentity}}}
|
||||
|
||||
;; hooks/my.clj
|
||||
(defn expand-defentity [{:keys [node]}]
|
||||
(let [[_ name & body] (:children node)]
|
||||
{:node (api/list-node
|
||||
[(api/token-node 'def)
|
||||
name
|
||||
(api/map-node body)])}))
|
||||
```
|
||||
|
||||
## Hook API - Node Functions
|
||||
|
||||
```clojure
|
||||
;; Query
|
||||
(api/tag node) ; :list, :vector, :map, :token, etc.
|
||||
(api/sexpr node) ; Convert to s-expression
|
||||
(:children node) ; Get child nodes
|
||||
(api/string node) ; String representation
|
||||
|
||||
;; Position
|
||||
(api/row node)
|
||||
(api/col node)
|
||||
(api/end-row node)
|
||||
(api/end-col node)
|
||||
|
||||
;; Predicates
|
||||
(api/token-node? node)
|
||||
(api/keyword-node? node)
|
||||
(api/string-node? node)
|
||||
(api/list-node? node)
|
||||
(api/vector-node? node)
|
||||
(api/map-node? node)
|
||||
```
|
||||
|
||||
## Hook API - Node Constructors
|
||||
|
||||
```clojure
|
||||
;; Atoms
|
||||
(api/token-node 'symbol)
|
||||
(api/keyword-node :keyword)
|
||||
(api/string-node "string")
|
||||
(api/number-node 42)
|
||||
|
||||
;; Collections
|
||||
(api/list-node [node1 node2 node3])
|
||||
(api/vector-node [node1 node2])
|
||||
(api/map-node [k1 v1 k2 v2])
|
||||
(api/set-node [node1 node2])
|
||||
```
|
||||
|
||||
## Hook Return Values
|
||||
|
||||
```clojure
|
||||
;; Return findings
|
||||
{:findings [{:message "Error message"
|
||||
:type :my-custom-type
|
||||
:row (api/row node)
|
||||
:col (api/col node)
|
||||
:level :warning}]}
|
||||
|
||||
;; Return transformed node
|
||||
{:node new-node}
|
||||
|
||||
;; Return both
|
||||
{:node new-node
|
||||
:findings [{:message "..." ...}]}
|
||||
```
|
||||
|
||||
## Common Hook Patterns
|
||||
|
||||
### Deprecation Warning
|
||||
|
||||
```clojure
|
||||
(defn deprecation [{:keys [node]}]
|
||||
{:findings [{:message "Use new-api instead of old-api"
|
||||
:type :deprecated
|
||||
:row (api/row node)
|
||||
:col (api/col node)
|
||||
:level :warning}]})
|
||||
```
|
||||
|
||||
### Argument Count Validation
|
||||
|
||||
```clojure
|
||||
(defn validate-arity [{:keys [node]}]
|
||||
(let [args (rest (:children node))]
|
||||
(when (< (count args) 2)
|
||||
{:findings [{:message "Expected at least 2 arguments"
|
||||
:type :invalid-arity
|
||||
:row (api/row node)
|
||||
:col (api/col node)
|
||||
:level :error}]})))
|
||||
```
|
||||
|
||||
### Argument Type Validation
|
||||
|
||||
```clojure
|
||||
(defn validate-type [{:keys [node]}]
|
||||
(let [first-arg (second (:children node))]
|
||||
(when-not (api/keyword-node? first-arg)
|
||||
{:findings [{:message "First argument must be a keyword"
|
||||
:type :invalid-argument-type
|
||||
:row (api/row node)
|
||||
:col (api/col node)
|
||||
:level :error}]})))
|
||||
```
|
||||
|
||||
### Required Map Keys
|
||||
|
||||
```clojure
|
||||
(defn validate-keys [{:keys [node]}]
|
||||
(let [map-node (second (:children node))]
|
||||
(when (api/map-node? map-node)
|
||||
(let [keys (->> (:children map-node)
|
||||
(take-nth 2)
|
||||
(map api/sexpr)
|
||||
(set))
|
||||
required #{:host :port}
|
||||
missing (clojure.set/difference required keys)]
|
||||
(when (seq missing)
|
||||
{:findings [{:message (str "Missing keys: " missing)
|
||||
:type :missing-keys
|
||||
:row (api/row node)
|
||||
:col (api/col node)
|
||||
:level :error}]})))))
|
||||
```
|
||||
|
||||
### Macro Expansion for DSL
|
||||
|
||||
```clojure
|
||||
(defn expand-defroute [{:keys [node]}]
|
||||
(let [[_ method path handler] (:children node)]
|
||||
{:node (api/list-node
|
||||
[(api/token-node 'def)
|
||||
(api/token-node (gensym "route"))
|
||||
(api/map-node [method path handler])])}))
|
||||
```
|
||||
|
||||
## IDE Integration
|
||||
|
||||
### VS Code (Calva)
|
||||
Built-in support, works automatically.
|
||||
|
||||
### Emacs
|
||||
```elisp
|
||||
(use-package flycheck-clj-kondo :ensure t)
|
||||
```
|
||||
|
||||
### Vim/Neovim (ALE)
|
||||
```vim
|
||||
let g:ale_linters = {'clojure': ['clj-kondo']}
|
||||
```
|
||||
|
||||
### IntelliJ/Cursive
|
||||
Native integration via Preferences → Editor → Inspections.
|
||||
|
||||
## CI/CD Patterns
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
- name: Install clj-kondo
|
||||
run: |
|
||||
curl -sLO https://raw.githubusercontent.com/clj-kondo/clj-kondo/master/script/install-clj-kondo
|
||||
chmod +x install-clj-kondo
|
||||
./install-clj-kondo
|
||||
- name: Run clj-kondo
|
||||
run: clj-kondo --lint src test
|
||||
```
|
||||
|
||||
### GitLab CI
|
||||
|
||||
```yaml
|
||||
lint:
|
||||
image: cljkondo/clj-kondo:latest
|
||||
script:
|
||||
- clj-kondo --lint src test
|
||||
```
|
||||
|
||||
### Pre-commit Hook
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
clj-kondo --lint src test
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### False Positive - Macro Generated Symbol
|
||||
|
||||
```clojure
|
||||
{:lint-as {myapp/my-macro clojure.core/let}}
|
||||
```
|
||||
|
||||
### Exclude Generated Files
|
||||
|
||||
```clojure
|
||||
{:output {:exclude-files ["generated/" "target/" "node_modules/"]}}
|
||||
```
|
||||
|
||||
### Hook Not Triggering
|
||||
|
||||
1. Check hook registration in config.edn
|
||||
2. Verify namespace path matches
|
||||
3. Test with minimal example
|
||||
4. Run with `--debug` flag
|
||||
|
||||
### Performance Issues
|
||||
|
||||
```bash
|
||||
# Cache dependencies once
|
||||
clj-kondo --lint "$(clojure -Spath)" --dependencies --parallel
|
||||
|
||||
# Exclude large directories
|
||||
{:output {:exclude-files ["resources/public/"]}}
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- Start with zero config - defaults are good
|
||||
- Use `--copy-configs` to get library-specific rules
|
||||
- Write hooks for domain-specific linting
|
||||
- Test hooks with minimal examples
|
||||
- Document why you suppress warnings
|
||||
- Run in CI to catch issues early
|
||||
- Cache dependency analysis for speed
|
||||
Reference in New Issue
Block a user