@sanity/groq-lint 0.0.1 → 0.0.2

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Main linting API\nexport { initLinter, lint, lintMany } from './linter'\nexport type { LintResult, LintOptions } from './linter'\n\n// WASM utilities\nexport { isWasmAvailable, WASM_RULES } from './wasm-linter'\n\n// Rules\nexport { rules, rulesById } from './rules'\nexport { joinInFilter } from './rules/join-in-filter'\n\n// Re-export types from core\nexport type {\n Rule,\n RuleConfig,\n LinterConfig,\n Finding,\n Suggestion,\n Severity,\n Category,\n SchemaType,\n} from '@sanity/lint-core'\n\n// Re-export utilities from core\nexport { formatFindings, formatFindingsJson, summarizeFindings } from '@sanity/lint-core'\n\n// Note: RuleTester is available from '@sanity/lint-core/testing' for test files\n// Note: Schema utilities (loadSchema, findSchemaPath, etc.) are available from '@sanity/groq-lint/schema'\n// They are in a separate entry point because they use Node.js APIs\n"],"mappings":";;;;;;;;;;;;AAwBA,SAAS,gBAAgB,oBAAoB,yBAAyB;","names":[]}
1
+ {"version":3,"sources":["../src/linter.ts","../src/walker.ts","../src/rules/computed-value-in-filter.ts","../src/rules/count-in-correlated-subquery.ts","../src/rules/deep-pagination.ts","../src/rules/deep-pagination-param.ts","../src/rules/extremely-large-query.ts","../src/rules/invalid-type-filter.ts","../src/rules/join-in-filter.ts","../src/rules/join-to-get-id.ts","../src/rules/large-pages.ts","../src/rules/many-joins.ts","../src/rules/match-on-id.ts","../src/rules/non-literal-comparison.ts","../src/rules/order-on-expr.ts","../src/rules/repeated-dereference.ts","../src/rules/unknown-field.ts","../src/rules/very-large-query.ts","../src/rules/index.ts","../src/wasm-linter.ts","../src/index.ts"],"sourcesContent":["import { parse, type SchemaType } from 'groq-js'\nimport type { Finding, Rule, RuleContext, LinterConfig } from '@sanity/lint-core'\nimport { rules as allRules } from './rules'\nimport { initWasmLinter, isWasmAvailable, isWasmRule, lintWithWasm } from './wasm-linter'\n\n/**\n * Result of linting a query\n */\nexport interface LintResult {\n /** The query that was linted */\n query: string\n /** Findings from the lint rules */\n findings: Finding[]\n /** Whether parsing failed */\n parseError?: string\n}\n\n/**\n * Options for linting\n */\nexport interface LintOptions {\n /** Linter configuration */\n config?: LinterConfig\n /** Schema for schema-aware rules */\n schema?: SchemaType\n /** Force use of TypeScript rules even when WASM is available */\n forceTs?: boolean\n}\n\n/**\n * Initialize the WASM linter for better performance\n *\n * Call this once at application startup. The linter will automatically\n * fall back to TypeScript rules if WASM is not available.\n *\n * @returns Promise that resolves to true if WASM is available\n *\n * @example\n * ```typescript\n * import { initLinter, lint } from '@sanity/groq-lint'\n *\n * // Optional: Initialize WASM for better performance\n * await initLinter()\n *\n * // Now lint() will use WASM for pure GROQ rules\n * const result = lint('*[_type == \"post\"]{ author-> }')\n * ```\n */\nexport async function initLinter(): Promise<boolean> {\n return initWasmLinter()\n}\n\n/**\n * Lint a GROQ query\n *\n * Uses WASM for pure GROQ rules (if available) and TypeScript for schema-aware rules.\n * Call `initLinter()` first to enable WASM support.\n *\n * @param query - The GROQ query string to lint\n * @param options - Optional configuration and schema\n * @returns Lint result with findings\n */\nexport function lint(query: string, options?: LintOptions): LintResult {\n const { config, schema, forceTs = false } = options ?? {}\n const findings: Finding[] = []\n\n // Handle empty query\n if (!query.trim()) {\n return { query, findings }\n }\n\n // Get enabled rules, filtering out schema-requiring rules if no schema provided\n const enabledRules = getEnabledRules(config, schema)\n\n // Split rules into WASM and TS rules\n const wasmRuleIds = new Set<string>()\n const tsRules: Rule[] = []\n\n for (const rule of enabledRules) {\n if (!forceTs && isWasmAvailable() && isWasmRule(rule.id)) {\n wasmRuleIds.add(rule.id)\n } else {\n tsRules.push(rule)\n }\n }\n\n // Run WASM rules first (if available)\n const wasmFindings: Finding[] = []\n if (wasmRuleIds.size > 0 && isWasmAvailable()) {\n try {\n const wf = lintWithWasm(query, wasmRuleIds)\n wasmFindings.push(...wf)\n } catch {\n // WASM failed - fall back to TS rules\n for (const ruleId of wasmRuleIds) {\n const rule = enabledRules.find((r) => r.id === ruleId)\n if (rule) {\n tsRules.push(rule)\n }\n }\n wasmRuleIds.clear()\n }\n }\n\n // Parse the query for TS rules (only if we have TS rules to run)\n let ast\n if (tsRules.length > 0) {\n try {\n ast = parse(query)\n } catch (error) {\n return {\n query,\n findings: wasmFindings, // Return any WASM findings we got\n parseError: error instanceof Error ? error.message : 'Unknown parse error',\n }\n }\n }\n\n // Track which rules have fired (for supersedes logic)\n const firedRules = new Set<string>()\n\n // Mark WASM rules that found issues\n for (const f of wasmFindings) {\n firedRules.add(f.ruleId)\n }\n\n // Run TS rules\n const tsFindings: Finding[] = []\n if (ast) {\n for (const rule of tsRules) {\n const ruleFindings: Finding[] = []\n\n const context: RuleContext = {\n query,\n queryLength: query.length,\n ...(schema && { schema }),\n report: (finding) => {\n ruleFindings.push({\n ...finding,\n ruleId: rule.id,\n severity: finding.severity ?? rule.severity,\n })\n },\n }\n\n rule.check(ast, context)\n\n if (ruleFindings.length > 0) {\n firedRules.add(rule.id)\n tsFindings.push(...ruleFindings)\n }\n }\n }\n\n // Combine all findings\n const allFindings = [...wasmFindings, ...tsFindings]\n\n // Apply supersedes logic\n for (const finding of allFindings) {\n const rule = enabledRules.find((r) => r.id === finding.ruleId)\n if (rule?.supersedes) {\n // Check if any superseding rule has fired\n const isSuperseded = enabledRules.some(\n (r) => r.supersedes?.includes(finding.ruleId) && firedRules.has(r.id)\n )\n if (!isSuperseded) {\n findings.push(finding)\n }\n } else {\n findings.push(finding)\n }\n }\n\n return { query, findings }\n}\n\n/**\n * Get enabled rules based on configuration\n */\nfunction getEnabledRules(config?: LinterConfig, schema?: SchemaType): Rule[] {\n let rules = allRules\n\n // Filter out schema-requiring rules if no schema is provided\n if (!schema) {\n rules = rules.filter((rule) => !rule.requiresSchema)\n }\n\n if (!config?.rules) {\n return rules\n }\n\n return rules.filter((rule) => {\n const ruleConfig = config.rules?.[rule.id]\n if (ruleConfig === false) {\n return false\n }\n if (typeof ruleConfig === 'object' && ruleConfig.enabled === false) {\n return false\n }\n return true\n })\n}\n\n/**\n * Lint multiple queries\n *\n * @param queries - Array of queries to lint\n * @param options - Optional configuration and schema\n * @returns Array of lint results\n */\nexport function lintMany(queries: string[], options?: LintOptions): LintResult[] {\n return queries.map((query) => lint(query, options))\n}\n","import type { ExprNode } from 'groq-js'\n\n/**\n * Context passed to visitor functions\n */\nexport interface WalkContext {\n /** Whether we're currently inside a filter constraint */\n inFilter: boolean\n /** Whether we're currently inside a projection */\n inProjection: boolean\n /** Parent node stack */\n parents: ExprNode[]\n}\n\n/**\n * Visitor function called for each node\n */\nexport type Visitor = (node: ExprNode, context: WalkContext) => void\n\n/**\n * Walk the AST and call visitor for each node\n *\n * @param node - The root AST node\n * @param visitor - Function called for each node\n * @param context - Initial context (defaults to not in filter/projection)\n */\nexport function walk(\n node: ExprNode,\n visitor: Visitor,\n context: WalkContext = { inFilter: false, inProjection: false, parents: [] }\n): void {\n // Call visitor for current node\n visitor(node, context)\n\n // Build context for children\n const childContext: WalkContext = {\n ...context,\n parents: [...context.parents, node],\n }\n\n // Recursively walk children based on node type\n switch (node.type) {\n case 'Filter': {\n // Walk base with current context\n walk(node.base, visitor, childContext)\n // Walk constraint with inFilter = true\n walk(node.expr, visitor, { ...childContext, inFilter: true })\n break\n }\n\n case 'Projection': {\n // Walk base with current context\n walk(node.base, visitor, childContext)\n // Walk projection expression with inProjection = true\n walk(node.expr, visitor, { ...childContext, inProjection: true })\n break\n }\n\n case 'And':\n case 'Or':\n case 'OpCall': {\n walk(node.left, visitor, childContext)\n walk(node.right, visitor, childContext)\n break\n }\n\n case 'Not':\n case 'Neg':\n case 'Pos':\n case 'Asc':\n case 'Desc':\n case 'Group': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'Deref': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'AccessAttribute': {\n if (node.base) {\n walk(node.base, visitor, childContext)\n }\n break\n }\n\n case 'AccessElement': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'Slice': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'ArrayCoerce': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'FlatMap':\n case 'Map': {\n walk(node.base, visitor, childContext)\n walk(node.expr, visitor, childContext)\n break\n }\n\n case 'Array': {\n for (const element of node.elements) {\n walk(element.value, visitor, childContext)\n }\n break\n }\n\n case 'Object': {\n for (const attr of node.attributes) {\n if (attr.type === 'ObjectAttributeValue') {\n walk(attr.value, visitor, childContext)\n } else if (attr.type === 'ObjectConditionalSplat') {\n walk(attr.condition, visitor, childContext)\n walk(attr.value, visitor, childContext)\n } else if (attr.type === 'ObjectSplat') {\n walk(attr.value, visitor, childContext)\n }\n }\n break\n }\n\n case 'FuncCall': {\n for (const arg of node.args) {\n walk(arg, visitor, childContext)\n }\n break\n }\n\n case 'PipeFuncCall': {\n walk(node.base, visitor, childContext)\n for (const arg of node.args) {\n walk(arg, visitor, childContext)\n }\n break\n }\n\n case 'Select': {\n for (const alt of node.alternatives) {\n walk(alt.condition, visitor, childContext)\n walk(alt.value, visitor, childContext)\n }\n if (node.fallback) {\n walk(node.fallback, visitor, childContext)\n }\n break\n }\n\n case 'InRange': {\n walk(node.base, visitor, childContext)\n walk(node.left, visitor, childContext)\n walk(node.right, visitor, childContext)\n break\n }\n\n case 'Tuple': {\n for (const member of node.members) {\n walk(member, visitor, childContext)\n }\n break\n }\n\n // Leaf nodes - no children to walk\n case 'Everything':\n case 'This':\n case 'Parent':\n case 'Value':\n case 'Parameter':\n case 'Context':\n break\n\n default: {\n // For any unhandled node types, do nothing\n // This makes the walker forward-compatible with new node types\n break\n }\n }\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\nconst ARITHMETIC_OPS = ['+', '-', '*', '/']\n\n/**\n * Check if an expression tree contains a Parent reference (^)\n */\nfunction containsParentRef(node: ExprNode): boolean {\n let found = false\n walk(node, (n) => {\n if (n.type === 'Parent') {\n found = true\n }\n })\n return found\n}\n\n/**\n * Check if a node is a literal (Value, Parameter, now(), or arithmetic on literals)\n */\nfunction isLiteral(node: ExprNode): boolean {\n if (node.type === 'Value') return true\n if (node.type === 'Parameter') return true\n if (node.type === 'FuncCall' && node.name === 'now') return true\n\n // Arithmetic on literals is still a literal\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (ARITHMETIC_OPS.includes(op ?? '')) {\n const left = (node as { left?: ExprNode }).left\n const right = (node as { right?: ExprNode }).right\n if (left && right && isLiteral(left) && isLiteral(right)) {\n return true\n }\n }\n }\n\n return false\n}\n\n/**\n * Check if a node is an arithmetic operation\n */\nfunction isArithmeticOp(\n node: ExprNode\n): node is ExprNode & { op: string; left: ExprNode; right: ExprNode } {\n return node.type === 'OpCall' && ARITHMETIC_OPS.includes((node as { op?: string }).op ?? '')\n}\n\n/**\n * Rule: computed-value-in-filter\n *\n * Detects arithmetic operators (+, -, *, /) inside filter expressions,\n * unless one side references a parent scope (^).\n */\nexport const computedValueInFilter: Rule = {\n id: 'computed-value-in-filter',\n name: 'Computed Value in Filter',\n description: 'Avoid computed values in filters. Indices cannot be used.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, walkContext) => {\n // Only check inside filters\n if (!walkContext.inFilter) return\n\n if (isArithmeticOp(node)) {\n // Exempt if either side contains a parent reference\n if (containsParentRef(node.left) || containsParentRef(node.right)) {\n return\n }\n\n // Exempt if both sides are literals (computed at query time, not per-document)\n if (isLiteral(node.left) && isLiteral(node.right)) {\n return\n }\n\n context.report({\n message: `Arithmetic operation '${node.op}' in filter prevents index usage.`,\n severity: 'warning',\n help: 'Consider pre-computing values or adding additional filters to reduce the search space.',\n })\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Check if an expression tree contains a Parent reference (^)\n */\nfunction containsParentRef(node: ExprNode): boolean {\n let found = false\n walk(node, (n) => {\n if (n.type === 'Parent') {\n found = true\n }\n })\n return found\n}\n\n/**\n * Rule: count-in-correlated-subquery\n *\n * Detects count() calls on correlated subqueries, which don't execute\n * as efficient aggregations.\n */\nexport const countInCorrelatedSubquery: Rule = {\n id: 'count-in-correlated-subquery',\n name: 'Count in Correlated Subquery',\n description: 'count() on correlated subquery can be slow.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'FuncCall') {\n const name = node.name as string\n if (name !== 'count') return\n\n const args = (node as { args?: ExprNode[] }).args ?? []\n if (args.length !== 1) return\n\n const arg = args[0]\n if (!arg) return\n\n // Check if the argument is a filter with a parent reference\n if (arg.type === 'Filter') {\n if (containsParentRef(arg as ExprNode)) {\n context.report({\n message:\n 'count() on correlated subquery does not execute as an efficient aggregation.',\n severity: 'info',\n help: 'This pattern may be slow on large datasets. Consider restructuring the query.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\nconst DEEP_PAGINATION_THRESHOLD = 1000\n\n/**\n * Rule: deep-pagination\n *\n * Detects slice expressions where the start index is >= 1000,\n * which causes slow queries due to offset-based pagination.\n */\nexport const deepPagination: Rule = {\n id: 'deep-pagination',\n name: 'Deep Pagination',\n description: 'Deep pagination with large offsets is slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'Slice') {\n // In groq-js, left is a direct number (or undefined for [0...N])\n const left = node.left as number | undefined\n if (typeof left === 'number' && left >= DEEP_PAGINATION_THRESHOLD) {\n context.report({\n message: `Slice offset of ${left} is deep pagination. This is slow because all skipped documents must be sorted first.`,\n severity: 'warning',\n help: 'Use cursor-based pagination with _id instead (e.g., `*[_type == \"post\" && _id > $lastId] | order(_id)[0...20]`).',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\n/**\n * Rule: deep-pagination-param\n *\n * Detects slice expressions where the start index is a parameter,\n * which could potentially cause deep pagination if given a large value.\n *\n * NOTE: groq-js does not support parameters in slice expressions\n * (throws \"slicing must use constant numbers\"). This rule is included\n * for compatibility with other parsers that may support this syntax.\n */\nexport const deepPaginationParam: Rule = {\n id: 'deep-pagination-param',\n name: 'Deep Pagination Parameter',\n description: 'Slice offset uses a parameter which could cause deep pagination.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'Slice') {\n // In groq-js, left is always a number (or undefined).\n // This check is for future parser compatibility where left might be a Parameter node.\n const left = node.left as unknown\n if (left && typeof left === 'object' && (left as { type?: string }).type === 'Parameter') {\n const paramNode = left as { type: string; name: string }\n context.report({\n message: `Slice offset uses parameter $${paramNode.name}. If given a large value, this will cause slow deep pagination.`,\n severity: 'info',\n help: 'Consider using cursor-based pagination with _id instead, or validate the parameter value.',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\n\nconst HUNDRED_KB = 100 * 1024\n\n/**\n * Rule: extremely-large-query\n *\n * Detects queries larger than 100KB which will likely execute very slowly.\n * This rule supersedes very-large-query.\n */\nexport const extremelyLargeQuery: Rule = {\n id: 'extremely-large-query',\n name: 'Extremely Large Query',\n description: 'This query is extremely large and will likely execute very slowly.',\n severity: 'error',\n category: 'performance',\n supersedes: ['very-large-query'],\n\n check(_ast, context) {\n if (context.queryLength > HUNDRED_KB) {\n context.report({\n message: `This query is ${formatSize(context.queryLength)}, which is extremely large and will likely execute very slowly. It may be deprioritized by the server.`,\n severity: 'error',\n help: 'Break the query into smaller parts, simplify projections, or reconsider the data model.',\n })\n }\n },\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)}KB`\n }\n return `${bytes} bytes`\n}\n","/**\n * Rule: invalid-type-filter\n *\n * Detects when a query filters by `_type == \"someType\"` where \"someType\"\n * doesn't exist in the schema. This catches typos like `_type == \"psot\"`\n * when you meant `_type == \"post\"`.\n *\n * This rule requires a schema to function. Without a schema, it is skipped.\n */\n\nimport type { Rule, Suggestion } from '@sanity/lint-core'\nimport type { OpCallNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length\n const n = b.length\n\n // Edge cases\n if (m === 0) return n\n if (n === 0) return m\n\n // Use two rows instead of full matrix for efficiency\n let prevRow = new Array<number>(n + 1)\n let currRow = new Array<number>(n + 1)\n\n // Initialize first row\n for (let j = 0; j <= n; j++) {\n prevRow[j] = j\n }\n\n // Fill matrix row by row\n for (let i = 1; i <= m; i++) {\n currRow[0] = i\n\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n currRow[j] = Math.min(\n (prevRow[j] ?? 0) + 1, // deletion\n (currRow[j - 1] ?? 0) + 1, // insertion\n (prevRow[j - 1] ?? 0) + cost // substitution\n )\n }\n\n // Swap rows\n ;[prevRow, currRow] = [currRow, prevRow]\n }\n\n return prevRow[n] ?? 0\n}\n\n/**\n * Find similar type names from the schema\n */\nfunction findSimilarTypes(typeName: string, schemaTypes: string[], maxDistance = 3): string[] {\n return schemaTypes\n .map((t) => ({\n type: t,\n distance: levenshteinDistance(typeName.toLowerCase(), t.toLowerCase()),\n }))\n .filter((t) => t.distance <= maxDistance && t.distance > 0)\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 3)\n .map((t) => t.type)\n}\n\n/**\n * Extract document type names from schema\n */\nfunction getSchemaDocumentTypes(schema: { type: string; name: string }[]): string[] {\n return schema.filter((item) => item.type === 'document').map((item) => item.name)\n}\n\n/**\n * Check if an OpCallNode is a _type comparison\n */\nfunction isTypeComparison(node: OpCallNode): { typeName: string } | null {\n if (node.op !== '==') return null\n\n const { left, right } = node\n\n // Check _type == \"value\"\n if (\n left.type === 'AccessAttribute' &&\n left.name === '_type' &&\n right.type === 'Value' &&\n typeof right.value === 'string'\n ) {\n return { typeName: right.value }\n }\n\n // Check \"value\" == _type\n if (\n right.type === 'AccessAttribute' &&\n right.name === '_type' &&\n left.type === 'Value' &&\n typeof left.value === 'string'\n ) {\n return { typeName: left.value }\n }\n\n return null\n}\n\nexport const invalidTypeFilter: Rule = {\n id: 'invalid-type-filter',\n name: 'Invalid Type Filter',\n description: 'Document type in filter does not exist in schema',\n severity: 'error',\n category: 'correctness',\n requiresSchema: true,\n\n check(ast, context) {\n const { schema } = context\n if (!schema) return // Schema required\n\n const documentTypes = getSchemaDocumentTypes(schema as { type: string; name: string }[])\n const documentTypeSet = new Set(documentTypes)\n\n walk(ast, (node) => {\n if (node.type !== 'OpCall') return\n\n const typeComparison = isTypeComparison(node as OpCallNode)\n if (!typeComparison) return\n\n const { typeName } = typeComparison\n\n // Check if the type exists in the schema\n if (!documentTypeSet.has(typeName)) {\n const similarTypes = findSimilarTypes(typeName, documentTypes)\n\n const suggestions: Suggestion[] = similarTypes.map((t) => ({\n description: `Change to \"${t}\"`,\n replacement: t,\n }))\n\n context.report({\n message: `Document type \"${typeName}\" does not exist in schema`,\n severity: 'error',\n help:\n similarTypes.length > 0\n ? `Did you mean: ${similarTypes.map((t) => `\"${t}\"`).join(', ')}?`\n : `Available types: ${documentTypes.slice(0, 5).join(', ')}${documentTypes.length > 5 ? '...' : ''}`,\n suggestions,\n })\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\n/**\n * Rule: join-in-filter\n *\n * Detects dereference operators (->) inside filter constraints.\n * This is a performance anti-pattern because it prevents query optimization.\n *\n * Bad: *[author->name == \"Bob\"]\n * Good: *[_type == \"post\" && author._ref == $authorId]\n */\nexport const joinInFilter: Rule = {\n id: 'join-in-filter',\n name: 'Join in Filter',\n description: 'Avoid `->` inside filters. It prevents optimization.',\n severity: 'error',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, walkContext) => {\n // Only check Deref nodes that are inside a filter\n if (node.type === 'Deref' && walkContext.inFilter) {\n context.report({\n message: 'Avoid joins (`->`) inside filters. It prevents optimization.',\n severity: 'error',\n help: 'Use `field._ref == $id` instead of `field->attr == \"value\"`',\n // Note: groq-js doesn't expose source positions, so we omit span\n // In a future version, we might add position tracking\n })\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\n/**\n * Rule: join-to-get-id\n *\n * Detects using a dereference operator (->) to retrieve the _id of a document.\n * This is inefficient because `ref->_id` can be written as `ref._ref`.\n */\nexport const joinToGetId: Rule = {\n id: 'join-to-get-id',\n name: 'Join to Get ID',\n description: 'Avoid using `->` to retrieve `_id`.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n // Look for AccessAttribute with name \"_id\" whose base is a Deref\n if (node.type === 'AccessAttribute' && node.name === '_id') {\n if (node.base && node.base.type === 'Deref') {\n context.report({\n message: 'Avoid using `->` to retrieve `_id`. Use `._ref` instead.',\n severity: 'info',\n help: 'Replace `reference->_id` with `reference._ref` for better performance.',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\nconst LARGE_PAGE_THRESHOLD = 100\n\n/**\n * Rule: large-pages\n *\n * Detects slice expressions that fetch more than 100 results at once.\n */\nexport const largePages: Rule = {\n id: 'large-pages',\n name: 'Large Pages',\n description: 'Fetching many results at once can be slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'Slice') {\n // In groq-js, left/right are direct numbers (or undefined)\n const left = node.left as number | undefined\n const right = node.right as number | undefined\n\n // Only flag if start is 0 (or not specified) and end > 100\n if ((left === 0 || left === undefined) && typeof right === 'number') {\n if (right > LARGE_PAGE_THRESHOLD) {\n context.report({\n message: `Fetching ${right} results at once can be slow.`,\n severity: 'warning',\n help: 'Consider breaking into smaller batches and/or using cursor-based pagination.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\nconst MAX_JOINS = 10\n\n/**\n * Rule: many-joins\n *\n * Detects queries with more than 10 dereference operators (->).\n */\nexport const manyJoins: Rule = {\n id: 'many-joins',\n name: 'Many Joins',\n description: 'This query uses many joins and may have poor performance.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n let joinCount = 0\n\n walk(ast, (node) => {\n if (node.type === 'Deref') {\n joinCount++\n }\n })\n\n if (joinCount > MAX_JOINS) {\n context.report({\n message: `This query uses ${joinCount} joins (->), which may cause poor performance.`,\n severity: 'warning',\n help: 'Consider denormalizing data, using fewer reference expansions, or splitting into multiple queries.',\n })\n }\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Rule: match-on-id\n *\n * Detects using the `match` operator on `_id`, which may not work as expected\n * because `match` is designed for full-text matching.\n */\nexport const matchOnId: Rule = {\n id: 'match-on-id',\n name: 'Match on ID',\n description: '`match` on `_id` may not work as expected.',\n severity: 'info',\n category: 'correctness',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (op !== 'match') return\n\n const left = (node as { left?: ExprNode }).left\n\n // Check if left side is an AccessAttribute for _id\n if (left && left.type === 'AccessAttribute') {\n const name = (left as { name?: string }).name\n if (name === '_id') {\n context.report({\n message:\n '`match` is designed for full-text matching and may not work as expected on `_id`.',\n severity: 'info',\n help: 'Consider using `==` for exact matches or `string::startsWith()` for prefix matching.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\nconst COMPARISON_OPS = ['==', '!=', '<', '>', '<=', '>=']\n\n/**\n * Check if an expression tree contains a Parent reference (^)\n */\nfunction containsParentRef(node: ExprNode): boolean {\n let found = false\n walk(node, (n) => {\n if (n.type === 'Parent') {\n found = true\n }\n })\n return found\n}\n\n/**\n * Check if a node is a \"literal\" for purposes of this rule.\n * Literals include: Value, Parameter, now()\n */\nfunction isLiteral(node: ExprNode): boolean {\n if (node.type === 'Value') return true\n if (node.type === 'Parameter') return true\n\n // now() is considered a literal (computed at query time, not per-document)\n if (node.type === 'FuncCall' && node.name === 'now') return true\n\n // Arithmetic on literals is still a literal (e.g., 2+1)\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (['+', '-', '*', '/'].includes(op ?? '')) {\n const left = (node as { left?: ExprNode }).left\n const right = (node as { right?: ExprNode }).right\n if (left && right && isLiteral(left) && isLiteral(right)) {\n return true\n }\n }\n }\n\n return false\n}\n\n/**\n * Rule: non-literal-comparison\n *\n * Detects comparisons where both sides are non-literal expressions,\n * which cannot use indices efficiently.\n */\nexport const nonLiteralComparison: Rule = {\n id: 'non-literal-comparison',\n name: 'Non-Literal Comparison',\n description: 'Comparisons between two non-literal fields are slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, walkContext) => {\n // Only check inside filters\n if (!walkContext.inFilter) return\n\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (!COMPARISON_OPS.includes(op ?? '')) return\n\n const left = (node as { left?: ExprNode }).left\n const right = (node as { right?: ExprNode }).right\n\n if (!left || !right) return\n\n // Exempt if either side references parent scope\n if (containsParentRef(left) || containsParentRef(right)) return\n\n // Flag if both sides are non-literals\n if (!isLiteral(left) && !isLiteral(right)) {\n context.report({\n message: 'Comparing two non-literal expressions prevents efficient index usage.',\n severity: 'warning',\n help: 'Consider adding additional filters to reduce the search space.',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Check if a node is a simple attribute (can use index for sorting)\n */\nfunction isSimpleAttribute(node: ExprNode): boolean {\n return node.type === 'AccessAttribute'\n}\n\n/**\n * Check if a node is an allowed wrapped attribute:\n * - lower(attribute)\n * - dateTime(attribute)\n * - geo::distance(attribute, constant)\n */\nfunction isAllowedWrappedAttribute(node: ExprNode): boolean {\n if (node.type !== 'FuncCall') return false\n\n const name = node.name as string\n const namespace = (node as { namespace?: string }).namespace\n const args = (node as { args?: ExprNode[] }).args ?? []\n\n // lower(attribute)\n if (name === 'lower' && namespace === 'global' && args.length === 1) {\n const arg = args[0]\n return arg !== undefined && isSimpleAttribute(arg)\n }\n\n // dateTime(attribute)\n if (name === 'dateTime' && namespace === 'global' && args.length === 1) {\n const arg = args[0]\n return arg !== undefined && isSimpleAttribute(arg)\n }\n\n // geo::distance(attribute, constant)\n if (name === 'distance' && namespace === 'geo' && args.length === 2) {\n const arg0 = args[0]\n const arg1 = args[1]\n return (\n arg0 !== undefined && arg1 !== undefined && isSimpleAttribute(arg0) && arg1.type === 'Value'\n )\n }\n\n return false\n}\n\n/**\n * Check if an order argument is valid (can use index)\n */\nfunction isValidOrderArg(node: ExprNode): boolean {\n // Direct attribute access\n if (isSimpleAttribute(node)) return true\n\n // Allowed function wrappers\n if (isAllowedWrappedAttribute(node)) return true\n\n // Asc/Desc wrappers - check the base\n if (node.type === 'Asc' || node.type === 'Desc') {\n const base = (node as { base?: ExprNode }).base\n if (base) {\n return isSimpleAttribute(base) || isAllowedWrappedAttribute(base)\n }\n }\n\n return false\n}\n\n/**\n * Rule: order-on-expr\n *\n * Detects order() calls with computed expressions that can't use indices.\n */\nexport const orderOnExpr: Rule = {\n id: 'order-on-expr',\n name: 'Order on Expression',\n description: 'Ordering on computed values is slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'PipeFuncCall') {\n const name = (node as { name?: string }).name\n if (name !== 'order') return\n\n const args = (node as { args?: ExprNode[] }).args ?? []\n\n for (const arg of args) {\n if (!isValidOrderArg(arg)) {\n context.report({\n message: 'Ordering on computed expression prevents index usage.',\n severity: 'warning',\n help: 'Use simple attributes or allowed functions (lower, dateTime, geo::distance) for ordering.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Get the base attribute name from a dereference chain.\n * e.g., author->name returns \"author\"\n */\nfunction getDerefBaseName(node: ExprNode): string | null {\n if (node.type !== 'Deref') return null\n\n const base = (node as { base?: ExprNode }).base\n if (!base) return null\n\n // Walk down to find the root AccessAttribute\n let current = base\n while (current.type === 'AccessAttribute' && (current as { base?: ExprNode }).base) {\n current = (current as { base: ExprNode }).base\n }\n\n if (current.type === 'AccessAttribute') {\n return (current as { name?: string }).name ?? null\n }\n\n return null\n}\n\n/**\n * Rule: repeated-dereference\n *\n * Detects multiple dereference operations on the same attribute\n * within a single projection.\n */\nexport const repeatedDereference: Rule = {\n id: 'repeated-dereference',\n name: 'Repeated Dereference',\n description: 'Repeatedly resolving the same reference is inefficient.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, _walkContext) => {\n // Only check Projection nodes\n if (node.type !== 'Projection') return\n\n const projectionExpr = (node as { expr?: ExprNode }).expr\n if (!projectionExpr) return\n\n // Track dereferenced attributes in this projection\n const derefCounts = new Map<string, number>()\n\n walk(projectionExpr, (innerNode) => {\n if (innerNode.type === 'Deref') {\n const baseName = getDerefBaseName(innerNode)\n if (baseName) {\n derefCounts.set(baseName, (derefCounts.get(baseName) ?? 0) + 1)\n }\n }\n })\n\n // Report any repeated dereferences\n for (const [name, count] of derefCounts) {\n if (count > 1) {\n context.report({\n message: `Reference '${name}' is dereferenced ${count} times. Consider a single sub-projection.`,\n severity: 'info',\n help: `Replace multiple '${name}->...' with '${name}->{ ... }' to resolve the reference once.`,\n })\n }\n }\n })\n },\n}\n","/**\n * Rule: unknown-field\n *\n * Detects when a query projects a field that doesn't exist in the schema\n * for the document type being queried. This catches typos like `{ titel }`\n * when you meant `{ title }`.\n *\n * This rule requires a schema to function. Without a schema, it is skipped.\n */\n\nimport type { Rule, RuleContext, Suggestion } from '@sanity/lint-core'\nimport type { ExprNode, MapNode, ObjectNode, OpCallNode, ProjectionNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length\n const n = b.length\n\n if (m === 0) return n\n if (n === 0) return m\n\n let prevRow = new Array<number>(n + 1)\n let currRow = new Array<number>(n + 1)\n\n for (let j = 0; j <= n; j++) {\n prevRow[j] = j\n }\n\n for (let i = 1; i <= m; i++) {\n currRow[0] = i\n\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n currRow[j] = Math.min(\n (prevRow[j] ?? 0) + 1,\n (currRow[j - 1] ?? 0) + 1,\n (prevRow[j - 1] ?? 0) + cost\n )\n }\n\n ;[prevRow, currRow] = [currRow, prevRow]\n }\n\n return prevRow[n] ?? 0\n}\n\n/**\n * Find similar field names from a list\n */\nfunction findSimilarFields(\n fieldName: string,\n availableFields: string[],\n maxDistance = 3\n): string[] {\n return availableFields\n .map((f) => ({\n field: f,\n distance: levenshteinDistance(fieldName.toLowerCase(), f.toLowerCase()),\n }))\n .filter((f) => f.distance <= maxDistance && f.distance > 0)\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 3)\n .map((f) => f.field)\n}\n\n/**\n * Extract document type from a filter constraint\n * Returns the type name if found, null otherwise\n */\nfunction extractTypeFromFilter(node: ExprNode): string | null {\n if (node.type === 'OpCall') {\n const opNode = node as OpCallNode\n if (opNode.op !== '==') return null\n\n const { left, right } = opNode\n\n // Check _type == \"value\"\n if (\n left.type === 'AccessAttribute' &&\n left.name === '_type' &&\n right.type === 'Value' &&\n typeof right.value === 'string'\n ) {\n return right.value\n }\n\n // Check \"value\" == _type\n if (\n right.type === 'AccessAttribute' &&\n right.name === '_type' &&\n left.type === 'Value' &&\n typeof left.value === 'string'\n ) {\n return left.value\n }\n }\n\n // Check for And conditions: _type == \"post\" && ...\n if (node.type === 'And') {\n const leftType = extractTypeFromFilter(node.left)\n if (leftType) return leftType\n return extractTypeFromFilter(node.right)\n }\n\n return null\n}\n\n/**\n * Find the document type by examining a Filter node\n */\nfunction findDocumentTypeFromFilter(filterBase: ExprNode): string | null {\n if (filterBase.type === 'Filter') {\n return extractTypeFromFilter(filterBase.expr)\n }\n return null\n}\n\n/**\n * Get field names for a document type from schema\n */\nfunction getFieldsForType(\n schema: { type: string; name: string; attributes?: Record<string, unknown> }[],\n typeName: string\n): string[] {\n const doc = schema.find((item) => item.type === 'document' && item.name === typeName)\n if (!doc?.attributes) return []\n return Object.keys(doc.attributes)\n}\n\n/**\n * Built-in Sanity fields that exist on all documents\n */\nconst BUILT_IN_FIELDS = new Set(['_id', '_type', '_rev', '_createdAt', '_updatedAt'])\n\n/**\n * Check fields in an Object node against schema\n */\nfunction checkObjectFields(\n objNode: ObjectNode,\n documentType: string,\n availableFieldsSet: Set<string>,\n schemaFields: string[],\n context: RuleContext\n): void {\n for (const attr of objNode.attributes) {\n if (attr.type === 'ObjectAttributeValue') {\n // Get the field being accessed\n const value = attr.value\n if (value.type === 'AccessAttribute' && !value.base) {\n const fieldName = value.name\n\n // Skip if it's a known field\n if (availableFieldsSet.has(fieldName)) continue\n\n // Unknown field - report it\n const similarFields = findSimilarFields(fieldName, [...availableFieldsSet])\n\n const suggestions: Suggestion[] = similarFields.map((f) => ({\n description: `Change to \"${f}\"`,\n replacement: f,\n }))\n\n context.report({\n message: `Field \"${fieldName}\" does not exist on type \"${documentType}\"`,\n severity: 'warning',\n help:\n similarFields.length > 0\n ? `Did you mean: ${similarFields.map((f) => `\"${f}\"`).join(', ')}?`\n : `Available fields: ${schemaFields.slice(0, 5).join(', ')}${schemaFields.length > 5 ? '...' : ''}`,\n suggestions,\n })\n }\n }\n }\n}\n\nexport const unknownField: Rule = {\n id: 'unknown-field',\n name: 'Unknown Field',\n description: 'Field in projection does not exist in schema',\n severity: 'warning',\n category: 'correctness',\n requiresSchema: true,\n\n check(ast, context) {\n const { schema } = context\n if (!schema) return\n\n walk(ast, (node) => {\n // Handle Map nodes: *[_type == \"post\"]{ title }\n // AST structure: Map { base: Filter, expr: Projection { base: This, expr: Object } }\n if (node.type === 'Map') {\n const mapNode = node as MapNode\n const documentType = findDocumentTypeFromFilter(mapNode.base)\n if (!documentType) return\n\n // The expr should be a Projection\n if (mapNode.expr.type !== 'Projection') return\n const projection = mapNode.expr as ProjectionNode\n\n // The projection's expr should be an Object\n if (projection.expr.type !== 'Object') return\n const objNode = projection.expr as ObjectNode\n\n // Get available fields for this type\n const schemaFields = getFieldsForType(\n schema as { type: string; name: string; attributes?: Record<string, unknown> }[],\n documentType\n )\n if (schemaFields.length === 0) return\n\n const availableFields = [...schemaFields, ...BUILT_IN_FIELDS]\n const availableFieldsSet = new Set(availableFields)\n\n checkObjectFields(objNode, documentType, availableFieldsSet, schemaFields, context)\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\n\nconst TEN_KB = 10 * 1024\n\n/**\n * Rule: very-large-query\n *\n * Detects queries larger than 10KB which may execute slowly.\n */\nexport const veryLargeQuery: Rule = {\n id: 'very-large-query',\n name: 'Very Large Query',\n description: 'This query is very large and may execute slowly.',\n severity: 'warning',\n category: 'performance',\n\n check(_ast, context) {\n if (context.queryLength > TEN_KB) {\n context.report({\n message: `This query is ${formatSize(context.queryLength)}, which is very large and may execute slowly. It may be deprioritized by the server.`,\n severity: 'warning',\n help: 'Consider breaking the query into smaller parts or simplifying projections.',\n })\n }\n },\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)}KB`\n }\n return `${bytes} bytes`\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { computedValueInFilter } from './computed-value-in-filter'\nimport { countInCorrelatedSubquery } from './count-in-correlated-subquery'\nimport { deepPagination } from './deep-pagination'\nimport { deepPaginationParam } from './deep-pagination-param'\nimport { extremelyLargeQuery } from './extremely-large-query'\nimport { invalidTypeFilter } from './invalid-type-filter'\nimport { joinInFilter } from './join-in-filter'\nimport { joinToGetId } from './join-to-get-id'\nimport { largePages } from './large-pages'\nimport { manyJoins } from './many-joins'\nimport { matchOnId } from './match-on-id'\nimport { nonLiteralComparison } from './non-literal-comparison'\nimport { orderOnExpr } from './order-on-expr'\nimport { repeatedDereference } from './repeated-dereference'\nimport { unknownField } from './unknown-field'\nimport { veryLargeQuery } from './very-large-query'\n\n/**\n * All available lint rules\n */\nexport const rules: Rule[] = [\n // Performance - query size\n veryLargeQuery,\n extremelyLargeQuery,\n\n // Performance - joins\n joinInFilter,\n joinToGetId,\n manyJoins,\n repeatedDereference,\n\n // Performance - pagination\n deepPagination,\n deepPaginationParam,\n largePages,\n\n // Performance - filtering\n computedValueInFilter,\n nonLiteralComparison,\n\n // Performance - ordering\n orderOnExpr,\n\n // Performance - aggregation\n countInCorrelatedSubquery,\n\n // Correctness\n matchOnId,\n\n // Schema-aware correctness (requires schema to run)\n invalidTypeFilter,\n unknownField,\n]\n\n/**\n * Rules indexed by ID for quick lookup\n */\nexport const rulesById: Record<string, Rule> = Object.fromEntries(\n rules.map((rule) => [rule.id, rule])\n)\n\n// Re-export individual rules for direct imports\nexport {\n computedValueInFilter,\n countInCorrelatedSubquery,\n deepPagination,\n deepPaginationParam,\n extremelyLargeQuery,\n invalidTypeFilter,\n joinInFilter,\n joinToGetId,\n largePages,\n manyJoins,\n matchOnId,\n nonLiteralComparison,\n orderOnExpr,\n repeatedDereference,\n unknownField,\n veryLargeQuery,\n}\n","/**\n * WASM-based linter for pure GROQ rules\n *\n * Uses @sanity/groq-wasm for high-performance linting of GROQ queries.\n * Falls back gracefully if WASM is not available.\n */\n\nimport type { Finding } from '@sanity/lint-core'\n\n// WASM module state\nlet wasmAvailable = false\nlet wasmInitialized = false\nlet wasmInitPromise: Promise<boolean> | null = null\n\n// Import types for the WASM module\ntype WasmModule = typeof import('@sanity/groq-wasm')\nlet wasmModule: WasmModule | null = null\n\n/**\n * Rules available in the WASM linter (from Rust groq-lint)\n */\nexport const WASM_RULES = new Set([\n 'join-in-filter',\n 'join-to-get-id',\n 'computed-value-in-filter',\n 'match-on-id',\n 'order-on-expr',\n 'deep-pagination',\n 'large-pages',\n 'non-literal-comparison',\n 'repeated-dereference',\n 'count-in-correlated-subquery',\n 'very-large-query',\n 'extremely-large-query',\n 'many-joins',\n])\n\n/**\n * Check if a rule is available in WASM\n */\nexport function isWasmRule(ruleId: string): boolean {\n return WASM_RULES.has(ruleId)\n}\n\n/**\n * Initialize the WASM linter\n *\n * Safe to call multiple times. Returns true if WASM is available.\n */\nexport async function initWasmLinter(): Promise<boolean> {\n if (wasmInitialized) {\n return wasmAvailable\n }\n\n if (wasmInitPromise) {\n return wasmInitPromise\n }\n\n wasmInitPromise = doInit()\n return wasmInitPromise\n}\n\nasync function doInit(): Promise<boolean> {\n try {\n // Dynamic import to avoid bundling issues\n wasmModule = await import('@sanity/groq-wasm')\n await wasmModule.initWasm()\n wasmAvailable = true\n wasmInitialized = true\n return true\n } catch {\n // WASM not available - will fall back to TS rules\n wasmAvailable = false\n wasmInitialized = true\n return false\n }\n}\n\n/**\n * Check if WASM linter is available\n */\nexport function isWasmAvailable(): boolean {\n return wasmAvailable\n}\n\n/**\n * Lint a query using WASM\n *\n * @param query - The GROQ query to lint\n * @param enabledRules - Optional set of rule IDs to enable (all if not provided)\n * @returns Array of findings from WASM linter\n * @throws If WASM is not initialized\n */\nexport function lintWithWasm(query: string, enabledRules?: Set<string>): Finding[] {\n if (!wasmAvailable || !wasmModule) {\n throw new Error('WASM linter not initialized. Call initWasmLinter() first.')\n }\n\n // Get findings from WASM\n const wasmFindings = wasmModule.lint(query)\n\n // Filter by enabled rules if provided\n if (enabledRules) {\n return wasmFindings.filter((f) => enabledRules.has(f.ruleId))\n }\n\n return wasmFindings\n}\n\n/**\n * Lint a query using WASM (async version)\n *\n * Automatically initializes WASM if needed.\n */\nexport async function lintWithWasmAsync(\n query: string,\n enabledRules?: Set<string>\n): Promise<Finding[]> {\n const available = await initWasmLinter()\n if (!available) {\n return [] // Return empty - caller should use TS fallback\n }\n return lintWithWasm(query, enabledRules)\n}\n","// Main linting API\nexport { initLinter, lint, lintMany } from './linter'\nexport type { LintResult, LintOptions } from './linter'\n\n// WASM utilities\nexport { isWasmAvailable, WASM_RULES } from './wasm-linter'\n\n// Rules\nexport { rules, rulesById } from './rules'\nexport { joinInFilter } from './rules/join-in-filter'\n\n// Re-export types from core\nexport type {\n Rule,\n RuleConfig,\n LinterConfig,\n Finding,\n Suggestion,\n Severity,\n Category,\n SchemaType,\n} from '@sanity/lint-core'\n\n// Re-export utilities from core\nexport { formatFindings, formatFindingsJson, summarizeFindings } from '@sanity/lint-core'\n\n// Note: RuleTester is available from '@sanity/lint-core/testing' for test files\n// Note: Schema utilities (loadSchema, findSchemaPath, etc.) are available from '@sanity/groq-lint/schema'\n// They are in a separate entry point because they use Node.js APIs\n"],"mappings":"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","names":["containsParentRef","containsParentRef","isLiteral","levenshteinDistance","formatSize","rules"]}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/schema.ts
21
+ var schema_exports = {};
22
+ __export(schema_exports, {
23
+ findSchemaPath: () => findSchemaPath,
24
+ getDocumentByType: () => getDocumentByType,
25
+ getDocumentTypes: () => getDocumentTypes,
26
+ getFieldsForType: () => getFieldsForType,
27
+ loadSchema: () => loadSchema,
28
+ tryLoadSchema: () => tryLoadSchema
29
+ });
30
+ module.exports = __toCommonJS(schema_exports);
31
+ var import_codegen = require("@sanity/codegen");
32
+ var import_node_fs = require("fs");
33
+ var import_node_path = require("path");
34
+ async function loadSchema(path) {
35
+ return (0, import_codegen.readSchema)(path);
36
+ }
37
+ var SCHEMA_CANDIDATES = [
38
+ "schema.json",
39
+ "sanity.schema.json",
40
+ ".sanity/schema.json",
41
+ "studio/schema.json"
42
+ ];
43
+ function findSchemaPath(root = process.cwd()) {
44
+ for (const candidate of SCHEMA_CANDIDATES) {
45
+ const fullPath = (0, import_node_path.resolve)(root, candidate);
46
+ if ((0, import_node_fs.existsSync)(fullPath)) {
47
+ return fullPath;
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ async function tryLoadSchema(root = process.cwd()) {
53
+ const schemaPath = findSchemaPath(root);
54
+ if (!schemaPath) {
55
+ return null;
56
+ }
57
+ try {
58
+ return await loadSchema(schemaPath);
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ function getDocumentTypes(schema) {
64
+ return schema.filter((item) => item.type === "document").map((doc) => doc.name);
65
+ }
66
+ function getDocumentByType(schema, typeName) {
67
+ return schema.find(
68
+ (item) => item.type === "document" && item.name === typeName
69
+ );
70
+ }
71
+ function getFieldsForType(schema, typeName) {
72
+ const doc = getDocumentByType(schema, typeName);
73
+ if (!doc) {
74
+ return [];
75
+ }
76
+ return Object.keys(doc.attributes);
77
+ }
78
+ // Annotate the CommonJS export names for ESM import in node:
79
+ 0 && (module.exports = {
80
+ findSchemaPath,
81
+ getDocumentByType,
82
+ getDocumentTypes,
83
+ getFieldsForType,
84
+ loadSchema,
85
+ tryLoadSchema
86
+ });
87
+ //# sourceMappingURL=schema.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts"],"sourcesContent":["/**\n * Schema loading utilities for schema-aware GROQ linting.\n *\n * Uses @sanity/codegen's readSchema to load schema.json files\n * generated by `sanity schema extract`.\n */\n\nimport { readSchema } from '@sanity/codegen'\nimport type { SchemaType } from 'groq-js'\nimport { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\n/**\n * Load a schema from a JSON file path.\n *\n * @param path - Path to schema.json file\n * @returns The parsed schema\n * @throws If the file doesn't exist or is invalid\n */\nexport async function loadSchema(path: string): Promise<SchemaType> {\n return readSchema(path)\n}\n\n/**\n * Common locations to look for schema.json files\n */\nconst SCHEMA_CANDIDATES = [\n 'schema.json',\n 'sanity.schema.json',\n '.sanity/schema.json',\n 'studio/schema.json',\n]\n\n/**\n * Auto-discover a schema.json file in a project directory.\n *\n * Looks in common locations for schema files generated by\n * `sanity schema extract`.\n *\n * @param root - Project root directory (defaults to cwd)\n * @returns Path to schema file, or null if not found\n */\nexport function findSchemaPath(root: string = process.cwd()): string | null {\n for (const candidate of SCHEMA_CANDIDATES) {\n const fullPath = resolve(root, candidate)\n if (existsSync(fullPath)) {\n return fullPath\n }\n }\n return null\n}\n\n/**\n * Try to load a schema from common locations.\n *\n * @param root - Project root directory (defaults to cwd)\n * @returns The schema if found, or null\n */\nexport async function tryLoadSchema(root: string = process.cwd()): Promise<SchemaType | null> {\n const schemaPath = findSchemaPath(root)\n if (!schemaPath) {\n return null\n }\n\n try {\n return await loadSchema(schemaPath)\n } catch {\n return null\n }\n}\n\n/**\n * Get document type names from a schema.\n *\n * @param schema - The schema to extract document types from\n * @returns Array of document type names\n */\nexport function getDocumentTypes(schema: SchemaType): string[] {\n return schema\n .filter((item): item is Extract<typeof item, { type: 'document' }> => item.type === 'document')\n .map((doc) => doc.name)\n}\n\n/**\n * Get a document definition by name from a schema.\n *\n * @param schema - The schema to search\n * @param typeName - The document type name (e.g., 'post')\n * @returns The document definition, or undefined\n */\nexport function getDocumentByType(\n schema: SchemaType,\n typeName: string\n): Extract<SchemaType[number], { type: 'document' }> | undefined {\n return schema.find(\n (item): item is Extract<typeof item, { type: 'document' }> =>\n item.type === 'document' && item.name === typeName\n )\n}\n\n/**\n * Get field names for a document type.\n *\n * @param schema - The schema to search\n * @param typeName - The document type name\n * @returns Array of field names, or empty array if type not found\n */\nexport function getFieldsForType(schema: SchemaType, typeName: string): string[] {\n const doc = getDocumentByType(schema, typeName)\n if (!doc) {\n return []\n }\n return Object.keys(doc.attributes)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,qBAA2B;AAE3B,qBAA2B;AAC3B,uBAAwB;AASxB,eAAsB,WAAW,MAAmC;AAClE,aAAO,2BAAW,IAAI;AACxB;AAKA,IAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWO,SAAS,eAAe,OAAe,QAAQ,IAAI,GAAkB;AAC1E,aAAW,aAAa,mBAAmB;AACzC,UAAM,eAAW,0BAAQ,MAAM,SAAS;AACxC,YAAI,2BAAW,QAAQ,GAAG;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAQA,eAAsB,cAAc,OAAe,QAAQ,IAAI,GAA+B;AAC5F,QAAM,aAAa,eAAe,IAAI;AACtC,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,MAAM,WAAW,UAAU;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,iBAAiB,QAA8B;AAC7D,SAAO,OACJ,OAAO,CAAC,SAA6D,KAAK,SAAS,UAAU,EAC7F,IAAI,CAAC,QAAQ,IAAI,IAAI;AAC1B;AASO,SAAS,kBACd,QACA,UAC+D;AAC/D,SAAO,OAAO;AAAA,IACZ,CAAC,SACC,KAAK,SAAS,cAAc,KAAK,SAAS;AAAA,EAC9C;AACF;AASO,SAAS,iBAAiB,QAAoB,UAA4B;AAC/E,QAAM,MAAM,kBAAkB,QAAQ,QAAQ;AAC9C,MAAI,CAAC,KAAK;AACR,WAAO,CAAC;AAAA,EACV;AACA,SAAO,OAAO,KAAK,IAAI,UAAU;AACnC;","names":[]}
@@ -0,0 +1,61 @@
1
+ import { SchemaType } from 'groq-js';
2
+
3
+ /**
4
+ * Schema loading utilities for schema-aware GROQ linting.
5
+ *
6
+ * Uses @sanity/codegen's readSchema to load schema.json files
7
+ * generated by `sanity schema extract`.
8
+ */
9
+
10
+ /**
11
+ * Load a schema from a JSON file path.
12
+ *
13
+ * @param path - Path to schema.json file
14
+ * @returns The parsed schema
15
+ * @throws If the file doesn't exist or is invalid
16
+ */
17
+ declare function loadSchema(path: string): Promise<SchemaType>;
18
+ /**
19
+ * Auto-discover a schema.json file in a project directory.
20
+ *
21
+ * Looks in common locations for schema files generated by
22
+ * `sanity schema extract`.
23
+ *
24
+ * @param root - Project root directory (defaults to cwd)
25
+ * @returns Path to schema file, or null if not found
26
+ */
27
+ declare function findSchemaPath(root?: string): string | null;
28
+ /**
29
+ * Try to load a schema from common locations.
30
+ *
31
+ * @param root - Project root directory (defaults to cwd)
32
+ * @returns The schema if found, or null
33
+ */
34
+ declare function tryLoadSchema(root?: string): Promise<SchemaType | null>;
35
+ /**
36
+ * Get document type names from a schema.
37
+ *
38
+ * @param schema - The schema to extract document types from
39
+ * @returns Array of document type names
40
+ */
41
+ declare function getDocumentTypes(schema: SchemaType): string[];
42
+ /**
43
+ * Get a document definition by name from a schema.
44
+ *
45
+ * @param schema - The schema to search
46
+ * @param typeName - The document type name (e.g., 'post')
47
+ * @returns The document definition, or undefined
48
+ */
49
+ declare function getDocumentByType(schema: SchemaType, typeName: string): Extract<SchemaType[number], {
50
+ type: 'document';
51
+ }> | undefined;
52
+ /**
53
+ * Get field names for a document type.
54
+ *
55
+ * @param schema - The schema to search
56
+ * @param typeName - The document type name
57
+ * @returns Array of field names, or empty array if type not found
58
+ */
59
+ declare function getFieldsForType(schema: SchemaType, typeName: string): string[];
60
+
61
+ export { findSchemaPath, getDocumentByType, getDocumentTypes, getFieldsForType, loadSchema, tryLoadSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/groq-lint",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "GROQ linting engine, rules, and CLI for Sanity Lint",
5
5
  "author": "Sanity.io <hello@sanity.io>",
6
6
  "license": "MIT",
@@ -13,14 +13,16 @@
13
13
  "exports": {
14
14
  ".": {
15
15
  "types": "./dist/index.d.ts",
16
- "import": "./dist/index.js"
16
+ "import": "./dist/index.js",
17
+ "require": "./dist/index.cjs"
17
18
  },
18
19
  "./schema": {
19
20
  "types": "./dist/schema.d.ts",
20
- "import": "./dist/schema.js"
21
+ "import": "./dist/schema.js",
22
+ "require": "./dist/schema.cjs"
21
23
  }
22
24
  },
23
- "main": "./dist/index.js",
25
+ "main": "./dist/index.cjs",
24
26
  "types": "./dist/index.d.ts",
25
27
  "bin": {
26
28
  "groq-lint": "./dist/cli.js"
@@ -32,7 +34,7 @@
32
34
  "@sanity/codegen": "^5.1.0",
33
35
  "groq-js": "^1.14.0",
34
36
  "@sanity/groq-wasm": "0.0.1",
35
- "@sanity/lint-core": "0.0.1"
37
+ "@sanity/lint-core": "0.0.2"
36
38
  },
37
39
  "devDependencies": {
38
40
  "tsup": "^8.3.5",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/walker.ts","../src/rules/join-in-filter.ts","../src/rules/computed-value-in-filter.ts","../src/rules/count-in-correlated-subquery.ts","../src/rules/deep-pagination.ts","../src/rules/deep-pagination-param.ts","../src/rules/extremely-large-query.ts","../src/rules/invalid-type-filter.ts","../src/rules/join-to-get-id.ts","../src/rules/large-pages.ts","../src/rules/many-joins.ts","../src/rules/match-on-id.ts","../src/rules/non-literal-comparison.ts","../src/rules/order-on-expr.ts","../src/rules/repeated-dereference.ts","../src/rules/unknown-field.ts","../src/rules/very-large-query.ts","../src/rules/index.ts","../src/wasm-linter.ts","../src/linter.ts"],"sourcesContent":["import type { ExprNode } from 'groq-js'\n\n/**\n * Context passed to visitor functions\n */\nexport interface WalkContext {\n /** Whether we're currently inside a filter constraint */\n inFilter: boolean\n /** Whether we're currently inside a projection */\n inProjection: boolean\n /** Parent node stack */\n parents: ExprNode[]\n}\n\n/**\n * Visitor function called for each node\n */\nexport type Visitor = (node: ExprNode, context: WalkContext) => void\n\n/**\n * Walk the AST and call visitor for each node\n *\n * @param node - The root AST node\n * @param visitor - Function called for each node\n * @param context - Initial context (defaults to not in filter/projection)\n */\nexport function walk(\n node: ExprNode,\n visitor: Visitor,\n context: WalkContext = { inFilter: false, inProjection: false, parents: [] }\n): void {\n // Call visitor for current node\n visitor(node, context)\n\n // Build context for children\n const childContext: WalkContext = {\n ...context,\n parents: [...context.parents, node],\n }\n\n // Recursively walk children based on node type\n switch (node.type) {\n case 'Filter': {\n // Walk base with current context\n walk(node.base, visitor, childContext)\n // Walk constraint with inFilter = true\n walk(node.expr, visitor, { ...childContext, inFilter: true })\n break\n }\n\n case 'Projection': {\n // Walk base with current context\n walk(node.base, visitor, childContext)\n // Walk projection expression with inProjection = true\n walk(node.expr, visitor, { ...childContext, inProjection: true })\n break\n }\n\n case 'And':\n case 'Or':\n case 'OpCall': {\n walk(node.left, visitor, childContext)\n walk(node.right, visitor, childContext)\n break\n }\n\n case 'Not':\n case 'Neg':\n case 'Pos':\n case 'Asc':\n case 'Desc':\n case 'Group': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'Deref': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'AccessAttribute': {\n if (node.base) {\n walk(node.base, visitor, childContext)\n }\n break\n }\n\n case 'AccessElement': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'Slice': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'ArrayCoerce': {\n walk(node.base, visitor, childContext)\n break\n }\n\n case 'FlatMap':\n case 'Map': {\n walk(node.base, visitor, childContext)\n walk(node.expr, visitor, childContext)\n break\n }\n\n case 'Array': {\n for (const element of node.elements) {\n walk(element.value, visitor, childContext)\n }\n break\n }\n\n case 'Object': {\n for (const attr of node.attributes) {\n if (attr.type === 'ObjectAttributeValue') {\n walk(attr.value, visitor, childContext)\n } else if (attr.type === 'ObjectConditionalSplat') {\n walk(attr.condition, visitor, childContext)\n walk(attr.value, visitor, childContext)\n } else if (attr.type === 'ObjectSplat') {\n walk(attr.value, visitor, childContext)\n }\n }\n break\n }\n\n case 'FuncCall': {\n for (const arg of node.args) {\n walk(arg, visitor, childContext)\n }\n break\n }\n\n case 'PipeFuncCall': {\n walk(node.base, visitor, childContext)\n for (const arg of node.args) {\n walk(arg, visitor, childContext)\n }\n break\n }\n\n case 'Select': {\n for (const alt of node.alternatives) {\n walk(alt.condition, visitor, childContext)\n walk(alt.value, visitor, childContext)\n }\n if (node.fallback) {\n walk(node.fallback, visitor, childContext)\n }\n break\n }\n\n case 'InRange': {\n walk(node.base, visitor, childContext)\n walk(node.left, visitor, childContext)\n walk(node.right, visitor, childContext)\n break\n }\n\n case 'Tuple': {\n for (const member of node.members) {\n walk(member, visitor, childContext)\n }\n break\n }\n\n // Leaf nodes - no children to walk\n case 'Everything':\n case 'This':\n case 'Parent':\n case 'Value':\n case 'Parameter':\n case 'Context':\n break\n\n default: {\n // For any unhandled node types, do nothing\n // This makes the walker forward-compatible with new node types\n break\n }\n }\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\n/**\n * Rule: join-in-filter\n *\n * Detects dereference operators (->) inside filter constraints.\n * This is a performance anti-pattern because it prevents query optimization.\n *\n * Bad: *[author->name == \"Bob\"]\n * Good: *[_type == \"post\" && author._ref == $authorId]\n */\nexport const joinInFilter: Rule = {\n id: 'join-in-filter',\n name: 'Join in Filter',\n description: 'Avoid `->` inside filters. It prevents optimization.',\n severity: 'error',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, walkContext) => {\n // Only check Deref nodes that are inside a filter\n if (node.type === 'Deref' && walkContext.inFilter) {\n context.report({\n message: 'Avoid joins (`->`) inside filters. It prevents optimization.',\n severity: 'error',\n help: 'Use `field._ref == $id` instead of `field->attr == \"value\"`',\n // Note: groq-js doesn't expose source positions, so we omit span\n // In a future version, we might add position tracking\n })\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\nconst ARITHMETIC_OPS = ['+', '-', '*', '/']\n\n/**\n * Check if an expression tree contains a Parent reference (^)\n */\nfunction containsParentRef(node: ExprNode): boolean {\n let found = false\n walk(node, (n) => {\n if (n.type === 'Parent') {\n found = true\n }\n })\n return found\n}\n\n/**\n * Check if a node is a literal (Value, Parameter, now(), or arithmetic on literals)\n */\nfunction isLiteral(node: ExprNode): boolean {\n if (node.type === 'Value') return true\n if (node.type === 'Parameter') return true\n if (node.type === 'FuncCall' && node.name === 'now') return true\n\n // Arithmetic on literals is still a literal\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (ARITHMETIC_OPS.includes(op ?? '')) {\n const left = (node as { left?: ExprNode }).left\n const right = (node as { right?: ExprNode }).right\n if (left && right && isLiteral(left) && isLiteral(right)) {\n return true\n }\n }\n }\n\n return false\n}\n\n/**\n * Check if a node is an arithmetic operation\n */\nfunction isArithmeticOp(\n node: ExprNode\n): node is ExprNode & { op: string; left: ExprNode; right: ExprNode } {\n return node.type === 'OpCall' && ARITHMETIC_OPS.includes((node as { op?: string }).op ?? '')\n}\n\n/**\n * Rule: computed-value-in-filter\n *\n * Detects arithmetic operators (+, -, *, /) inside filter expressions,\n * unless one side references a parent scope (^).\n */\nexport const computedValueInFilter: Rule = {\n id: 'computed-value-in-filter',\n name: 'Computed Value in Filter',\n description: 'Avoid computed values in filters. Indices cannot be used.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, walkContext) => {\n // Only check inside filters\n if (!walkContext.inFilter) return\n\n if (isArithmeticOp(node)) {\n // Exempt if either side contains a parent reference\n if (containsParentRef(node.left) || containsParentRef(node.right)) {\n return\n }\n\n // Exempt if both sides are literals (computed at query time, not per-document)\n if (isLiteral(node.left) && isLiteral(node.right)) {\n return\n }\n\n context.report({\n message: `Arithmetic operation '${node.op}' in filter prevents index usage.`,\n severity: 'warning',\n help: 'Consider pre-computing values or adding additional filters to reduce the search space.',\n })\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Check if an expression tree contains a Parent reference (^)\n */\nfunction containsParentRef(node: ExprNode): boolean {\n let found = false\n walk(node, (n) => {\n if (n.type === 'Parent') {\n found = true\n }\n })\n return found\n}\n\n/**\n * Rule: count-in-correlated-subquery\n *\n * Detects count() calls on correlated subqueries, which don't execute\n * as efficient aggregations.\n */\nexport const countInCorrelatedSubquery: Rule = {\n id: 'count-in-correlated-subquery',\n name: 'Count in Correlated Subquery',\n description: 'count() on correlated subquery can be slow.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'FuncCall') {\n const name = node.name as string\n if (name !== 'count') return\n\n const args = (node as { args?: ExprNode[] }).args ?? []\n if (args.length !== 1) return\n\n const arg = args[0]\n if (!arg) return\n\n // Check if the argument is a filter with a parent reference\n if (arg.type === 'Filter') {\n if (containsParentRef(arg as ExprNode)) {\n context.report({\n message:\n 'count() on correlated subquery does not execute as an efficient aggregation.',\n severity: 'info',\n help: 'This pattern may be slow on large datasets. Consider restructuring the query.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\nconst DEEP_PAGINATION_THRESHOLD = 1000\n\n/**\n * Rule: deep-pagination\n *\n * Detects slice expressions where the start index is >= 1000,\n * which causes slow queries due to offset-based pagination.\n */\nexport const deepPagination: Rule = {\n id: 'deep-pagination',\n name: 'Deep Pagination',\n description: 'Deep pagination with large offsets is slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'Slice') {\n // In groq-js, left is a direct number (or undefined for [0...N])\n const left = node.left as number | undefined\n if (typeof left === 'number' && left >= DEEP_PAGINATION_THRESHOLD) {\n context.report({\n message: `Slice offset of ${left} is deep pagination. This is slow because all skipped documents must be sorted first.`,\n severity: 'warning',\n help: 'Use cursor-based pagination with _id instead (e.g., `*[_type == \"post\" && _id > $lastId] | order(_id)[0...20]`).',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\n/**\n * Rule: deep-pagination-param\n *\n * Detects slice expressions where the start index is a parameter,\n * which could potentially cause deep pagination if given a large value.\n *\n * NOTE: groq-js does not support parameters in slice expressions\n * (throws \"slicing must use constant numbers\"). This rule is included\n * for compatibility with other parsers that may support this syntax.\n */\nexport const deepPaginationParam: Rule = {\n id: 'deep-pagination-param',\n name: 'Deep Pagination Parameter',\n description: 'Slice offset uses a parameter which could cause deep pagination.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'Slice') {\n // In groq-js, left is always a number (or undefined).\n // This check is for future parser compatibility where left might be a Parameter node.\n const left = node.left as unknown\n if (left && typeof left === 'object' && (left as { type?: string }).type === 'Parameter') {\n const paramNode = left as { type: string; name: string }\n context.report({\n message: `Slice offset uses parameter $${paramNode.name}. If given a large value, this will cause slow deep pagination.`,\n severity: 'info',\n help: 'Consider using cursor-based pagination with _id instead, or validate the parameter value.',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\n\nconst HUNDRED_KB = 100 * 1024\n\n/**\n * Rule: extremely-large-query\n *\n * Detects queries larger than 100KB which will likely execute very slowly.\n * This rule supersedes very-large-query.\n */\nexport const extremelyLargeQuery: Rule = {\n id: 'extremely-large-query',\n name: 'Extremely Large Query',\n description: 'This query is extremely large and will likely execute very slowly.',\n severity: 'error',\n category: 'performance',\n supersedes: ['very-large-query'],\n\n check(_ast, context) {\n if (context.queryLength > HUNDRED_KB) {\n context.report({\n message: `This query is ${formatSize(context.queryLength)}, which is extremely large and will likely execute very slowly. It may be deprioritized by the server.`,\n severity: 'error',\n help: 'Break the query into smaller parts, simplify projections, or reconsider the data model.',\n })\n }\n },\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)}KB`\n }\n return `${bytes} bytes`\n}\n","/**\n * Rule: invalid-type-filter\n *\n * Detects when a query filters by `_type == \"someType\"` where \"someType\"\n * doesn't exist in the schema. This catches typos like `_type == \"psot\"`\n * when you meant `_type == \"post\"`.\n *\n * This rule requires a schema to function. Without a schema, it is skipped.\n */\n\nimport type { Rule, Suggestion } from '@sanity/lint-core'\nimport type { OpCallNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length\n const n = b.length\n\n // Edge cases\n if (m === 0) return n\n if (n === 0) return m\n\n // Use two rows instead of full matrix for efficiency\n let prevRow = new Array<number>(n + 1)\n let currRow = new Array<number>(n + 1)\n\n // Initialize first row\n for (let j = 0; j <= n; j++) {\n prevRow[j] = j\n }\n\n // Fill matrix row by row\n for (let i = 1; i <= m; i++) {\n currRow[0] = i\n\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n currRow[j] = Math.min(\n (prevRow[j] ?? 0) + 1, // deletion\n (currRow[j - 1] ?? 0) + 1, // insertion\n (prevRow[j - 1] ?? 0) + cost // substitution\n )\n }\n\n // Swap rows\n ;[prevRow, currRow] = [currRow, prevRow]\n }\n\n return prevRow[n] ?? 0\n}\n\n/**\n * Find similar type names from the schema\n */\nfunction findSimilarTypes(typeName: string, schemaTypes: string[], maxDistance = 3): string[] {\n return schemaTypes\n .map((t) => ({\n type: t,\n distance: levenshteinDistance(typeName.toLowerCase(), t.toLowerCase()),\n }))\n .filter((t) => t.distance <= maxDistance && t.distance > 0)\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 3)\n .map((t) => t.type)\n}\n\n/**\n * Extract document type names from schema\n */\nfunction getSchemaDocumentTypes(schema: { type: string; name: string }[]): string[] {\n return schema.filter((item) => item.type === 'document').map((item) => item.name)\n}\n\n/**\n * Check if an OpCallNode is a _type comparison\n */\nfunction isTypeComparison(node: OpCallNode): { typeName: string } | null {\n if (node.op !== '==') return null\n\n const { left, right } = node\n\n // Check _type == \"value\"\n if (\n left.type === 'AccessAttribute' &&\n left.name === '_type' &&\n right.type === 'Value' &&\n typeof right.value === 'string'\n ) {\n return { typeName: right.value }\n }\n\n // Check \"value\" == _type\n if (\n right.type === 'AccessAttribute' &&\n right.name === '_type' &&\n left.type === 'Value' &&\n typeof left.value === 'string'\n ) {\n return { typeName: left.value }\n }\n\n return null\n}\n\nexport const invalidTypeFilter: Rule = {\n id: 'invalid-type-filter',\n name: 'Invalid Type Filter',\n description: 'Document type in filter does not exist in schema',\n severity: 'error',\n category: 'correctness',\n requiresSchema: true,\n\n check(ast, context) {\n const { schema } = context\n if (!schema) return // Schema required\n\n const documentTypes = getSchemaDocumentTypes(schema as { type: string; name: string }[])\n const documentTypeSet = new Set(documentTypes)\n\n walk(ast, (node) => {\n if (node.type !== 'OpCall') return\n\n const typeComparison = isTypeComparison(node as OpCallNode)\n if (!typeComparison) return\n\n const { typeName } = typeComparison\n\n // Check if the type exists in the schema\n if (!documentTypeSet.has(typeName)) {\n const similarTypes = findSimilarTypes(typeName, documentTypes)\n\n const suggestions: Suggestion[] = similarTypes.map((t) => ({\n description: `Change to \"${t}\"`,\n replacement: t,\n }))\n\n context.report({\n message: `Document type \"${typeName}\" does not exist in schema`,\n severity: 'error',\n help:\n similarTypes.length > 0\n ? `Did you mean: ${similarTypes.map((t) => `\"${t}\"`).join(', ')}?`\n : `Available types: ${documentTypes.slice(0, 5).join(', ')}${documentTypes.length > 5 ? '...' : ''}`,\n suggestions,\n })\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\n/**\n * Rule: join-to-get-id\n *\n * Detects using a dereference operator (->) to retrieve the _id of a document.\n * This is inefficient because `ref->_id` can be written as `ref._ref`.\n */\nexport const joinToGetId: Rule = {\n id: 'join-to-get-id',\n name: 'Join to Get ID',\n description: 'Avoid using `->` to retrieve `_id`.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n // Look for AccessAttribute with name \"_id\" whose base is a Deref\n if (node.type === 'AccessAttribute' && node.name === '_id') {\n if (node.base && node.base.type === 'Deref') {\n context.report({\n message: 'Avoid using `->` to retrieve `_id`. Use `._ref` instead.',\n severity: 'info',\n help: 'Replace `reference->_id` with `reference._ref` for better performance.',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\nconst LARGE_PAGE_THRESHOLD = 100\n\n/**\n * Rule: large-pages\n *\n * Detects slice expressions that fetch more than 100 results at once.\n */\nexport const largePages: Rule = {\n id: 'large-pages',\n name: 'Large Pages',\n description: 'Fetching many results at once can be slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'Slice') {\n // In groq-js, left/right are direct numbers (or undefined)\n const left = node.left as number | undefined\n const right = node.right as number | undefined\n\n // Only flag if start is 0 (or not specified) and end > 100\n if ((left === 0 || left === undefined) && typeof right === 'number') {\n if (right > LARGE_PAGE_THRESHOLD) {\n context.report({\n message: `Fetching ${right} results at once can be slow.`,\n severity: 'warning',\n help: 'Consider breaking into smaller batches and/or using cursor-based pagination.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { walk } from '../walker'\n\nconst MAX_JOINS = 10\n\n/**\n * Rule: many-joins\n *\n * Detects queries with more than 10 dereference operators (->).\n */\nexport const manyJoins: Rule = {\n id: 'many-joins',\n name: 'Many Joins',\n description: 'This query uses many joins and may have poor performance.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n let joinCount = 0\n\n walk(ast, (node) => {\n if (node.type === 'Deref') {\n joinCount++\n }\n })\n\n if (joinCount > MAX_JOINS) {\n context.report({\n message: `This query uses ${joinCount} joins (->), which may cause poor performance.`,\n severity: 'warning',\n help: 'Consider denormalizing data, using fewer reference expansions, or splitting into multiple queries.',\n })\n }\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Rule: match-on-id\n *\n * Detects using the `match` operator on `_id`, which may not work as expected\n * because `match` is designed for full-text matching.\n */\nexport const matchOnId: Rule = {\n id: 'match-on-id',\n name: 'Match on ID',\n description: '`match` on `_id` may not work as expected.',\n severity: 'info',\n category: 'correctness',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (op !== 'match') return\n\n const left = (node as { left?: ExprNode }).left\n\n // Check if left side is an AccessAttribute for _id\n if (left && left.type === 'AccessAttribute') {\n const name = (left as { name?: string }).name\n if (name === '_id') {\n context.report({\n message:\n '`match` is designed for full-text matching and may not work as expected on `_id`.',\n severity: 'info',\n help: 'Consider using `==` for exact matches or `string::startsWith()` for prefix matching.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\nconst COMPARISON_OPS = ['==', '!=', '<', '>', '<=', '>=']\n\n/**\n * Check if an expression tree contains a Parent reference (^)\n */\nfunction containsParentRef(node: ExprNode): boolean {\n let found = false\n walk(node, (n) => {\n if (n.type === 'Parent') {\n found = true\n }\n })\n return found\n}\n\n/**\n * Check if a node is a \"literal\" for purposes of this rule.\n * Literals include: Value, Parameter, now()\n */\nfunction isLiteral(node: ExprNode): boolean {\n if (node.type === 'Value') return true\n if (node.type === 'Parameter') return true\n\n // now() is considered a literal (computed at query time, not per-document)\n if (node.type === 'FuncCall' && node.name === 'now') return true\n\n // Arithmetic on literals is still a literal (e.g., 2+1)\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (['+', '-', '*', '/'].includes(op ?? '')) {\n const left = (node as { left?: ExprNode }).left\n const right = (node as { right?: ExprNode }).right\n if (left && right && isLiteral(left) && isLiteral(right)) {\n return true\n }\n }\n }\n\n return false\n}\n\n/**\n * Rule: non-literal-comparison\n *\n * Detects comparisons where both sides are non-literal expressions,\n * which cannot use indices efficiently.\n */\nexport const nonLiteralComparison: Rule = {\n id: 'non-literal-comparison',\n name: 'Non-Literal Comparison',\n description: 'Comparisons between two non-literal fields are slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, walkContext) => {\n // Only check inside filters\n if (!walkContext.inFilter) return\n\n if (node.type === 'OpCall') {\n const op = (node as { op?: string }).op\n if (!COMPARISON_OPS.includes(op ?? '')) return\n\n const left = (node as { left?: ExprNode }).left\n const right = (node as { right?: ExprNode }).right\n\n if (!left || !right) return\n\n // Exempt if either side references parent scope\n if (containsParentRef(left) || containsParentRef(right)) return\n\n // Flag if both sides are non-literals\n if (!isLiteral(left) && !isLiteral(right)) {\n context.report({\n message: 'Comparing two non-literal expressions prevents efficient index usage.',\n severity: 'warning',\n help: 'Consider adding additional filters to reduce the search space.',\n })\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Check if a node is a simple attribute (can use index for sorting)\n */\nfunction isSimpleAttribute(node: ExprNode): boolean {\n return node.type === 'AccessAttribute'\n}\n\n/**\n * Check if a node is an allowed wrapped attribute:\n * - lower(attribute)\n * - dateTime(attribute)\n * - geo::distance(attribute, constant)\n */\nfunction isAllowedWrappedAttribute(node: ExprNode): boolean {\n if (node.type !== 'FuncCall') return false\n\n const name = node.name as string\n const namespace = (node as { namespace?: string }).namespace\n const args = (node as { args?: ExprNode[] }).args ?? []\n\n // lower(attribute)\n if (name === 'lower' && namespace === 'global' && args.length === 1) {\n const arg = args[0]\n return arg !== undefined && isSimpleAttribute(arg)\n }\n\n // dateTime(attribute)\n if (name === 'dateTime' && namespace === 'global' && args.length === 1) {\n const arg = args[0]\n return arg !== undefined && isSimpleAttribute(arg)\n }\n\n // geo::distance(attribute, constant)\n if (name === 'distance' && namespace === 'geo' && args.length === 2) {\n const arg0 = args[0]\n const arg1 = args[1]\n return (\n arg0 !== undefined && arg1 !== undefined && isSimpleAttribute(arg0) && arg1.type === 'Value'\n )\n }\n\n return false\n}\n\n/**\n * Check if an order argument is valid (can use index)\n */\nfunction isValidOrderArg(node: ExprNode): boolean {\n // Direct attribute access\n if (isSimpleAttribute(node)) return true\n\n // Allowed function wrappers\n if (isAllowedWrappedAttribute(node)) return true\n\n // Asc/Desc wrappers - check the base\n if (node.type === 'Asc' || node.type === 'Desc') {\n const base = (node as { base?: ExprNode }).base\n if (base) {\n return isSimpleAttribute(base) || isAllowedWrappedAttribute(base)\n }\n }\n\n return false\n}\n\n/**\n * Rule: order-on-expr\n *\n * Detects order() calls with computed expressions that can't use indices.\n */\nexport const orderOnExpr: Rule = {\n id: 'order-on-expr',\n name: 'Order on Expression',\n description: 'Ordering on computed values is slow.',\n severity: 'warning',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node) => {\n if (node.type === 'PipeFuncCall') {\n const name = (node as { name?: string }).name\n if (name !== 'order') return\n\n const args = (node as { args?: ExprNode[] }).args ?? []\n\n for (const arg of args) {\n if (!isValidOrderArg(arg)) {\n context.report({\n message: 'Ordering on computed expression prevents index usage.',\n severity: 'warning',\n help: 'Use simple attributes or allowed functions (lower, dateTime, geo::distance) for ordering.',\n })\n }\n }\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\nimport type { ExprNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Get the base attribute name from a dereference chain.\n * e.g., author->name returns \"author\"\n */\nfunction getDerefBaseName(node: ExprNode): string | null {\n if (node.type !== 'Deref') return null\n\n const base = (node as { base?: ExprNode }).base\n if (!base) return null\n\n // Walk down to find the root AccessAttribute\n let current = base\n while (current.type === 'AccessAttribute' && (current as { base?: ExprNode }).base) {\n current = (current as { base: ExprNode }).base\n }\n\n if (current.type === 'AccessAttribute') {\n return (current as { name?: string }).name ?? null\n }\n\n return null\n}\n\n/**\n * Rule: repeated-dereference\n *\n * Detects multiple dereference operations on the same attribute\n * within a single projection.\n */\nexport const repeatedDereference: Rule = {\n id: 'repeated-dereference',\n name: 'Repeated Dereference',\n description: 'Repeatedly resolving the same reference is inefficient.',\n severity: 'info',\n category: 'performance',\n\n check(ast, context) {\n walk(ast, (node, _walkContext) => {\n // Only check Projection nodes\n if (node.type !== 'Projection') return\n\n const projectionExpr = (node as { expr?: ExprNode }).expr\n if (!projectionExpr) return\n\n // Track dereferenced attributes in this projection\n const derefCounts = new Map<string, number>()\n\n walk(projectionExpr, (innerNode) => {\n if (innerNode.type === 'Deref') {\n const baseName = getDerefBaseName(innerNode)\n if (baseName) {\n derefCounts.set(baseName, (derefCounts.get(baseName) ?? 0) + 1)\n }\n }\n })\n\n // Report any repeated dereferences\n for (const [name, count] of derefCounts) {\n if (count > 1) {\n context.report({\n message: `Reference '${name}' is dereferenced ${count} times. Consider a single sub-projection.`,\n severity: 'info',\n help: `Replace multiple '${name}->...' with '${name}->{ ... }' to resolve the reference once.`,\n })\n }\n }\n })\n },\n}\n","/**\n * Rule: unknown-field\n *\n * Detects when a query projects a field that doesn't exist in the schema\n * for the document type being queried. This catches typos like `{ titel }`\n * when you meant `{ title }`.\n *\n * This rule requires a schema to function. Without a schema, it is skipped.\n */\n\nimport type { Rule, RuleContext, Suggestion } from '@sanity/lint-core'\nimport type { ExprNode, MapNode, ObjectNode, OpCallNode, ProjectionNode } from 'groq-js'\nimport { walk } from '../walker'\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length\n const n = b.length\n\n if (m === 0) return n\n if (n === 0) return m\n\n let prevRow = new Array<number>(n + 1)\n let currRow = new Array<number>(n + 1)\n\n for (let j = 0; j <= n; j++) {\n prevRow[j] = j\n }\n\n for (let i = 1; i <= m; i++) {\n currRow[0] = i\n\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n currRow[j] = Math.min(\n (prevRow[j] ?? 0) + 1,\n (currRow[j - 1] ?? 0) + 1,\n (prevRow[j - 1] ?? 0) + cost\n )\n }\n\n ;[prevRow, currRow] = [currRow, prevRow]\n }\n\n return prevRow[n] ?? 0\n}\n\n/**\n * Find similar field names from a list\n */\nfunction findSimilarFields(\n fieldName: string,\n availableFields: string[],\n maxDistance = 3\n): string[] {\n return availableFields\n .map((f) => ({\n field: f,\n distance: levenshteinDistance(fieldName.toLowerCase(), f.toLowerCase()),\n }))\n .filter((f) => f.distance <= maxDistance && f.distance > 0)\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 3)\n .map((f) => f.field)\n}\n\n/**\n * Extract document type from a filter constraint\n * Returns the type name if found, null otherwise\n */\nfunction extractTypeFromFilter(node: ExprNode): string | null {\n if (node.type === 'OpCall') {\n const opNode = node as OpCallNode\n if (opNode.op !== '==') return null\n\n const { left, right } = opNode\n\n // Check _type == \"value\"\n if (\n left.type === 'AccessAttribute' &&\n left.name === '_type' &&\n right.type === 'Value' &&\n typeof right.value === 'string'\n ) {\n return right.value\n }\n\n // Check \"value\" == _type\n if (\n right.type === 'AccessAttribute' &&\n right.name === '_type' &&\n left.type === 'Value' &&\n typeof left.value === 'string'\n ) {\n return left.value\n }\n }\n\n // Check for And conditions: _type == \"post\" && ...\n if (node.type === 'And') {\n const leftType = extractTypeFromFilter(node.left)\n if (leftType) return leftType\n return extractTypeFromFilter(node.right)\n }\n\n return null\n}\n\n/**\n * Find the document type by examining a Filter node\n */\nfunction findDocumentTypeFromFilter(filterBase: ExprNode): string | null {\n if (filterBase.type === 'Filter') {\n return extractTypeFromFilter(filterBase.expr)\n }\n return null\n}\n\n/**\n * Get field names for a document type from schema\n */\nfunction getFieldsForType(\n schema: { type: string; name: string; attributes?: Record<string, unknown> }[],\n typeName: string\n): string[] {\n const doc = schema.find((item) => item.type === 'document' && item.name === typeName)\n if (!doc?.attributes) return []\n return Object.keys(doc.attributes)\n}\n\n/**\n * Built-in Sanity fields that exist on all documents\n */\nconst BUILT_IN_FIELDS = new Set(['_id', '_type', '_rev', '_createdAt', '_updatedAt'])\n\n/**\n * Check fields in an Object node against schema\n */\nfunction checkObjectFields(\n objNode: ObjectNode,\n documentType: string,\n availableFieldsSet: Set<string>,\n schemaFields: string[],\n context: RuleContext\n): void {\n for (const attr of objNode.attributes) {\n if (attr.type === 'ObjectAttributeValue') {\n // Get the field being accessed\n const value = attr.value\n if (value.type === 'AccessAttribute' && !value.base) {\n const fieldName = value.name\n\n // Skip if it's a known field\n if (availableFieldsSet.has(fieldName)) continue\n\n // Unknown field - report it\n const similarFields = findSimilarFields(fieldName, [...availableFieldsSet])\n\n const suggestions: Suggestion[] = similarFields.map((f) => ({\n description: `Change to \"${f}\"`,\n replacement: f,\n }))\n\n context.report({\n message: `Field \"${fieldName}\" does not exist on type \"${documentType}\"`,\n severity: 'warning',\n help:\n similarFields.length > 0\n ? `Did you mean: ${similarFields.map((f) => `\"${f}\"`).join(', ')}?`\n : `Available fields: ${schemaFields.slice(0, 5).join(', ')}${schemaFields.length > 5 ? '...' : ''}`,\n suggestions,\n })\n }\n }\n }\n}\n\nexport const unknownField: Rule = {\n id: 'unknown-field',\n name: 'Unknown Field',\n description: 'Field in projection does not exist in schema',\n severity: 'warning',\n category: 'correctness',\n requiresSchema: true,\n\n check(ast, context) {\n const { schema } = context\n if (!schema) return\n\n walk(ast, (node) => {\n // Handle Map nodes: *[_type == \"post\"]{ title }\n // AST structure: Map { base: Filter, expr: Projection { base: This, expr: Object } }\n if (node.type === 'Map') {\n const mapNode = node as MapNode\n const documentType = findDocumentTypeFromFilter(mapNode.base)\n if (!documentType) return\n\n // The expr should be a Projection\n if (mapNode.expr.type !== 'Projection') return\n const projection = mapNode.expr as ProjectionNode\n\n // The projection's expr should be an Object\n if (projection.expr.type !== 'Object') return\n const objNode = projection.expr as ObjectNode\n\n // Get available fields for this type\n const schemaFields = getFieldsForType(\n schema as { type: string; name: string; attributes?: Record<string, unknown> }[],\n documentType\n )\n if (schemaFields.length === 0) return\n\n const availableFields = [...schemaFields, ...BUILT_IN_FIELDS]\n const availableFieldsSet = new Set(availableFields)\n\n checkObjectFields(objNode, documentType, availableFieldsSet, schemaFields, context)\n }\n })\n },\n}\n","import type { Rule } from '@sanity/lint-core'\n\nconst TEN_KB = 10 * 1024\n\n/**\n * Rule: very-large-query\n *\n * Detects queries larger than 10KB which may execute slowly.\n */\nexport const veryLargeQuery: Rule = {\n id: 'very-large-query',\n name: 'Very Large Query',\n description: 'This query is very large and may execute slowly.',\n severity: 'warning',\n category: 'performance',\n\n check(_ast, context) {\n if (context.queryLength > TEN_KB) {\n context.report({\n message: `This query is ${formatSize(context.queryLength)}, which is very large and may execute slowly. It may be deprioritized by the server.`,\n severity: 'warning',\n help: 'Consider breaking the query into smaller parts or simplifying projections.',\n })\n }\n },\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)}KB`\n }\n return `${bytes} bytes`\n}\n","import type { Rule } from '@sanity/lint-core'\nimport { computedValueInFilter } from './computed-value-in-filter'\nimport { countInCorrelatedSubquery } from './count-in-correlated-subquery'\nimport { deepPagination } from './deep-pagination'\nimport { deepPaginationParam } from './deep-pagination-param'\nimport { extremelyLargeQuery } from './extremely-large-query'\nimport { invalidTypeFilter } from './invalid-type-filter'\nimport { joinInFilter } from './join-in-filter'\nimport { joinToGetId } from './join-to-get-id'\nimport { largePages } from './large-pages'\nimport { manyJoins } from './many-joins'\nimport { matchOnId } from './match-on-id'\nimport { nonLiteralComparison } from './non-literal-comparison'\nimport { orderOnExpr } from './order-on-expr'\nimport { repeatedDereference } from './repeated-dereference'\nimport { unknownField } from './unknown-field'\nimport { veryLargeQuery } from './very-large-query'\n\n/**\n * All available lint rules\n */\nexport const rules: Rule[] = [\n // Performance - query size\n veryLargeQuery,\n extremelyLargeQuery,\n\n // Performance - joins\n joinInFilter,\n joinToGetId,\n manyJoins,\n repeatedDereference,\n\n // Performance - pagination\n deepPagination,\n deepPaginationParam,\n largePages,\n\n // Performance - filtering\n computedValueInFilter,\n nonLiteralComparison,\n\n // Performance - ordering\n orderOnExpr,\n\n // Performance - aggregation\n countInCorrelatedSubquery,\n\n // Correctness\n matchOnId,\n\n // Schema-aware correctness (requires schema to run)\n invalidTypeFilter,\n unknownField,\n]\n\n/**\n * Rules indexed by ID for quick lookup\n */\nexport const rulesById: Record<string, Rule> = Object.fromEntries(\n rules.map((rule) => [rule.id, rule])\n)\n\n// Re-export individual rules for direct imports\nexport {\n computedValueInFilter,\n countInCorrelatedSubquery,\n deepPagination,\n deepPaginationParam,\n extremelyLargeQuery,\n invalidTypeFilter,\n joinInFilter,\n joinToGetId,\n largePages,\n manyJoins,\n matchOnId,\n nonLiteralComparison,\n orderOnExpr,\n repeatedDereference,\n unknownField,\n veryLargeQuery,\n}\n","/**\n * WASM-based linter for pure GROQ rules\n *\n * Uses @sanity/groq-wasm for high-performance linting of GROQ queries.\n * Falls back gracefully if WASM is not available.\n */\n\nimport type { Finding } from '@sanity/lint-core'\n\n// WASM module state\nlet wasmAvailable = false\nlet wasmInitialized = false\nlet wasmInitPromise: Promise<boolean> | null = null\n\n// Import types for the WASM module\ntype WasmModule = typeof import('@sanity/groq-wasm')\nlet wasmModule: WasmModule | null = null\n\n/**\n * Rules available in the WASM linter (from Rust groq-lint)\n */\nexport const WASM_RULES = new Set([\n 'join-in-filter',\n 'join-to-get-id',\n 'computed-value-in-filter',\n 'match-on-id',\n 'order-on-expr',\n 'deep-pagination',\n 'large-pages',\n 'non-literal-comparison',\n 'repeated-dereference',\n 'count-in-correlated-subquery',\n 'very-large-query',\n 'extremely-large-query',\n 'many-joins',\n])\n\n/**\n * Check if a rule is available in WASM\n */\nexport function isWasmRule(ruleId: string): boolean {\n return WASM_RULES.has(ruleId)\n}\n\n/**\n * Initialize the WASM linter\n *\n * Safe to call multiple times. Returns true if WASM is available.\n */\nexport async function initWasmLinter(): Promise<boolean> {\n if (wasmInitialized) {\n return wasmAvailable\n }\n\n if (wasmInitPromise) {\n return wasmInitPromise\n }\n\n wasmInitPromise = doInit()\n return wasmInitPromise\n}\n\nasync function doInit(): Promise<boolean> {\n try {\n // Dynamic import to avoid bundling issues\n wasmModule = await import('@sanity/groq-wasm')\n await wasmModule.initWasm()\n wasmAvailable = true\n wasmInitialized = true\n return true\n } catch {\n // WASM not available - will fall back to TS rules\n wasmAvailable = false\n wasmInitialized = true\n return false\n }\n}\n\n/**\n * Check if WASM linter is available\n */\nexport function isWasmAvailable(): boolean {\n return wasmAvailable\n}\n\n/**\n * Lint a query using WASM\n *\n * @param query - The GROQ query to lint\n * @param enabledRules - Optional set of rule IDs to enable (all if not provided)\n * @returns Array of findings from WASM linter\n * @throws If WASM is not initialized\n */\nexport function lintWithWasm(query: string, enabledRules?: Set<string>): Finding[] {\n if (!wasmAvailable || !wasmModule) {\n throw new Error('WASM linter not initialized. Call initWasmLinter() first.')\n }\n\n // Get findings from WASM\n const wasmFindings = wasmModule.lint(query)\n\n // Filter by enabled rules if provided\n if (enabledRules) {\n return wasmFindings.filter((f) => enabledRules.has(f.ruleId))\n }\n\n return wasmFindings\n}\n\n/**\n * Lint a query using WASM (async version)\n *\n * Automatically initializes WASM if needed.\n */\nexport async function lintWithWasmAsync(\n query: string,\n enabledRules?: Set<string>\n): Promise<Finding[]> {\n const available = await initWasmLinter()\n if (!available) {\n return [] // Return empty - caller should use TS fallback\n }\n return lintWithWasm(query, enabledRules)\n}\n","import { parse, type SchemaType } from 'groq-js'\nimport type { Finding, Rule, RuleContext, LinterConfig } from '@sanity/lint-core'\nimport { rules as allRules } from './rules'\nimport { initWasmLinter, isWasmAvailable, isWasmRule, lintWithWasm } from './wasm-linter'\n\n/**\n * Result of linting a query\n */\nexport interface LintResult {\n /** The query that was linted */\n query: string\n /** Findings from the lint rules */\n findings: Finding[]\n /** Whether parsing failed */\n parseError?: string\n}\n\n/**\n * Options for linting\n */\nexport interface LintOptions {\n /** Linter configuration */\n config?: LinterConfig\n /** Schema for schema-aware rules */\n schema?: SchemaType\n /** Force use of TypeScript rules even when WASM is available */\n forceTs?: boolean\n}\n\n/**\n * Initialize the WASM linter for better performance\n *\n * Call this once at application startup. The linter will automatically\n * fall back to TypeScript rules if WASM is not available.\n *\n * @returns Promise that resolves to true if WASM is available\n *\n * @example\n * ```typescript\n * import { initLinter, lint } from '@sanity/groq-lint'\n *\n * // Optional: Initialize WASM for better performance\n * await initLinter()\n *\n * // Now lint() will use WASM for pure GROQ rules\n * const result = lint('*[_type == \"post\"]{ author-> }')\n * ```\n */\nexport async function initLinter(): Promise<boolean> {\n return initWasmLinter()\n}\n\n/**\n * Lint a GROQ query\n *\n * Uses WASM for pure GROQ rules (if available) and TypeScript for schema-aware rules.\n * Call `initLinter()` first to enable WASM support.\n *\n * @param query - The GROQ query string to lint\n * @param options - Optional configuration and schema\n * @returns Lint result with findings\n */\nexport function lint(query: string, options?: LintOptions): LintResult {\n const { config, schema, forceTs = false } = options ?? {}\n const findings: Finding[] = []\n\n // Handle empty query\n if (!query.trim()) {\n return { query, findings }\n }\n\n // Get enabled rules, filtering out schema-requiring rules if no schema provided\n const enabledRules = getEnabledRules(config, schema)\n\n // Split rules into WASM and TS rules\n const wasmRuleIds = new Set<string>()\n const tsRules: Rule[] = []\n\n for (const rule of enabledRules) {\n if (!forceTs && isWasmAvailable() && isWasmRule(rule.id)) {\n wasmRuleIds.add(rule.id)\n } else {\n tsRules.push(rule)\n }\n }\n\n // Run WASM rules first (if available)\n const wasmFindings: Finding[] = []\n if (wasmRuleIds.size > 0 && isWasmAvailable()) {\n try {\n const wf = lintWithWasm(query, wasmRuleIds)\n wasmFindings.push(...wf)\n } catch {\n // WASM failed - fall back to TS rules\n for (const ruleId of wasmRuleIds) {\n const rule = enabledRules.find((r) => r.id === ruleId)\n if (rule) {\n tsRules.push(rule)\n }\n }\n wasmRuleIds.clear()\n }\n }\n\n // Parse the query for TS rules (only if we have TS rules to run)\n let ast\n if (tsRules.length > 0) {\n try {\n ast = parse(query)\n } catch (error) {\n return {\n query,\n findings: wasmFindings, // Return any WASM findings we got\n parseError: error instanceof Error ? error.message : 'Unknown parse error',\n }\n }\n }\n\n // Track which rules have fired (for supersedes logic)\n const firedRules = new Set<string>()\n\n // Mark WASM rules that found issues\n for (const f of wasmFindings) {\n firedRules.add(f.ruleId)\n }\n\n // Run TS rules\n const tsFindings: Finding[] = []\n if (ast) {\n for (const rule of tsRules) {\n const ruleFindings: Finding[] = []\n\n const context: RuleContext = {\n query,\n queryLength: query.length,\n ...(schema && { schema }),\n report: (finding) => {\n ruleFindings.push({\n ...finding,\n ruleId: rule.id,\n severity: finding.severity ?? rule.severity,\n })\n },\n }\n\n rule.check(ast, context)\n\n if (ruleFindings.length > 0) {\n firedRules.add(rule.id)\n tsFindings.push(...ruleFindings)\n }\n }\n }\n\n // Combine all findings\n const allFindings = [...wasmFindings, ...tsFindings]\n\n // Apply supersedes logic\n for (const finding of allFindings) {\n const rule = enabledRules.find((r) => r.id === finding.ruleId)\n if (rule?.supersedes) {\n // Check if any superseding rule has fired\n const isSuperseded = enabledRules.some(\n (r) => r.supersedes?.includes(finding.ruleId) && firedRules.has(r.id)\n )\n if (!isSuperseded) {\n findings.push(finding)\n }\n } else {\n findings.push(finding)\n }\n }\n\n return { query, findings }\n}\n\n/**\n * Get enabled rules based on configuration\n */\nfunction getEnabledRules(config?: LinterConfig, schema?: SchemaType): Rule[] {\n let rules = allRules\n\n // Filter out schema-requiring rules if no schema is provided\n if (!schema) {\n rules = rules.filter((rule) => !rule.requiresSchema)\n }\n\n if (!config?.rules) {\n return rules\n }\n\n return rules.filter((rule) => {\n const ruleConfig = config.rules?.[rule.id]\n if (ruleConfig === false) {\n return false\n }\n if (typeof ruleConfig === 'object' && ruleConfig.enabled === false) {\n return false\n }\n return true\n })\n}\n\n/**\n * Lint multiple queries\n *\n * @param queries - Array of queries to lint\n * @param options - Optional configuration and schema\n * @returns Array of lint results\n */\nexport function lintMany(queries: string[], options?: LintOptions): LintResult[] {\n return queries.map((query) => lint(query, options))\n}\n"],"mappings":"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","names":["containsParentRef","containsParentRef","isLiteral","levenshteinDistance","formatSize","rules"]}
package/dist/cli.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node