Skip to content

Commit 34cd85c

Browse files
committed
Add JSON schema for AI tools config
Signed-off-by: Gabriel Nordeborn <gabbe.nord@gmail.com>
1 parent e3d1097 commit 34cd85c

5 files changed

Lines changed: 300 additions & 1 deletion

File tree

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "ReScript AI Tools Config",
4+
"description": "Configuration for `rescript-tools` AI-oriented lint and rewrite commands. This schema covers both the recommended namespaced shape (`lint.rules` and `rewrite.rules`) and the older top-level `rules` shortcut for lint-only configs.",
5+
"type": "object",
6+
"properties": {
7+
"$schema": {
8+
"type": "string",
9+
"description": "Optional schema URI or path for editor support."
10+
},
11+
"lint": {
12+
"$ref": "#/definitions/lint-config"
13+
},
14+
"rewrite": {
15+
"$ref": "#/definitions/rewrite-config"
16+
},
17+
"rules": {
18+
"$ref": "#/definitions/lint-rules",
19+
"description": "Deprecated lint-only shortcut. Prefer `lint.rules` for new configs."
20+
}
21+
},
22+
"additionalProperties": false,
23+
"definitions": {
24+
"severity": {
25+
"type": "string",
26+
"enum": ["error", "warning"],
27+
"description": "Finding severity. Defaults depend on the rule."
28+
},
29+
"enabled": {
30+
"type": "boolean",
31+
"description": "Whether the rule is enabled. Default: true."
32+
},
33+
"message": {
34+
"type": "string",
35+
"description": "Optional custom message. When omitted, the built-in default message for that rule is used."
36+
},
37+
"forbidden-reference-kind": {
38+
"type": "string",
39+
"enum": ["module", "value", "type"],
40+
"description": "Which kind of symbol path is being forbidden."
41+
},
42+
"forbidden-reference-item": {
43+
"type": "object",
44+
"description": "A single forbidden symbol target. Item-level `message` overrides the enclosing rule message when this item matches.",
45+
"properties": {
46+
"kind": {
47+
"$ref": "#/definitions/forbidden-reference-kind"
48+
},
49+
"path": {
50+
"type": "string",
51+
"description": "Dot-separated symbol path such as `Belt.Array.map` or `Js.Json.t`."
52+
},
53+
"message": {
54+
"$ref": "#/definitions/message"
55+
}
56+
},
57+
"required": ["kind", "path"],
58+
"additionalProperties": false
59+
},
60+
"forbidden-reference-rule": {
61+
"type": "object",
62+
"description": "Report references to configured modules, values, or types. Module items also match nested references beneath that module path.",
63+
"properties": {
64+
"enabled": {
65+
"$ref": "#/definitions/enabled"
66+
},
67+
"severity": {
68+
"$ref": "#/definitions/severity"
69+
},
70+
"message": {
71+
"$ref": "#/definitions/message"
72+
},
73+
"items": {
74+
"type": "array",
75+
"description": "Targets to forbid. Multiple items can share one severity and default message.",
76+
"items": {
77+
"$ref": "#/definitions/forbidden-reference-item"
78+
}
79+
}
80+
},
81+
"additionalProperties": false
82+
},
83+
"forbidden-reference-config": {
84+
"description": "Either one forbidden-reference rule object or an array of rule objects. Arrays let you group distinct severities or default messages.",
85+
"oneOf": [
86+
{
87+
"$ref": "#/definitions/forbidden-reference-rule"
88+
},
89+
{
90+
"type": "array",
91+
"items": {
92+
"$ref": "#/definitions/forbidden-reference-rule"
93+
}
94+
}
95+
]
96+
},
97+
"single-use-function-rule": {
98+
"type": "object",
99+
"description": "Report same-file local functions that are defined once and used once. Default severity: warning.",
100+
"properties": {
101+
"enabled": {
102+
"$ref": "#/definitions/enabled"
103+
},
104+
"severity": {
105+
"$ref": "#/definitions/severity"
106+
},
107+
"message": {
108+
"$ref": "#/definitions/message"
109+
}
110+
},
111+
"additionalProperties": false
112+
},
113+
"alias-avoidance-rule": {
114+
"type": "object",
115+
"description": "Report pass-through aliases such as `let alias = Module.value`, `type alias = Module.t`, or `module Alias = Long.Module.Path`. Default severity: warning.",
116+
"properties": {
117+
"enabled": {
118+
"$ref": "#/definitions/enabled"
119+
},
120+
"severity": {
121+
"$ref": "#/definitions/severity"
122+
},
123+
"message": {
124+
"$ref": "#/definitions/message"
125+
}
126+
},
127+
"additionalProperties": false
128+
},
129+
"forbidden-source-root-reference-kind": {
130+
"type": "string",
131+
"enum": ["value", "type"],
132+
"description": "Kinds that can be checked by declaration source root. Modules are not supported by this rule yet."
133+
},
134+
"forbidden-source-root-reference-rule": {
135+
"type": "object",
136+
"description": "Report references whose declarations come from configured source roots. Root paths are resolved relative to the config file. The rule does not report when the current file is also inside the matching forbidden root.",
137+
"properties": {
138+
"enabled": {
139+
"$ref": "#/definitions/enabled"
140+
},
141+
"severity": {
142+
"$ref": "#/definitions/severity"
143+
},
144+
"message": {
145+
"$ref": "#/definitions/message"
146+
},
147+
"roots": {
148+
"type": "array",
149+
"description": "Source folder roots to block, such as `src/generated` or `src/__generated__`.",
150+
"items": {
151+
"type": "string"
152+
}
153+
},
154+
"kinds": {
155+
"type": "array",
156+
"description": "Declaration kinds to block from those roots. Default: `[\"value\", \"type\"]`.",
157+
"items": {
158+
"$ref": "#/definitions/forbidden-source-root-reference-kind"
159+
}
160+
}
161+
},
162+
"additionalProperties": false
163+
},
164+
"preferred-type-syntax-lint-rule": {
165+
"type": "object",
166+
"description": "Prefer canonical builtin type syntax where available. Today this supports the `dict<_>` preference over `Dict.t<_>` and `Stdlib.Dict.t<_>`. Default severity: warning.",
167+
"properties": {
168+
"enabled": {
169+
"$ref": "#/definitions/enabled"
170+
},
171+
"severity": {
172+
"$ref": "#/definitions/severity"
173+
},
174+
"message": {
175+
"$ref": "#/definitions/message"
176+
},
177+
"dict": {
178+
"type": "boolean",
179+
"description": "When true, report `Dict.t<_>` and `Stdlib.Dict.t<_>` in favor of `dict<_>`. Default: false."
180+
}
181+
},
182+
"additionalProperties": false
183+
},
184+
"lint-rules": {
185+
"type": "object",
186+
"description": "Configured lint rules. Omitted rules keep their built-in defaults.",
187+
"properties": {
188+
"forbidden-reference": {
189+
"$ref": "#/definitions/forbidden-reference-config"
190+
},
191+
"single-use-function": {
192+
"$ref": "#/definitions/single-use-function-rule"
193+
},
194+
"alias-avoidance": {
195+
"$ref": "#/definitions/alias-avoidance-rule"
196+
},
197+
"forbidden-source-root-reference": {
198+
"$ref": "#/definitions/forbidden-source-root-reference-rule"
199+
},
200+
"preferred-type-syntax": {
201+
"$ref": "#/definitions/preferred-type-syntax-lint-rule"
202+
}
203+
},
204+
"additionalProperties": false
205+
},
206+
"lint-config": {
207+
"type": "object",
208+
"description": "Namespace for lint rules.",
209+
"properties": {
210+
"rules": {
211+
"$ref": "#/definitions/lint-rules"
212+
}
213+
},
214+
"additionalProperties": false
215+
},
216+
"prefer-switch-rewrite-rule": {
217+
"type": "object",
218+
"description": "Rewrite `if` and ternary control flow into canonical `switch` forms.",
219+
"properties": {
220+
"enabled": {
221+
"$ref": "#/definitions/enabled"
222+
},
223+
"if": {
224+
"type": "boolean",
225+
"description": "When true, rewrite eligible `if` / `else if` branches. Default: true."
226+
},
227+
"ternary": {
228+
"type": "boolean",
229+
"description": "When true, rewrite eligible ternary expressions. Default: true."
230+
}
231+
},
232+
"additionalProperties": false
233+
},
234+
"no-optional-some-rewrite-rule": {
235+
"type": "object",
236+
"description": "Rewrite redundant optional-argument wrapping from `~label=?Some(expr)` to the direct labeled form.",
237+
"properties": {
238+
"enabled": {
239+
"$ref": "#/definitions/enabled"
240+
}
241+
},
242+
"additionalProperties": false
243+
},
244+
"preferred-type-syntax-rewrite-rule": {
245+
"type": "object",
246+
"description": "Rewrite supported type spellings into canonical builtin syntax. Today this supports rewriting `Dict.t<_>` and `Stdlib.Dict.t<_>` to `dict<_>`.",
247+
"properties": {
248+
"enabled": {
249+
"$ref": "#/definitions/enabled"
250+
},
251+
"dict": {
252+
"type": "boolean",
253+
"description": "When true, rewrite `Dict.t<_>` and `Stdlib.Dict.t<_>` to `dict<_>`. Default: false."
254+
}
255+
},
256+
"additionalProperties": false
257+
},
258+
"rewrite-rules": {
259+
"type": "object",
260+
"description": "Configured rewrite rules. Omitted rules keep their built-in defaults.",
261+
"properties": {
262+
"prefer-switch": {
263+
"$ref": "#/definitions/prefer-switch-rewrite-rule"
264+
},
265+
"no-optional-some": {
266+
"$ref": "#/definitions/no-optional-some-rewrite-rule"
267+
},
268+
"preferred-type-syntax": {
269+
"$ref": "#/definitions/preferred-type-syntax-rewrite-rule"
270+
}
271+
},
272+
"additionalProperties": false
273+
},
274+
"rewrite-config": {
275+
"type": "object",
276+
"description": "Namespace for rewrite rules.",
277+
"properties": {
278+
"rules": {
279+
"$ref": "#/definitions/rewrite-rules"
280+
}
281+
},
282+
"additionalProperties": false
283+
}
284+
}
285+
}

docs/rescript_ai.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Initial rules
2727
- Rewrite: `prefer-switch`, `no-optional-some`, `preferred-type-syntax`
2828
2929
Support
30-
- Add `.rescript-lint.json` config support, AI tooling docs, and golden tests for the new command surface
30+
- Add `.rescript-lint.json` config support, a shipped JSON schema, AI tooling docs, and golden tests for the new command surface
3131
```
3232

3333
## Goal
@@ -180,10 +180,17 @@ Possible file names:
180180
- `.rescript-lint.json`
181181
- `rescript-lint.json`
182182

183+
Schema:
184+
185+
- shipped at `docs/docson/rescript-lint-schema.json`
186+
- can be referenced from config files through a `$schema` field for editor completion and validation
187+
- the schema covers both the recommended namespaced shape and the deprecated top-level `rules` lint shortcut
188+
183189
Example shape:
184190

185191
```json
186192
{
193+
"$schema": "./node_modules/rescript/docs/docson/rescript-lint-schema.json",
187194
"lint": {
188195
"rules": {
189196
"forbidden-reference": [
@@ -222,6 +229,8 @@ Example shape:
222229
`forbidden-reference` accepts either one rule object or an array of rule objects.
223230
Each `items` entry must be an object with `kind` (`module`, `value`, or `type`)
224231
and `path`; `message` is optional at both the rule and item level.
232+
The older top-level `rules` field is still accepted for lint-only configs, but
233+
new configs should prefer `lint.rules`.
225234

226235
## Lint V1 Rules
227236

@@ -295,6 +304,7 @@ Example:
295304

296305
```json
297306
{
307+
"$schema": "./node_modules/rescript/docs/docson/rescript-lint-schema.json",
298308
"lint": {
299309
"rules": {
300310
"forbidden-source-root-reference": {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"COPYING.LESSER",
6666
"CREDITS.md",
6767
"docs/docson/build-schema.json",
68+
"docs/docson/rescript-lint-schema.json",
6869
"cli"
6970
],
7071
"exports": {

tests/tools_tests/.rescript-lint.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"$schema": "../../docs/docson/rescript-lint-schema.json",
23
"lint": {
34
"rules": {
45
"forbidden-reference": [

tests/tools_tests/test.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ for file in src/docstrings-format/*.{res,resi,md}; do
3333
fi
3434
done
3535

36+
node -e "JSON.parse(require('node:fs').readFileSync('../../docs/docson/rescript-lint-schema.json', 'utf8'))" || exit 1
37+
3638
# Test lint command
3739
for file in src/lint/*.{res,resi}; do
3840
output="src/expected/$(basename $file).lint.expected"

0 commit comments

Comments
 (0)