npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-sanity

v0.2.0

Published

ESLint plugin for Sanity Lint - lint GROQ queries and schema definitions

Downloads

736

Readme

eslint-plugin-sanity

Catch bugs in your GROQ queries and schema definitions before they hit production.

Works with both ESLint and OxLint.

What It Catches

GROQ queries:

// ❌ Performance issue - joins in filters cause full scans
const query = groq`*[_type == "post" && author->name == "John"]`
// ⚠️ sanity/groq-join-in-filter

// ❌ Typo that silently returns nothing (with schema-aware linting)
const query = groq`*[_type == "psot"]`
// ⚠️ sanity/groq-invalid-type-filter: Type "psot" not found in schema

Schema definitions:

// ❌ Missing defineType wrapper
export const post = {
  name: 'post',
  type: 'document',
  fields: [...]
}
// ⚠️ sanity/schema-missing-define-type

// ❌ Reserved field name that would break at runtime
defineField({ name: '_type', type: 'string' })
// ⚠️ sanity/schema-reserved-field-name

Installation

npm install eslint-plugin-sanity

Usage with ESLint

// eslint.config.js
import sanity from 'eslint-plugin-sanity'

export default [
  ...sanity.configs.recommended,
  // or for stricter checking:
  // ...sanity.configs.strict,
]

Usage with OxLint

OxLint supports ESLint-compatible JS plugins. Our plugin works out of the box:

// oxlint.config.json
{
  "jsPlugins": ["eslint-plugin-sanity"],
  "rules": {
    "sanity/groq-join-in-filter": "error",
    "sanity/groq-deep-pagination": "warn"
  }
}

Then run:

oxlint --config oxlint.config.json src/

Note: OxLint JS plugins are experimental. See OxLint JS Plugins for details.

Configurations

recommended

Balanced defaults - errors for serious issues, warnings for improvements.

strict

All rules enabled as errors.

Rules

GROQ Rules

These rules lint GROQ queries in:

  • groq\...`` tagged template literals
  • defineQuery('...') function calls (from next-sanity or @sanity/client)

| Rule | Default | Description | | ------------------------------------------ | ------- | ----------------------------------------------- | | sanity/groq-join-in-filter | error | Avoid -> inside filters | | sanity/groq-join-to-get-id | warn | Use ._ref instead of ->_id | | sanity/groq-deep-pagination | warn | Avoid large offsets (>=1000) | | sanity/groq-large-pages | warn | Avoid fetching >100 results | | sanity/groq-many-joins | warn | Avoid >10 joins in one query | | sanity/groq-computed-value-in-filter | error | Avoid computed values in filters | | sanity/groq-non-literal-comparison | error | Avoid comparing two non-literals | | sanity/groq-order-on-expr | error | Avoid ordering on computed values | | sanity/groq-repeated-dereference | info | Avoid repeated -> on same attribute | | sanity/groq-match-on-id | info | Avoid match on _id with wildcard | | sanity/groq-count-in-correlated-subquery | info | Avoid count() on correlated subqueries | | sanity/groq-very-large-query | error | Query exceeds 10KB | | sanity/groq-extremely-large-query | error | Query exceeds 100KB | | sanity/groq-unknown-field | error | Field doesn't exist in schema (requires schema) | | sanity/groq-invalid-type-filter | error | Type doesn't exist in schema (requires schema) |

Schema Rules

These rules lint Sanity schema definitions using defineType() and defineField().

| Rule | Default | Description | | ------------------------------------------- | ------- | ------------------------------------------- | | sanity/schema-missing-define-type | error | Must use defineType() | | sanity/schema-missing-define-field | warn | Fields should use defineField() | | sanity/schema-missing-icon | warn | Document types should have icons | | sanity/schema-missing-title | warn | Types should have titles | | sanity/schema-missing-description | info | Fields should have descriptions | | sanity/schema-missing-slug-source | warn | Slug fields need options.source | | sanity/schema-reserved-field-name | error | Avoid reserved field names (_id, _type) | | sanity/schema-array-missing-constraints | warn | Arrays should have constraints | | sanity/schema-boolean-instead-of-list | info | Consider options.list over boolean | | sanity/schema-heading-level-in-schema | warn | Don't store heading levels | | sanity/schema-unnecessary-reference | info | Consider embedding instead | | sanity/schema-presentation-field-name | warn | Avoid presentation-focused names | | sanity/schema-missing-required-validation | warn | Critical fields need validation |

Schema-Aware Linting

For schema-aware rules (unknown-field, invalid-type-filter), you need to provide a schema:

// eslint.config.js
import sanity from 'eslint-plugin-sanity'

export default [
  ...sanity.configs.recommended,
  {
    settings: {
      sanity: {
        schemaPath: './schema.json',
      },
    },
  },
]

Generate schema.json with:

npx sanity schema extract

Monorepo Setup

When using eslint-plugin-sanity in a monorepo (turborepo, pnpm workspaces, etc.), VS Code/Cursor may have trouble finding the ESLint config for nested packages.

VS Code / Cursor Settings

Create .vscode/settings.json at your monorepo root:

{
  "eslint.workingDirectories": [
    { "directory": "apps/web", "changeProcessCWD": true },
    { "directory": "apps/studio", "changeProcessCWD": true }
  ]
}

Replace the paths with your actual package directories that have ESLint configs.

Alternative: Auto-detect

You can also let ESLint auto-detect working directories:

{
  "eslint.workingDirectories": [{ "mode": "auto" }]
}

Troubleshooting

If rules still don't appear in the editor:

  1. Restart ESLint Server: Cmd+Shift+P → "ESLint: Restart ESLint Server"
  2. Check ESLint Output: View → Output → select "ESLint" to see errors
  3. Verify flat config: Ensure eslint.config.mjs exists in your package directory

Note: The CLI (npx eslint .) works regardless of these settings. This is purely for editor integration.

License

MIT