eslint-plugin-sanity 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Sanity.io
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # eslint-plugin-sanity
2
+
3
+ ESLint plugin for linting GROQ queries and Sanity schemas.
4
+
5
+ Works with both **ESLint** and **OxLint**.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install eslint-plugin-sanity
11
+ ```
12
+
13
+ ## Usage with ESLint
14
+
15
+ ```javascript
16
+ // eslint.config.js
17
+ import sanity from 'eslint-plugin-sanity'
18
+
19
+ export default [
20
+ sanity.configs.recommended,
21
+ // or for stricter checking:
22
+ // sanity.configs.strict,
23
+ ]
24
+ ```
25
+
26
+ ## Usage with OxLint
27
+
28
+ OxLint supports ESLint-compatible JS plugins. Our plugin works out of the box:
29
+
30
+ ```json
31
+ // oxlint.config.json
32
+ {
33
+ "jsPlugins": ["eslint-plugin-sanity"],
34
+ "rules": {
35
+ "sanity/groq-join-in-filter": "error",
36
+ "sanity/groq-deep-pagination": "warn"
37
+ }
38
+ }
39
+ ```
40
+
41
+ Then run:
42
+
43
+ ```bash
44
+ oxlint --config oxlint.config.json src/
45
+ ```
46
+
47
+ > **Note**: OxLint JS plugins are experimental. See [OxLint JS Plugins](https://oxc.rs/docs/guide/usage/linter/js-plugins) for details.
48
+
49
+ ## Configurations
50
+
51
+ ### `recommended`
52
+
53
+ Balanced defaults - errors for serious issues, warnings for improvements.
54
+
55
+ ### `strict`
56
+
57
+ All rules enabled as errors.
58
+
59
+ ## Rules
60
+
61
+ ### GROQ Rules
62
+
63
+ These rules lint GROQ queries in `groq\`...\`` tagged template literals.
64
+
65
+ | Rule | Default | Description |
66
+ | ------------------------------------------ | ------- | ----------------------------------------------- |
67
+ | `sanity/groq-join-in-filter` | error | Avoid `->` inside filters |
68
+ | `sanity/groq-join-to-get-id` | warn | Use `._ref` instead of `->_id` |
69
+ | `sanity/groq-deep-pagination` | warn | Avoid large offsets (>=1000) |
70
+ | `sanity/groq-large-pages` | warn | Avoid fetching >100 results |
71
+ | `sanity/groq-many-joins` | warn | Avoid >10 joins in one query |
72
+ | `sanity/groq-computed-value-in-filter` | error | Avoid computed values in filters |
73
+ | `sanity/groq-non-literal-comparison` | error | Avoid comparing two non-literals |
74
+ | `sanity/groq-order-on-expr` | error | Avoid ordering on computed values |
75
+ | `sanity/groq-repeated-dereference` | info | Avoid repeated `->` on same attribute |
76
+ | `sanity/groq-match-on-id` | info | Avoid match on `_id` with wildcard |
77
+ | `sanity/groq-count-in-correlated-subquery` | info | Avoid `count()` on correlated subqueries |
78
+ | `sanity/groq-very-large-query` | error | Query exceeds 10KB |
79
+ | `sanity/groq-extremely-large-query` | error | Query exceeds 100KB |
80
+ | `sanity/groq-unknown-field` | error | Field doesn't exist in schema (requires schema) |
81
+ | `sanity/groq-invalid-type-filter` | error | Type doesn't exist in schema (requires schema) |
82
+
83
+ ### Schema Rules
84
+
85
+ These rules lint Sanity schema definitions using `defineType()` and `defineField()`.
86
+
87
+ | Rule | Default | Description |
88
+ | ------------------------------------------- | ------- | ------------------------------------------- |
89
+ | `sanity/schema-missing-define-type` | error | Must use `defineType()` |
90
+ | `sanity/schema-missing-define-field` | warn | Fields should use `defineField()` |
91
+ | `sanity/schema-missing-icon` | warn | Document types should have icons |
92
+ | `sanity/schema-missing-title` | warn | Types should have titles |
93
+ | `sanity/schema-missing-description` | info | Fields should have descriptions |
94
+ | `sanity/schema-missing-slug-source` | warn | Slug fields need `options.source` |
95
+ | `sanity/schema-reserved-field-name` | error | Avoid reserved field names (`_id`, `_type`) |
96
+ | `sanity/schema-array-missing-constraints` | warn | Arrays should have constraints |
97
+ | `sanity/schema-boolean-instead-of-list` | info | Consider options.list over boolean |
98
+ | `sanity/schema-heading-level-in-schema` | warn | Don't store heading levels |
99
+ | `sanity/schema-unnecessary-reference` | info | Consider embedding instead |
100
+ | `sanity/schema-presentation-field-name` | warn | Avoid presentation-focused names |
101
+ | `sanity/schema-missing-required-validation` | warn | Critical fields need validation |
102
+
103
+ ## Schema-Aware Linting
104
+
105
+ For schema-aware rules (`unknown-field`, `invalid-type-filter`), you need to provide a schema:
106
+
107
+ ```javascript
108
+ // eslint.config.js
109
+ import sanity from 'eslint-plugin-sanity'
110
+
111
+ export default [
112
+ {
113
+ ...sanity.configs.recommended,
114
+ settings: {
115
+ sanity: {
116
+ schemaPath: './schema.json',
117
+ },
118
+ },
119
+ },
120
+ ]
121
+ ```
122
+
123
+ Generate `schema.json` with:
124
+
125
+ ```bash
126
+ npx sanity schema extract
127
+ ```
128
+
129
+ ## License
130
+
131
+ MIT
@@ -0,0 +1,18 @@
1
+ import * as eslint from 'eslint';
2
+ import { ESLint, Linter } from 'eslint';
3
+
4
+ declare const rules: {
5
+ [x: string]: eslint.Rule.RuleModule;
6
+ };
7
+ declare const configs: {
8
+ recommended: Linter.Config;
9
+ strict: Linter.Config;
10
+ };
11
+ interface SanityPlugin {
12
+ meta: ESLint.Plugin['meta'];
13
+ rules: typeof rules;
14
+ configs: typeof configs;
15
+ }
16
+ declare const sanityPlugin: SanityPlugin;
17
+
18
+ export { configs, sanityPlugin as default, rules };
package/dist/index.js ADDED
@@ -0,0 +1,508 @@
1
+ // src/index.ts
2
+ import { createRequire } from "module";
3
+ import { rules as groqRules } from "@sanity/groq-lint";
4
+ import { rules as schemaRules2 } from "@sanity/schema-lint";
5
+
6
+ // src/utils/rule-factory.ts
7
+ import { lint, rules as allGroqRules } from "@sanity/groq-lint";
8
+
9
+ // src/utils/groq-extractor.ts
10
+ function isGroqTaggedTemplate(node) {
11
+ const tag = node.tag;
12
+ if (tag.type === "Identifier" && tag.name === "groq") {
13
+ return true;
14
+ }
15
+ if (tag.type === "MemberExpression" && tag.object.type === "Identifier" && tag.object.name === "groq") {
16
+ return true;
17
+ }
18
+ return false;
19
+ }
20
+ function extractGroqString(node) {
21
+ const { quasis, expressions } = node.quasi;
22
+ if (expressions.length === 0) {
23
+ return quasis[0]?.value.cooked ?? quasis[0]?.value.raw ?? "";
24
+ }
25
+ let result = "";
26
+ for (let i = 0; i < quasis.length; i++) {
27
+ result += quasis[i]?.value.cooked ?? quasis[i]?.value.raw ?? "";
28
+ if (i < expressions.length) {
29
+ result += `$__expr${i}__`;
30
+ }
31
+ }
32
+ return result;
33
+ }
34
+
35
+ // src/utils/rule-factory.ts
36
+ function buildSingleRuleConfig(ruleId) {
37
+ const config = {};
38
+ for (const rule of allGroqRules) {
39
+ config[rule.id] = rule.id === ruleId;
40
+ }
41
+ return config;
42
+ }
43
+ function createESLintRule(groqRule) {
44
+ return {
45
+ meta: {
46
+ type: groqRule.category === "correctness" ? "problem" : "suggestion",
47
+ docs: {
48
+ description: groqRule.description,
49
+ recommended: groqRule.severity === "error"
50
+ },
51
+ messages: {
52
+ [groqRule.id]: "{{ message }}"
53
+ },
54
+ schema: []
55
+ // No options for now
56
+ },
57
+ create(context) {
58
+ return {
59
+ TaggedTemplateExpression(eslintNode) {
60
+ const node = eslintNode;
61
+ if (!isGroqTaggedTemplate(node)) {
62
+ return;
63
+ }
64
+ try {
65
+ const query = extractGroqString(node);
66
+ const result = lint(query, { config: { rules: buildSingleRuleConfig(groqRule.id) } });
67
+ for (const finding of result.findings) {
68
+ if (finding.ruleId === groqRule.id) {
69
+ context.report({
70
+ node: eslintNode,
71
+ messageId: groqRule.id,
72
+ data: {
73
+ message: finding.help ? `${finding.message} ${finding.help}` : finding.message
74
+ }
75
+ });
76
+ }
77
+ }
78
+ } catch {
79
+ }
80
+ }
81
+ };
82
+ }
83
+ };
84
+ }
85
+ function createAllRules(groqRules2) {
86
+ const eslintRules = {};
87
+ for (const rule of groqRules2) {
88
+ const eslintRuleId = `groq-${rule.id}`;
89
+ eslintRules[eslintRuleId] = createESLintRule(rule);
90
+ }
91
+ return eslintRules;
92
+ }
93
+
94
+ // src/utils/schema-rule-factory.ts
95
+ import { lint as lint2, rules as schemaRules } from "@sanity/schema-lint";
96
+
97
+ // src/utils/schema-extractor.ts
98
+ function isDefineTypeCall(node) {
99
+ const callee = node.callee;
100
+ if (callee.type === "Identifier" && callee.name === "defineType") {
101
+ return true;
102
+ }
103
+ return false;
104
+ }
105
+ function isDefineFieldCall(node) {
106
+ const callee = node.callee;
107
+ if (callee.type === "Identifier" && callee.name === "defineField") {
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+ function toSourceSpan(loc) {
113
+ return {
114
+ start: {
115
+ line: loc.start.line,
116
+ column: loc.start.column + 1,
117
+ // 1-based
118
+ offset: 0
119
+ // We don't have offset info easily
120
+ },
121
+ end: {
122
+ line: loc.end.line,
123
+ column: loc.end.column + 1,
124
+ // 1-based
125
+ offset: 0
126
+ }
127
+ };
128
+ }
129
+ function extractStringValue(node) {
130
+ if (!node) return void 0;
131
+ if (node.type === "Literal" && typeof node.value === "string") {
132
+ return node.value;
133
+ }
134
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
135
+ return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw;
136
+ }
137
+ return void 0;
138
+ }
139
+ function extractBooleanValue(node) {
140
+ if (!node) return void 0;
141
+ if (node.type === "Literal" && typeof node.value === "boolean") {
142
+ return node.value;
143
+ }
144
+ return void 0;
145
+ }
146
+ function hasProperty(node, name) {
147
+ return node.properties.some((prop) => {
148
+ if (prop.type === "Property" && prop.key.type === "Identifier") {
149
+ return prop.key.name === name;
150
+ }
151
+ return false;
152
+ });
153
+ }
154
+ function getProperty(node, name) {
155
+ for (const prop of node.properties) {
156
+ if (prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === name) {
157
+ return prop.value;
158
+ }
159
+ }
160
+ return void 0;
161
+ }
162
+ function extractFieldOptions(optionsNode) {
163
+ if (!optionsNode || optionsNode.type !== "ObjectExpression") {
164
+ return void 0;
165
+ }
166
+ const options = {};
167
+ for (const prop of optionsNode.properties) {
168
+ if (prop.type === "Property" && prop.key.type === "Identifier") {
169
+ const name = prop.key.name;
170
+ if (name === "source") {
171
+ const source = extractStringValue(prop.value);
172
+ if (source !== void 0) options.source = source;
173
+ } else if (name === "hotspot") {
174
+ const hotspot = extractBooleanValue(prop.value);
175
+ if (hotspot !== void 0) options.hotspot = hotspot;
176
+ } else if (name === "layout") {
177
+ const layout = extractStringValue(prop.value);
178
+ if (layout !== void 0) options.layout = layout;
179
+ } else if (name === "list" && prop.value.type === "ArrayExpression") {
180
+ options.list = prop.value.elements.map((el) => {
181
+ if (!el) return null;
182
+ if (el.type === "Literal") return el.value;
183
+ if (el.type === "ObjectExpression") {
184
+ const value = extractStringValue(getProperty(el, "value"));
185
+ return { value };
186
+ }
187
+ return null;
188
+ });
189
+ }
190
+ }
191
+ }
192
+ return Object.keys(options).length > 0 ? options : void 0;
193
+ }
194
+ function extractField(node, usesDefineField) {
195
+ let fieldObj;
196
+ if (node.type === "CallExpression" && isDefineFieldCall(node)) {
197
+ const arg = node.arguments[0];
198
+ if (arg?.type === "ObjectExpression") {
199
+ fieldObj = arg;
200
+ }
201
+ } else if (node.type === "ObjectExpression") {
202
+ fieldObj = node;
203
+ usesDefineField.value = false;
204
+ }
205
+ if (!fieldObj) return void 0;
206
+ const name = extractStringValue(getProperty(fieldObj, "name"));
207
+ const type = extractStringValue(getProperty(fieldObj, "type"));
208
+ if (!name || !type) return void 0;
209
+ const title = extractStringValue(getProperty(fieldObj, "title"));
210
+ const description = extractStringValue(getProperty(fieldObj, "description"));
211
+ const options = extractFieldOptions(getProperty(fieldObj, "options"));
212
+ const field = {
213
+ name,
214
+ type,
215
+ hasValidation: hasProperty(fieldObj, "validation"),
216
+ hidden: hasProperty(fieldObj, "hidden"),
217
+ readOnly: hasProperty(fieldObj, "readOnly"),
218
+ span: toSourceSpan(fieldObj.loc),
219
+ ...title !== void 0 && { title },
220
+ ...description !== void 0 && { description },
221
+ ...options !== void 0 && { options }
222
+ };
223
+ if (hasProperty(fieldObj, "deprecated")) {
224
+ const deprecatedNode = getProperty(fieldObj, "deprecated");
225
+ const deprecatedValue = extractStringValue(deprecatedNode);
226
+ field.deprecated = deprecatedValue ?? true;
227
+ }
228
+ return field;
229
+ }
230
+ function extractFields(node) {
231
+ const result = { fields: [], usesDefineField: true };
232
+ if (!node || node.type !== "ArrayExpression") {
233
+ return result;
234
+ }
235
+ const usesDefineFieldTracker = { value: true };
236
+ for (const element of node.elements) {
237
+ if (element) {
238
+ const field = extractField(element, usesDefineFieldTracker);
239
+ if (field) {
240
+ result.fields.push(field);
241
+ }
242
+ }
243
+ }
244
+ result.usesDefineField = usesDefineFieldTracker.value;
245
+ return result;
246
+ }
247
+ function extractSchemaFromDefineType(node) {
248
+ const arg = node.arguments[0];
249
+ if (!arg || arg.type !== "ObjectExpression") {
250
+ return void 0;
251
+ }
252
+ const name = extractStringValue(getProperty(arg, "name"));
253
+ const type = extractStringValue(getProperty(arg, "type"));
254
+ if (!name || !type) {
255
+ return void 0;
256
+ }
257
+ const title = extractStringValue(getProperty(arg, "title"));
258
+ const description = extractStringValue(getProperty(arg, "description"));
259
+ const fieldsResult = extractFields(getProperty(arg, "fields"));
260
+ const hasFields = fieldsResult.fields.length > 0;
261
+ const schema = {
262
+ name,
263
+ type,
264
+ hasIcon: hasProperty(arg, "icon"),
265
+ hasPreview: hasProperty(arg, "preview"),
266
+ usesDefineType: true,
267
+ span: toSourceSpan(arg.loc),
268
+ ...title !== void 0 && { title },
269
+ ...description !== void 0 && { description },
270
+ ...hasFields && { fields: fieldsResult.fields },
271
+ ...hasFields && { usesDefineField: fieldsResult.usesDefineField }
272
+ };
273
+ return schema;
274
+ }
275
+ function extractSchemaFromObject(node) {
276
+ const name = extractStringValue(getProperty(node, "name"));
277
+ const type = extractStringValue(getProperty(node, "type"));
278
+ if (!name || !type) {
279
+ return void 0;
280
+ }
281
+ const title = extractStringValue(getProperty(node, "title"));
282
+ const description = extractStringValue(getProperty(node, "description"));
283
+ const fieldsResult = extractFields(getProperty(node, "fields"));
284
+ const hasFields = fieldsResult.fields.length > 0;
285
+ const schema = {
286
+ name,
287
+ type,
288
+ hasIcon: hasProperty(node, "icon"),
289
+ hasPreview: hasProperty(node, "preview"),
290
+ usesDefineType: false,
291
+ span: toSourceSpan(node.loc),
292
+ ...title !== void 0 && { title },
293
+ ...description !== void 0 && { description },
294
+ ...hasFields && { fields: fieldsResult.fields },
295
+ ...hasFields && { usesDefineField: fieldsResult.usesDefineField }
296
+ };
297
+ return schema;
298
+ }
299
+
300
+ // src/utils/schema-rule-factory.ts
301
+ function createSchemaESLintRule(schemaRule) {
302
+ return {
303
+ meta: {
304
+ type: schemaRule.category === "correctness" ? "problem" : "suggestion",
305
+ docs: {
306
+ description: schemaRule.description,
307
+ recommended: schemaRule.severity === "error"
308
+ },
309
+ messages: {
310
+ [schemaRule.id]: "{{ message }}"
311
+ },
312
+ schema: []
313
+ // No options for now
314
+ },
315
+ create(context) {
316
+ return {
317
+ // Handle defineType() calls
318
+ CallExpression(eslintNode) {
319
+ const node = eslintNode;
320
+ if (!isDefineTypeCall(node)) {
321
+ return;
322
+ }
323
+ try {
324
+ const schema = extractSchemaFromDefineType(node);
325
+ if (!schema) {
326
+ return;
327
+ }
328
+ const result = lint2(schema, schemaRules, {
329
+ rules: [schemaRule],
330
+ filePath: context.filename
331
+ });
332
+ for (const finding of result.findings) {
333
+ if (finding.ruleId === schemaRule.id) {
334
+ context.report({
335
+ node: eslintNode,
336
+ messageId: schemaRule.id,
337
+ data: {
338
+ message: finding.help ? `${finding.message} ${finding.help}` : finding.message
339
+ }
340
+ });
341
+ }
342
+ }
343
+ } catch {
344
+ }
345
+ },
346
+ // Handle export statements with object literals (not using defineType)
347
+ ExportNamedDeclaration(eslintNode) {
348
+ const node = eslintNode;
349
+ if (schemaRule.id !== "missing-define-type") {
350
+ return;
351
+ }
352
+ if (!node.declaration) {
353
+ return;
354
+ }
355
+ if (node.declaration.type === "VariableDeclaration") {
356
+ for (const declarator of node.declaration.declarations) {
357
+ if (declarator.init?.type === "ObjectExpression" && !isWrappedInDefineType(declarator.init)) {
358
+ const schema = extractSchemaFromObject(declarator.init);
359
+ if (schema && (schema.type === "document" || schema.type === "object")) {
360
+ try {
361
+ const result = lint2(schema, schemaRules, {
362
+ rules: [schemaRule],
363
+ filePath: context.filename
364
+ });
365
+ for (const finding of result.findings) {
366
+ if (finding.ruleId === schemaRule.id) {
367
+ context.report({
368
+ node: eslintNode,
369
+ messageId: schemaRule.id,
370
+ data: {
371
+ message: finding.help ? `${finding.message} ${finding.help}` : finding.message
372
+ }
373
+ });
374
+ }
375
+ }
376
+ } catch {
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ },
383
+ // Handle default exports with object literals
384
+ ExportDefaultDeclaration(eslintNode) {
385
+ const node = eslintNode;
386
+ if (schemaRule.id !== "missing-define-type") {
387
+ return;
388
+ }
389
+ if (node.declaration.type === "ObjectExpression") {
390
+ const schema = extractSchemaFromObject(node.declaration);
391
+ if (schema && (schema.type === "document" || schema.type === "object")) {
392
+ try {
393
+ const result = lint2(schema, schemaRules, {
394
+ rules: [schemaRule],
395
+ filePath: context.filename
396
+ });
397
+ for (const finding of result.findings) {
398
+ if (finding.ruleId === schemaRule.id) {
399
+ context.report({
400
+ node: eslintNode,
401
+ messageId: schemaRule.id,
402
+ data: {
403
+ message: finding.help ? `${finding.message} ${finding.help}` : finding.message
404
+ }
405
+ });
406
+ }
407
+ }
408
+ } catch {
409
+ }
410
+ }
411
+ }
412
+ }
413
+ };
414
+ }
415
+ };
416
+ }
417
+ function isWrappedInDefineType(_node) {
418
+ return false;
419
+ }
420
+ function createAllSchemaRules(rules2) {
421
+ const eslintRules = {};
422
+ for (const rule of rules2) {
423
+ const eslintRuleId = `schema-${rule.id}`;
424
+ eslintRules[eslintRuleId] = createSchemaESLintRule(rule);
425
+ }
426
+ return eslintRules;
427
+ }
428
+
429
+ // src/index.ts
430
+ var require2 = createRequire(import.meta.url);
431
+ var { version } = require2("../package.json");
432
+ var groqEslintRules = createAllRules(groqRules);
433
+ var schemaEslintRules = createAllSchemaRules(schemaRules2);
434
+ var rules = {
435
+ ...groqEslintRules,
436
+ ...schemaEslintRules
437
+ };
438
+ var plugin = {
439
+ meta: {
440
+ name: "eslint-plugin-sanity",
441
+ version
442
+ },
443
+ rules
444
+ };
445
+ var recommended = {
446
+ plugins: {
447
+ sanity: plugin
448
+ },
449
+ rules: {
450
+ // === GROQ Rules ===
451
+ // Errors - these are serious performance or correctness issues
452
+ "sanity/groq-join-in-filter": "error",
453
+ // Warnings - performance issues that should be addressed
454
+ "sanity/groq-deep-pagination": "warn",
455
+ "sanity/groq-large-pages": "warn",
456
+ "sanity/groq-many-joins": "warn",
457
+ "sanity/groq-computed-value-in-filter": "warn",
458
+ "sanity/groq-non-literal-comparison": "warn",
459
+ "sanity/groq-order-on-expr": "warn",
460
+ "sanity/groq-very-large-query": "warn",
461
+ "sanity/groq-extremely-large-query": "error",
462
+ // Info - suggestions for improvement (off by default, enable as warnings)
463
+ "sanity/groq-join-to-get-id": "warn",
464
+ "sanity/groq-repeated-dereference": "warn",
465
+ "sanity/groq-match-on-id": "warn",
466
+ "sanity/groq-count-in-correlated-subquery": "warn",
467
+ "sanity/groq-deep-pagination-param": "warn",
468
+ // === Schema Rules ===
469
+ // Errors - correctness issues
470
+ "sanity/schema-missing-define-type": "error",
471
+ "sanity/schema-missing-define-field": "error",
472
+ "sanity/schema-reserved-field-name": "error",
473
+ // Warnings - best practice violations
474
+ "sanity/schema-missing-icon": "warn",
475
+ "sanity/schema-missing-title": "warn",
476
+ "sanity/schema-presentation-field-name": "warn",
477
+ "sanity/schema-missing-slug-source": "warn",
478
+ "sanity/schema-missing-required-validation": "warn",
479
+ "sanity/schema-heading-level-in-schema": "warn",
480
+ // Info - suggestions (off by default)
481
+ "sanity/schema-missing-description": "off",
482
+ "sanity/schema-boolean-instead-of-list": "off",
483
+ "sanity/schema-array-missing-constraints": "off",
484
+ "sanity/schema-unnecessary-reference": "off"
485
+ }
486
+ };
487
+ var strict = {
488
+ plugins: {
489
+ sanity: plugin
490
+ },
491
+ rules: Object.fromEntries(Object.keys(rules).map((ruleId) => [`sanity/${ruleId}`, "error"]))
492
+ };
493
+ var configs = {
494
+ recommended,
495
+ strict
496
+ };
497
+ var sanityPlugin = {
498
+ meta: plugin.meta,
499
+ rules,
500
+ configs
501
+ };
502
+ var index_default = sanityPlugin;
503
+ export {
504
+ configs,
505
+ index_default as default,
506
+ rules
507
+ };
508
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/utils/rule-factory.ts","../src/utils/groq-extractor.ts","../src/utils/schema-rule-factory.ts","../src/utils/schema-extractor.ts"],"sourcesContent":["/**\n * ESLint plugin for Sanity\n *\n * This plugin provides rules for linting GROQ queries and schema definitions\n * in JavaScript/TypeScript files.\n *\n * @example\n * ```js\n * // eslint.config.js\n * import sanity from 'eslint-plugin-sanity'\n *\n * export default [\n * {\n * plugins: { sanity },\n * rules: {\n * 'sanity/groq-join-in-filter': 'error',\n * 'sanity/schema-missing-icon': 'warn',\n * },\n * },\n * ]\n * ```\n *\n * Or use the recommended config:\n * ```js\n * import sanity from 'eslint-plugin-sanity'\n *\n * export default [\n * sanity.configs.recommended,\n * ]\n * ```\n */\n\nimport type { ESLint, Linter } from 'eslint'\nimport { createRequire } from 'node:module'\nimport { rules as groqRules } from '@sanity/groq-lint'\nimport { rules as schemaRules } from '@sanity/schema-lint'\nimport { createAllRules } from './utils/rule-factory'\nimport { createAllSchemaRules } from './utils/schema-rule-factory'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../package.json') as { version: string }\n\n// Create ESLint rules from all GROQ lint rules\nconst groqEslintRules = createAllRules(groqRules)\n\n// Create ESLint rules from all schema lint rules\nconst schemaEslintRules = createAllSchemaRules(schemaRules)\n\n// Combine all rules\nconst rules = {\n ...groqEslintRules,\n ...schemaEslintRules,\n}\n\n// Build the plugin object\nconst plugin: ESLint.Plugin = {\n meta: {\n name: 'eslint-plugin-sanity',\n version,\n },\n rules,\n}\n\n// Create recommended config\nconst recommended: Linter.Config = {\n plugins: {\n sanity: plugin,\n },\n rules: {\n // === GROQ Rules ===\n\n // Errors - these are serious performance or correctness issues\n 'sanity/groq-join-in-filter': 'error',\n\n // Warnings - performance issues that should be addressed\n 'sanity/groq-deep-pagination': 'warn',\n 'sanity/groq-large-pages': 'warn',\n 'sanity/groq-many-joins': 'warn',\n 'sanity/groq-computed-value-in-filter': 'warn',\n 'sanity/groq-non-literal-comparison': 'warn',\n 'sanity/groq-order-on-expr': 'warn',\n 'sanity/groq-very-large-query': 'warn',\n 'sanity/groq-extremely-large-query': 'error',\n\n // Info - suggestions for improvement (off by default, enable as warnings)\n 'sanity/groq-join-to-get-id': 'warn',\n 'sanity/groq-repeated-dereference': 'warn',\n 'sanity/groq-match-on-id': 'warn',\n 'sanity/groq-count-in-correlated-subquery': 'warn',\n 'sanity/groq-deep-pagination-param': 'warn',\n\n // === Schema Rules ===\n\n // Errors - correctness issues\n 'sanity/schema-missing-define-type': 'error',\n 'sanity/schema-missing-define-field': 'error',\n 'sanity/schema-reserved-field-name': 'error',\n\n // Warnings - best practice violations\n 'sanity/schema-missing-icon': 'warn',\n 'sanity/schema-missing-title': 'warn',\n 'sanity/schema-presentation-field-name': 'warn',\n 'sanity/schema-missing-slug-source': 'warn',\n 'sanity/schema-missing-required-validation': 'warn',\n 'sanity/schema-heading-level-in-schema': 'warn',\n\n // Info - suggestions (off by default)\n 'sanity/schema-missing-description': 'off',\n 'sanity/schema-boolean-instead-of-list': 'off',\n 'sanity/schema-array-missing-constraints': 'off',\n 'sanity/schema-unnecessary-reference': 'off',\n },\n}\n\n// Create strict config (all rules as errors)\nconst strict: Linter.Config = {\n plugins: {\n sanity: plugin,\n },\n rules: Object.fromEntries(Object.keys(rules).map((ruleId) => [`sanity/${ruleId}`, 'error'])),\n}\n\n// Configs with explicit type annotation\nconst configs: { recommended: Linter.Config; strict: Linter.Config } = {\n recommended,\n strict,\n}\n\n// Plugin type\ninterface SanityPlugin {\n meta: ESLint.Plugin['meta']\n rules: typeof rules\n configs: typeof configs\n}\n\n// Default export for ESLint flat config\nconst sanityPlugin: SanityPlugin = {\n meta: plugin.meta,\n rules,\n configs,\n}\n\nexport default sanityPlugin\n\n// Named exports for flexibility\nexport { rules, configs }\n","import type { Rule as ESLintRule } from 'eslint'\nimport type { TSESTree } from '@typescript-eslint/types'\nimport type { Rule as GroqRule } from '@sanity/groq-lint'\nimport { lint, rules as allGroqRules } from '@sanity/groq-lint'\nimport { isGroqTaggedTemplate, extractGroqString } from './groq-extractor'\n\n/**\n * Build a config that enables only the specified rule\n */\nfunction buildSingleRuleConfig(ruleId: string): Record<string, boolean> {\n const config: Record<string, boolean> = {}\n for (const rule of allGroqRules) {\n config[rule.id] = rule.id === ruleId\n }\n return config\n}\n\n/**\n * Create an ESLint rule from a GROQ lint rule.\n */\nexport function createESLintRule(groqRule: GroqRule): ESLintRule.RuleModule {\n return {\n meta: {\n type: groqRule.category === 'correctness' ? 'problem' : 'suggestion',\n docs: {\n description: groqRule.description,\n recommended: groqRule.severity === 'error',\n },\n messages: {\n [groqRule.id]: '{{ message }}',\n },\n schema: [], // No options for now\n },\n\n create(context) {\n return {\n TaggedTemplateExpression(eslintNode: ESLintRule.Node) {\n // Cast to our TSESTree type for type-safe property access\n const node = eslintNode as unknown as TSESTree.TaggedTemplateExpression\n if (!isGroqTaggedTemplate(node)) {\n return\n }\n\n try {\n const query = extractGroqString(node)\n const result = lint(query, { config: { rules: buildSingleRuleConfig(groqRule.id) } })\n\n for (const finding of result.findings) {\n if (finding.ruleId === groqRule.id) {\n context.report({\n node: eslintNode,\n messageId: groqRule.id,\n data: {\n message: finding.help ? `${finding.message} ${finding.help}` : finding.message,\n },\n })\n }\n }\n } catch {\n // Parse error - don't report, let the user see it in runtime\n }\n },\n }\n },\n }\n}\n\n/**\n * Create all ESLint rules from GROQ lint rules.\n */\nexport function createAllRules(groqRules: GroqRule[]): Record<string, ESLintRule.RuleModule> {\n const eslintRules: Record<string, ESLintRule.RuleModule> = {}\n\n for (const rule of groqRules) {\n // Convert rule ID from snake_case to kebab-case for ESLint convention\n const eslintRuleId = `groq-${rule.id}`\n eslintRules[eslintRuleId] = createESLintRule(rule)\n }\n\n return eslintRules\n}\n","import type { TSESTree } from '@typescript-eslint/types'\n\n/**\n * Check if a node is a tagged template literal with a GROQ tag.\n * Matches: groq`...`, groq.something`...`\n */\nexport function isGroqTaggedTemplate(node: TSESTree.TaggedTemplateExpression): boolean {\n const tag = node.tag\n\n // groq`...`\n if (tag.type === 'Identifier' && tag.name === 'groq') {\n return true\n }\n\n // groq.something`...` (for groq.experimental etc)\n if (\n tag.type === 'MemberExpression' &&\n tag.object.type === 'Identifier' &&\n tag.object.name === 'groq'\n ) {\n return true\n }\n\n return false\n}\n\n/**\n * Extract the GROQ query string from a tagged template literal.\n * Handles template literals with expressions by replacing them with placeholders.\n */\nexport function extractGroqString(node: TSESTree.TaggedTemplateExpression): string {\n const { quasis, expressions } = node.quasi\n\n // Simple case: no expressions\n if (expressions.length === 0) {\n return quasis[0]?.value.cooked ?? quasis[0]?.value.raw ?? ''\n }\n\n // Build the string with placeholders for expressions\n let result = ''\n for (let i = 0; i < quasis.length; i++) {\n result += quasis[i]?.value.cooked ?? quasis[i]?.value.raw ?? ''\n if (i < expressions.length) {\n // Replace expression with a parameter placeholder\n // This allows the query to still parse while marking where expressions are\n result += `$__expr${i}__`\n }\n }\n\n return result\n}\n\n/**\n * Get the source location for reporting errors.\n * Returns the location of the template literal content, not the tag.\n */\nexport function getTemplateLocation(\n node: TSESTree.TaggedTemplateExpression\n): TSESTree.SourceLocation {\n return node.quasi.loc\n}\n","import type { Rule as ESLintRule } from 'eslint'\nimport type { TSESTree } from '@typescript-eslint/types'\nimport type { SchemaRule } from '@sanity/schema-lint'\nimport { lint, rules as schemaRules } from '@sanity/schema-lint'\nimport {\n isDefineTypeCall,\n extractSchemaFromDefineType,\n extractSchemaFromObject,\n} from './schema-extractor'\n\n/**\n * Create an ESLint rule from a schema lint rule.\n */\nexport function createSchemaESLintRule(schemaRule: SchemaRule): ESLintRule.RuleModule {\n return {\n meta: {\n type: schemaRule.category === 'correctness' ? 'problem' : 'suggestion',\n docs: {\n description: schemaRule.description,\n recommended: schemaRule.severity === 'error',\n },\n messages: {\n [schemaRule.id]: '{{ message }}',\n },\n schema: [], // No options for now\n },\n\n create(context) {\n return {\n // Handle defineType() calls\n CallExpression(eslintNode: ESLintRule.Node) {\n // Cast to our TSESTree type for type-safe property access\n const node = eslintNode as unknown as TSESTree.CallExpression\n if (!isDefineTypeCall(node)) {\n return\n }\n\n try {\n const schema = extractSchemaFromDefineType(node)\n if (!schema) {\n return\n }\n\n const result = lint(schema, schemaRules, {\n rules: [schemaRule],\n filePath: context.filename,\n })\n\n for (const finding of result.findings) {\n if (finding.ruleId === schemaRule.id) {\n context.report({\n node: eslintNode,\n messageId: schemaRule.id,\n data: {\n message: finding.help ? `${finding.message} ${finding.help}` : finding.message,\n },\n })\n }\n }\n } catch {\n // Parse error - don't report\n }\n },\n\n // Handle export statements with object literals (not using defineType)\n ExportNamedDeclaration(eslintNode: ESLintRule.Node) {\n // Cast to our TSESTree type for type-safe property access\n const node = eslintNode as unknown as TSESTree.ExportNamedDeclaration\n\n // Only check the missing-define-type rule for non-defineType exports\n if (schemaRule.id !== 'missing-define-type') {\n return\n }\n\n if (!node.declaration) {\n return\n }\n\n // export const foo = { name: '...', type: '...' }\n if (node.declaration.type === 'VariableDeclaration') {\n for (const declarator of node.declaration.declarations) {\n if (\n declarator.init?.type === 'ObjectExpression' &&\n !isWrappedInDefineType(declarator.init)\n ) {\n const schema = extractSchemaFromObject(declarator.init)\n if (schema && (schema.type === 'document' || schema.type === 'object')) {\n try {\n const result = lint(schema, schemaRules, {\n rules: [schemaRule],\n filePath: context.filename,\n })\n\n for (const finding of result.findings) {\n if (finding.ruleId === schemaRule.id) {\n context.report({\n node: eslintNode,\n messageId: schemaRule.id,\n data: {\n message: finding.help\n ? `${finding.message} ${finding.help}`\n : finding.message,\n },\n })\n }\n }\n } catch {\n // Parse error - don't report\n }\n }\n }\n }\n }\n },\n\n // Handle default exports with object literals\n ExportDefaultDeclaration(eslintNode: ESLintRule.Node) {\n // Cast to our TSESTree type for type-safe property access\n const node = eslintNode as unknown as TSESTree.ExportDefaultDeclaration\n\n // Only check the missing-define-type rule for non-defineType exports\n if (schemaRule.id !== 'missing-define-type') {\n return\n }\n\n if (node.declaration.type === 'ObjectExpression') {\n const schema = extractSchemaFromObject(node.declaration)\n if (schema && (schema.type === 'document' || schema.type === 'object')) {\n try {\n const result = lint(schema, schemaRules, {\n rules: [schemaRule],\n filePath: context.filename,\n })\n\n for (const finding of result.findings) {\n if (finding.ruleId === schemaRule.id) {\n context.report({\n node: eslintNode,\n messageId: schemaRule.id,\n data: {\n message: finding.help\n ? `${finding.message} ${finding.help}`\n : finding.message,\n },\n })\n }\n }\n } catch {\n // Parse error - don't report\n }\n }\n }\n },\n }\n },\n }\n}\n\n/**\n * Check if an object expression's parent is a defineType() call\n */\nfunction isWrappedInDefineType(_node: TSESTree.ObjectExpression): boolean {\n // This is a simplified check - in practice we check by seeing if\n // the parent is a CallExpression with defineType callee\n // For now we rely on the CallExpression handler\n return false\n}\n\n/**\n * Create all ESLint rules from schema lint rules.\n */\nexport function createAllSchemaRules(rules: SchemaRule[]): Record<string, ESLintRule.RuleModule> {\n const eslintRules: Record<string, ESLintRule.RuleModule> = {}\n\n for (const rule of rules) {\n // Use schema- prefix to distinguish from groq- rules\n const eslintRuleId = `schema-${rule.id}`\n eslintRules[eslintRuleId] = createSchemaESLintRule(rule)\n }\n\n return eslintRules\n}\n","import type { TSESTree } from '@typescript-eslint/types'\nimport type { SchemaType, SchemaField } from '@sanity/schema-lint'\nimport type { SourceSpan } from '@sanity/lint-core'\n\n/**\n * Check if a node is a defineType() call\n */\nexport function isDefineTypeCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee\n\n // defineType({ ... })\n if (callee.type === 'Identifier' && callee.name === 'defineType') {\n return true\n }\n\n return false\n}\n\n/**\n * Check if a node is a defineField() call\n */\nexport function isDefineFieldCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee\n\n // defineField({ ... })\n if (callee.type === 'Identifier' && callee.name === 'defineField') {\n return true\n }\n\n return false\n}\n\n/**\n * Convert ESLint location to our SourceSpan format\n */\nfunction toSourceSpan(loc: TSESTree.SourceLocation): SourceSpan {\n return {\n start: {\n line: loc.start.line,\n column: loc.start.column + 1, // 1-based\n offset: 0, // We don't have offset info easily\n },\n end: {\n line: loc.end.line,\n column: loc.end.column + 1, // 1-based\n offset: 0,\n },\n }\n}\n\n/**\n * Extract a string value from an AST node\n */\nfunction extractStringValue(node: TSESTree.Node | undefined): string | undefined {\n if (!node) return undefined\n\n if (node.type === 'Literal' && typeof node.value === 'string') {\n return node.value\n }\n\n // Handle template literals without expressions\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {\n return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw\n }\n\n return undefined\n}\n\n/**\n * Extract a boolean value from an AST node\n */\nfunction extractBooleanValue(node: TSESTree.Node | undefined): boolean | undefined {\n if (!node) return undefined\n\n if (node.type === 'Literal' && typeof node.value === 'boolean') {\n return node.value\n }\n\n return undefined\n}\n\n/**\n * Check if a property exists in an object expression\n */\nfunction hasProperty(node: TSESTree.ObjectExpression, name: string): boolean {\n return node.properties.some((prop) => {\n if (prop.type === 'Property' && prop.key.type === 'Identifier') {\n return prop.key.name === name\n }\n return false\n })\n}\n\n/**\n * Get a property value from an object expression\n */\nfunction getProperty(node: TSESTree.ObjectExpression, name: string): TSESTree.Node | undefined {\n for (const prop of node.properties) {\n if (prop.type === 'Property' && prop.key.type === 'Identifier' && prop.key.name === name) {\n return prop.value\n }\n }\n return undefined\n}\n\n/**\n * Extract field options from an object expression\n */\nfunction extractFieldOptions(\n optionsNode: TSESTree.Node | undefined\n): SchemaField['options'] | undefined {\n if (!optionsNode || optionsNode.type !== 'ObjectExpression') {\n return undefined\n }\n\n const options: NonNullable<SchemaField['options']> = {}\n\n for (const prop of optionsNode.properties) {\n if (prop.type === 'Property' && prop.key.type === 'Identifier') {\n const name = prop.key.name\n\n if (name === 'source') {\n const source = extractStringValue(prop.value)\n if (source !== undefined) options.source = source\n } else if (name === 'hotspot') {\n const hotspot = extractBooleanValue(prop.value)\n if (hotspot !== undefined) options.hotspot = hotspot\n } else if (name === 'layout') {\n const layout = extractStringValue(prop.value)\n if (layout !== undefined) options.layout = layout\n } else if (name === 'list' && prop.value.type === 'ArrayExpression') {\n options.list = prop.value.elements.map((el) => {\n if (!el) return null\n if (el.type === 'Literal') return el.value\n if (el.type === 'ObjectExpression') {\n const value = extractStringValue(getProperty(el, 'value'))\n return { value }\n }\n return null\n })\n }\n }\n }\n\n return Object.keys(options).length > 0 ? options : undefined\n}\n\n/**\n * Extract a field from an object expression (either raw or wrapped in defineField)\n */\nfunction extractField(\n node: TSESTree.Node,\n usesDefineField: { value: boolean }\n): SchemaField | undefined {\n let fieldObj: TSESTree.ObjectExpression | undefined\n\n // Check if it's wrapped in defineField()\n if (node.type === 'CallExpression' && isDefineFieldCall(node)) {\n const arg = node.arguments[0]\n if (arg?.type === 'ObjectExpression') {\n fieldObj = arg\n }\n } else if (node.type === 'ObjectExpression') {\n fieldObj = node\n usesDefineField.value = false // At least one field doesn't use defineField\n }\n\n if (!fieldObj) return undefined\n\n const name = extractStringValue(getProperty(fieldObj, 'name'))\n const type = extractStringValue(getProperty(fieldObj, 'type'))\n\n if (!name || !type) return undefined\n\n const title = extractStringValue(getProperty(fieldObj, 'title'))\n const description = extractStringValue(getProperty(fieldObj, 'description'))\n const options = extractFieldOptions(getProperty(fieldObj, 'options'))\n\n const field: SchemaField = {\n name,\n type,\n hasValidation: hasProperty(fieldObj, 'validation'),\n hidden: hasProperty(fieldObj, 'hidden'),\n readOnly: hasProperty(fieldObj, 'readOnly'),\n span: toSourceSpan(fieldObj.loc),\n ...(title !== undefined && { title }),\n ...(description !== undefined && { description }),\n ...(options !== undefined && { options }),\n }\n\n // Check for deprecated\n if (hasProperty(fieldObj, 'deprecated')) {\n const deprecatedNode = getProperty(fieldObj, 'deprecated')\n const deprecatedValue = extractStringValue(deprecatedNode)\n field.deprecated = deprecatedValue ?? true\n }\n\n return field\n}\n\n/**\n * Extract fields from an array expression\n */\nfunction extractFields(node: TSESTree.Node | undefined): {\n fields: SchemaField[]\n usesDefineField: boolean\n} {\n const result = { fields: [] as SchemaField[], usesDefineField: true }\n\n if (!node || node.type !== 'ArrayExpression') {\n return result\n }\n\n const usesDefineFieldTracker = { value: true }\n\n for (const element of node.elements) {\n if (element) {\n const field = extractField(element, usesDefineFieldTracker)\n if (field) {\n result.fields.push(field)\n }\n }\n }\n\n result.usesDefineField = usesDefineFieldTracker.value\n\n return result\n}\n\n/**\n * Extract schema type from a defineType() call\n */\nexport function extractSchemaFromDefineType(node: TSESTree.CallExpression): SchemaType | undefined {\n const arg = node.arguments[0]\n\n if (!arg || arg.type !== 'ObjectExpression') {\n return undefined\n }\n\n const name = extractStringValue(getProperty(arg, 'name'))\n const type = extractStringValue(getProperty(arg, 'type'))\n\n if (!name || !type) {\n return undefined\n }\n\n const title = extractStringValue(getProperty(arg, 'title'))\n const description = extractStringValue(getProperty(arg, 'description'))\n const fieldsResult = extractFields(getProperty(arg, 'fields'))\n const hasFields = fieldsResult.fields.length > 0\n\n const schema: SchemaType = {\n name,\n type,\n hasIcon: hasProperty(arg, 'icon'),\n hasPreview: hasProperty(arg, 'preview'),\n usesDefineType: true,\n span: toSourceSpan(arg.loc),\n ...(title !== undefined && { title }),\n ...(description !== undefined && { description }),\n ...(hasFields && { fields: fieldsResult.fields }),\n ...(hasFields && { usesDefineField: fieldsResult.usesDefineField }),\n }\n\n return schema\n}\n\n/**\n * Extract schema type from a plain object literal (not wrapped in defineType)\n */\nexport function extractSchemaFromObject(node: TSESTree.ObjectExpression): SchemaType | undefined {\n const name = extractStringValue(getProperty(node, 'name'))\n const type = extractStringValue(getProperty(node, 'type'))\n\n if (!name || !type) {\n return undefined\n }\n\n // Only process if it looks like a Sanity schema (has name and type)\n const title = extractStringValue(getProperty(node, 'title'))\n const description = extractStringValue(getProperty(node, 'description'))\n const fieldsResult = extractFields(getProperty(node, 'fields'))\n const hasFields = fieldsResult.fields.length > 0\n\n const schema: SchemaType = {\n name,\n type,\n hasIcon: hasProperty(node, 'icon'),\n hasPreview: hasProperty(node, 'preview'),\n usesDefineType: false,\n span: toSourceSpan(node.loc),\n ...(title !== undefined && { title }),\n ...(description !== undefined && { description }),\n ...(hasFields && { fields: fieldsResult.fields }),\n ...(hasFields && { usesDefineField: fieldsResult.usesDefineField }),\n }\n\n return schema\n}\n"],"mappings":"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","names":["schemaRules","groqRules","lint","lint","rules","require","schemaRules"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "eslint-plugin-sanity",
3
+ "version": "0.0.1",
4
+ "description": "ESLint plugin for Sanity Lint - lint GROQ queries and schema definitions",
5
+ "author": "Sanity.io <hello@sanity.io>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/sanity-io/sanity-lint.git",
10
+ "directory": "packages/eslint-plugin"
11
+ },
12
+ "homepage": "https://github.com/sanity-io/sanity-lint#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/sanity-io/sanity-lint/issues"
15
+ },
16
+ "type": "module",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "dependencies": {
29
+ "@sanity/lint-core": "0.0.1",
30
+ "@sanity/schema-lint": "0.0.1",
31
+ "@sanity/groq-lint": "0.0.1"
32
+ },
33
+ "peerDependencies": {
34
+ "eslint": ">=8.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/eslint": "^9.6.1",
38
+ "@typescript-eslint/types": "^8.0.0",
39
+ "eslint": "^9.17.0",
40
+ "tsup": "^8.3.5",
41
+ "typescript": "^5.7.2"
42
+ },
43
+ "keywords": [
44
+ "eslint",
45
+ "eslint-plugin",
46
+ "sanity",
47
+ "groq",
48
+ "schema",
49
+ "lint"
50
+ ],
51
+ "scripts": {
52
+ "build": "tsup",
53
+ "dev": "tsup --watch",
54
+ "typecheck": "tsc --noEmit",
55
+ "clean": "rm -rf dist"
56
+ }
57
+ }