Skip to content

Commit 488ba0b

Browse files
authored
Merge pull request #35 from lambdaisland/laurence/include-clj-kondo-hook-in-resource
Export clj-kondo hooks for library consumers
2 parents 63a12f1 + 50e4dff commit 488ba0b

4 files changed

Lines changed: 98 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Added
44

5+
- Export clj-kondo hooks and config via `resources/clj-kondo.exports/` so library
6+
consumers can activate them by running
7+
`clj-kondo --copy-configs --dependencies --lint classpath`
8+
59
## Fixed
610

711
## Changed

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -945,11 +945,26 @@ meta-merge). e.g. `{:colors ^:replace {...}}`)
945945

946946
## clj-kondo Support
947947

948-
This library includes a built-in clj-kondo configuration to improve linting for Ornament components.
948+
Ornament ships clj-kondo hooks and configuration that validate `defstyled` forms — catching bad tag names, invalid syntax, and providing correct `def`-like analysis.
949949

950-
To use the configuration, simply copy the .clj-kondo/ directory from this repository into the root of your project.
950+
The hooks are bundled in the JAR under `clj-kondo.exports/`. To activate them in your project:
951951

952-
After copying the directory, clj-kondo will automatically use this configuration to provide more accurate linting and reduce false positives in your Ornament projects.
952+
1. Ensure a `.clj-kondo` directory exists at your project root:
953+
```
954+
mkdir -p .clj-kondo
955+
```
956+
957+
2. Copy the configs from your dependencies:
958+
```
959+
clj-kondo --lint "$(clojure -Spath)" --copy-configs --skip-lint
960+
```
961+
962+
3. Warm the linting cache:
963+
```
964+
clj-kondo --lint "$(clojure -Spath)" --dependencies --parallel
965+
```
966+
967+
After that, clj-kondo will automatically validate `defstyled` forms in your project. Consider checking the copied configs into version control.
953968

954969
## Babashka compatibility
955970

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{:lint-as {lambdaisland.ornament/defprop clojure.core/def
2+
lambdaisland.ornament/defrules clojure.core/def}
3+
:hooks {:analyze-call {lambdaisland.ornament/defstyled hooks.ornament/defstyled}}
4+
:linters {:lambdaisland.ornament/invalid-syntax
5+
{:level :warning}}}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
(ns hooks.ornament
2+
(:require [clj-kondo.hooks-api :as api]))
3+
4+
(defonce styled-registry (atom #{}))
5+
6+
(defn- symbol-node? [node]
7+
(and (api/token-node? node)
8+
(symbol? (api/sexpr node))))
9+
10+
(defn- styled-component? [node]
11+
(and (symbol-node? node)
12+
(contains? @styled-registry (api/sexpr node))))
13+
14+
(defn defstyled [{:keys [node]}]
15+
(let [[class-name html-tag & more] (rest (:children node))
16+
_ (when-not (and (symbol-node? class-name)
17+
(simple-symbol? (api/sexpr class-name)))
18+
(api/reg-finding! {:row (:row (meta class-name))
19+
:col (:col (meta class-name))
20+
:message "Style name must be a simple symbol"
21+
:type :lambdaisland.ornament/invalid-syntax}))
22+
_ (when-not (or (api/keyword-node? html-tag)
23+
(styled-component? html-tag))
24+
(api/reg-finding! {:row (:row (meta html-tag))
25+
:col (:col (meta html-tag))
26+
:message "Tag must be a keyword or an ornament-styled-component"
27+
:type :lambdaisland.ornament/invalid-syntax}))
28+
fn-tag (first (drop-while (fn [x]
29+
(or (api/string-node? x)
30+
(api/keyword-node? x)
31+
(api/map-node? x)
32+
(api/vector-node? x)
33+
(api/token-node? x)))
34+
more))
35+
_ (when (and fn-tag
36+
(not (api/list-node? fn-tag)))
37+
(api/reg-finding! {:row (:row (meta fn-tag))
38+
:col (:col (meta fn-tag))
39+
:message "Function part (if present) must be a list"
40+
:type :lambdaisland.ornament/invalid-syntax}))
41+
symbol-tag? (symbol-node? html-tag)]
42+
;; Register this component in the styled-registry
43+
(swap! styled-registry conj (api/sexpr class-name))
44+
(if (api/list-node? fn-tag)
45+
(let [[binding-vec & body] (:children fn-tag)
46+
fn-node (api/list-node
47+
(list*
48+
(api/token-node 'fn)
49+
binding-vec
50+
body))
51+
new-def-node (api/list-node
52+
(if symbol-tag?
53+
(list (api/token-node 'def)
54+
class-name
55+
(api/list-node
56+
(list (api/token-node 'do)
57+
html-tag
58+
fn-node)))
59+
(list (api/token-node 'def)
60+
class-name
61+
fn-node)))]
62+
{:node new-def-node})
63+
(let [def-class-form (api/list-node
64+
(if symbol-tag?
65+
(list (api/token-node 'def)
66+
class-name
67+
html-tag)
68+
(list (api/token-node 'def)
69+
class-name
70+
(api/token-node 'nil))))]
71+
{:node def-class-form}))))

0 commit comments

Comments
 (0)