@sanity-labs/prettier-plugin-groq 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/parser.ts","../src/printer.ts","../src/wasm-printer.ts"],"sourcesContent":["import type { Plugin, SupportLanguage, Parser, Printer } from 'prettier'\nimport { doc } from 'prettier'\nimport { groqParser, type GroqAst } from './parser.js'\nimport {\n createWasmGroqPrinter,\n initWasmFormatter,\n isWasmFormatterAvailable,\n} from './wasm-printer.js'\n\nconst languages: SupportLanguage[] = [\n {\n name: 'GROQ',\n parsers: ['groq'],\n extensions: ['.groq'],\n vscodeLanguageIds: ['groq'],\n },\n]\n\nconst parsers: Record<string, Parser<GroqAst>> = {\n groq: groqParser,\n}\n\nconst printers: Record<string, Printer> = {\n 'groq-ast': createWasmGroqPrinter(doc.builders),\n}\n\nconst plugin: Plugin = {\n languages,\n parsers,\n printers,\n}\n\nexport default plugin\nexport { languages, parsers, printers, initWasmFormatter, isWasmFormatterAvailable }\n\n// Re-export TS printer for direct use\nexport { createGroqPrinter } from './printer.js'\n","import { parse as groqParse } from 'groq-js'\nimport type { Parser } from 'prettier'\nimport type { ExprNode } from 'groq-js'\n\nexport interface GroqAst {\n type: 'groq-root'\n node: ExprNode\n}\n\nfunction locStart(_node: GroqAst): number {\n // groq-js doesn't provide location info, so we return 0\n return 0\n}\n\nfunction locEnd(_node: GroqAst): number {\n // groq-js doesn't provide location info, so we return 0\n return 0\n}\n\nexport const groqParser: Parser<GroqAst> = {\n parse(text: string): GroqAst {\n const trimmed = text.trim()\n if (!trimmed) {\n throw new Error('Empty GROQ query')\n }\n const node = groqParse(trimmed)\n return {\n type: 'groq-root',\n node,\n }\n },\n astFormat: 'groq-ast',\n locStart,\n locEnd,\n}\n","import type { AstPath, Doc, Printer, doc } from 'prettier'\nimport type { ExprNode } from 'groq-js'\nimport type { GroqAst } from './parser.js'\n\ntype Builders = typeof doc.builders\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PrintFn = (path: AstPath<any>) => Doc\n\n// Note: groq-js TypeScript types are incomplete - many node types exist at runtime\n// but aren't in the type definitions (ObjectSplat, Range, SelectAlternative, etc.)\n// We use 'any' here to handle all runtime node types correctly.\nfunction createPrintNode(builders: Builders) {\n const { group, indent, join, line, softline } = builders\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return function printNode(node: any, print: PrintFn, path: AstPath<any>): Doc {\n switch (node.type) {\n case 'Everything':\n return '*'\n\n case 'This':\n return '@'\n\n case 'Parent':\n return '^'\n\n case 'Value':\n return printValue(node.value)\n\n case 'Parameter':\n return `$${node.name}`\n\n case 'AccessAttribute':\n if (node.base) {\n const base = path.call((p) => printNode(p.node, print, p), 'base')\n // Don't add dot after dereference\n if (isDerefNode(node.base)) {\n return [base, node.name]\n }\n return [base, '.', node.name]\n }\n return node.name\n\n case 'AccessElement':\n return [\n path.call((p) => printNode(p.node, print, p), 'base'),\n '[',\n path.call((p) => printNode(p.node, print, p), 'index'),\n ']',\n ]\n\n case 'Filter':\n return [\n path.call((p) => printNode(p.node, print, p), 'base'),\n group([\n '[',\n indent([softline, path.call((p) => printNode(p.node, print, p), 'expr')]),\n softline,\n ']',\n ]),\n ]\n\n case 'Slice':\n return [\n path.call((p) => printNode(p.node, print, p), 'base'),\n '[',\n String(node.left),\n node.isInclusive ? '..' : '...',\n String(node.right),\n ']',\n ]\n\n case 'Map': {\n const base = path.call((p) => printNode(p.node, print, p), 'base')\n const expr = path.call((p) => printNode(p.node, print, p), 'expr')\n // Special case: when base is ArrayCoerce and expr starts with Deref(This),\n // collapse to the common `field[]->{ ... }` notation instead of `field[] @->{ ... }`\n if (node.base.type === 'ArrayCoerce' && isDerefOfThis(node.expr)) {\n // No space needed - the expr will start with ->\n return [base, expr]\n }\n return [base, ' ', expr]\n }\n\n case 'Projection': {\n const base = path.call((p) => printNode(p.node, print, p), 'base')\n const expr = path.call((p) => printNode(p.node, print, p), 'expr')\n // If base is This (@), omit it\n if (node.base.type === 'This') {\n return expr\n }\n // No space after dereference (author->{ name } not author-> { name })\n if (node.base.type === 'Deref') {\n return [base, expr]\n }\n return [base, ' ', expr]\n }\n\n case 'Object': {\n if (node.attributes.length === 0) {\n return '{}'\n }\n const attrs = path.map((p) => printNode(p.node, print, p), 'attributes')\n return group(['{', indent([line, join([',', line], attrs)]), line, '}'])\n }\n\n case 'ObjectAttributeValue': {\n const value = path.call((p) => printNode(p.node, print, p), 'value')\n // Check if we can use shorthand (omit the key entirely)\n // Shorthand is valid when the leading attribute name matches the key\n // e.g., { title } for { \"title\": title }\n // e.g., { author->{ name } } for { \"author\": author->{ name } }\n // e.g., { categories[]->{ title } } for { \"categories\": categories[]->{ title } }\n const leadingAttr = getLeadingAttribute(node.value)\n if (leadingAttr === node.name) {\n return value\n }\n // For all other cases, we MUST quote the key in GROQ\n // e.g., { \"slug\": slug.current } not { slug: slug.current }\n return ['\"', escapeString(node.name), '\": ', value]\n }\n\n case 'ObjectSplat':\n if (node.value) {\n return ['...', path.call((p) => printNode(p.node, print, p), 'value')]\n }\n return '...'\n\n case 'ObjectConditionalSplat':\n return [\n '...',\n path.call((p) => printNode(p.node, print, p), 'condition'),\n ' => ',\n path.call((p) => printNode(p.node, print, p), 'value'),\n ]\n\n case 'Array': {\n if (node.elements.length === 0) {\n return '[]'\n }\n const elems = path.map((p) => printNode(p.node, print, p), 'elements')\n return group(['[', indent([softline, join([',', line], elems)]), softline, ']'])\n }\n\n case 'ArrayElement':\n if (node.isSplat) {\n return ['...', path.call((p) => printNode(p.node, print, p), 'value')]\n }\n return path.call((p) => printNode(p.node, print, p), 'value')\n\n case 'Tuple': {\n const members = path.map((p) => printNode(p.node, print, p), 'members')\n return ['(', join([',', ' '], members), ')']\n }\n\n case 'Range':\n return [\n path.call((p) => printNode(p.node, print, p), 'left'),\n node.isInclusive ? '..' : '...',\n path.call((p) => printNode(p.node, print, p), 'right'),\n ]\n\n case 'And':\n return group([\n path.call((p) => printNode(p.node, print, p), 'left'),\n indent([line, '&& ', path.call((p) => printNode(p.node, print, p), 'right')]),\n ])\n\n case 'Or':\n return group([\n path.call((p) => printNode(p.node, print, p), 'left'),\n indent([line, '|| ', path.call((p) => printNode(p.node, print, p), 'right')]),\n ])\n\n case 'Not':\n return ['!', path.call((p) => printNode(p.node, print, p), 'base')]\n\n case 'Neg':\n return ['-', path.call((p) => printNode(p.node, print, p), 'base')]\n\n case 'Pos':\n return ['+', path.call((p) => printNode(p.node, print, p), 'base')]\n\n case 'OpCall': {\n const left = path.call((p) => printNode(p.node, print, p), 'left')\n const right = path.call((p) => printNode(p.node, print, p), 'right')\n return [left, ` ${node.op} `, right]\n }\n\n case 'FuncCall': {\n const name =\n node.namespace && node.namespace !== 'global'\n ? `${node.namespace}::${node.name}`\n : node.name\n if (node.args.length === 0) {\n return `${name}()`\n }\n const args = path.map((p) => printNode(p.node, print, p), 'args')\n return group([name, '(', indent([softline, join([',', line], args)]), softline, ')'])\n }\n\n case 'PipeFuncCall': {\n const base = path.call((p) => printNode(p.node, print, p), 'base')\n const name =\n node.namespace && node.namespace !== 'global'\n ? `${node.namespace}::${node.name}`\n : node.name\n const args = path.map((p) => printNode(p.node, print, p), 'args')\n const argsDoc = args.length === 0 ? '' : group(['(', join([', '], args), ')'])\n return group([base, indent([line, '| ', name, argsDoc])])\n }\n\n case 'Deref': {\n // When base is This (@), omit it - this is the implicit dereference in array traversals\n // e.g., categories[]->{ title } should not become categories[] @->{ title }\n if (node.base.type === 'This') {\n return '->'\n }\n const base = path.call((p) => printNode(p.node, print, p), 'base')\n return [base, '->']\n }\n\n case 'Asc':\n return [path.call((p) => printNode(p.node, print, p), 'base'), ' asc']\n\n case 'Desc':\n return [path.call((p) => printNode(p.node, print, p), 'base'), ' desc']\n\n case 'Group':\n return ['(', path.call((p) => printNode(p.node, print, p), 'base'), ')']\n\n case 'ArrayCoerce':\n return [path.call((p) => printNode(p.node, print, p), 'base'), '[]']\n\n case 'FlatMap':\n return [\n path.call((p) => printNode(p.node, print, p), 'base'),\n '[]',\n path.call((p) => printNode(p.node, print, p), 'expr'),\n ]\n\n case 'Select': {\n const alternatives = path.map((p) => printNode(p.node, print, p), 'alternatives')\n const fallback = node.fallback\n ? path.call((p) => printNode(p.node, print, p), 'fallback')\n : null\n const allArgs = fallback ? [...alternatives, fallback] : alternatives\n return group(['select(', indent([softline, join([',', line], allArgs)]), softline, ')'])\n }\n\n case 'SelectAlternative':\n return [\n path.call((p) => printNode(p.node, print, p), 'condition'),\n ' => ',\n path.call((p) => printNode(p.node, print, p), 'value'),\n ]\n\n case 'InRange':\n return [\n path.call((p) => printNode(p.node, print, p), 'base'),\n ' in ',\n path.call((p) => printNode(p.node, print, p), 'range'),\n ]\n\n case 'Context':\n // Internal node, shouldn't appear in user queries\n return path.call((p) => printNode(p.node, print, p), 'base')\n\n default:\n // Fallback for any unhandled node types\n throw new Error(`Unknown GROQ node type: ${(node as { type: string }).type}`)\n }\n }\n}\n\nfunction printValue(value: unknown): string {\n if (value === null) {\n return 'null'\n }\n if (typeof value === 'string') {\n return `\"${escapeString(value)}\"`\n }\n if (typeof value === 'number') {\n return formatNumber(value)\n }\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false'\n }\n // Arrays and objects shouldn't appear as raw values\n return String(value)\n}\n\nfunction escapeString(s: string): string {\n let result = ''\n for (const ch of s) {\n switch (ch) {\n case '\"':\n result += '\\\\\"'\n break\n case '\\\\':\n result += '\\\\\\\\'\n break\n case '\\n':\n result += '\\\\n'\n break\n case '\\r':\n result += '\\\\r'\n break\n case '\\t':\n result += '\\\\t'\n break\n default:\n if (ch.charCodeAt(0) < 32) {\n result += `\\\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`\n } else {\n result += ch\n }\n }\n }\n return result\n}\n\nfunction formatNumber(value: number): string {\n return String(value)\n}\n\nfunction isDerefNode(node: ExprNode): boolean {\n return node.type === 'Deref'\n}\n\n/**\n * Check if a node represents a Deref of This (@->)\n * This handles both direct Deref(This) and Projection with Deref(This) as base\n */\nfunction isDerefOfThis(node: ExprNode): boolean {\n if (node.type === 'Deref' && node.base.type === 'This') {\n return true\n }\n if (node.type === 'Projection' && node.base.type === 'Deref' && node.base.base.type === 'This') {\n return true\n }\n return false\n}\n\n/**\n * Get the attribute name that can be used for shorthand in projections.\n * Returns the name only when shorthand is valid in GROQ.\n *\n * Valid shorthand cases:\n * - { title } → simple attribute access\n * - { author->{ name } } → dereference of attribute\n * - { categories[]->{ title } } → array traversal of attribute\n *\n * Invalid shorthand (returns null):\n * - { slug.current } → dot access (GROQ can't infer key)\n */\nfunction getLeadingAttribute(node: ExprNode): string | null {\n switch (node.type) {\n case 'AccessAttribute':\n // Only valid for shorthand if there's NO base (simple attribute)\n // { title } is valid, but { slug.current } is NOT\n if (!node.base) {\n return node.name\n }\n // Dot access - shorthand not valid\n return null\n case 'Deref':\n // { author->{ name } } - check if base is a simple attribute\n return getLeadingAttribute(node.base)\n case 'Projection':\n // Projection's base determines the leading attribute\n return getLeadingAttribute(node.base)\n case 'ArrayCoerce':\n // { tags[] } - check if base is a simple attribute\n return getLeadingAttribute(node.base)\n case 'Map':\n // Map's base determines the leading attribute\n return getLeadingAttribute(node.base)\n default:\n return null\n }\n}\n\nexport function createGroqPrinter(builders: Builders): Printer<GroqAst | ExprNode> {\n const printNode = createPrintNode(builders)\n\n return {\n print(path, options, print) {\n const node = path.node\n\n // Handle root wrapper\n if ('type' in node && node.type === 'groq-root') {\n return path.call(\n (p) => printNode(p.node as ExprNode, print as PrintFn, p as AstPath<ExprNode>),\n 'node'\n )\n }\n\n // Handle ExprNode\n return printNode(node as ExprNode, print as PrintFn, path as AstPath<ExprNode>)\n },\n }\n}\n","/**\n * WASM-based GROQ printer for Prettier\n *\n * Uses @sanity-labs/groq-wasm for high-performance formatting.\n * Falls back to the TypeScript printer if WASM is not available.\n */\n\nimport type { Printer, doc } from 'prettier'\nimport { createGroqPrinter } from './printer.js'\n\n// WASM module state\nlet wasmAvailable = false\nlet wasmInitialized = false\nlet wasmInitPromise: Promise<boolean> | null = null\n\n// WASM module reference\ntype WasmModule = typeof import('@sanity-labs/groq-wasm')\nlet wasmModule: WasmModule | null = null\n\n/**\n * Initialize the WASM formatter\n *\n * Call this once at application startup for best performance.\n * The plugin will fall back to TypeScript formatting if WASM is not available.\n *\n * @returns Promise that resolves to true if WASM is available\n *\n * @example\n * ```typescript\n * import { initWasmFormatter } from 'prettier-plugin-groq'\n *\n * // Optional: Initialize WASM for better performance\n * await initWasmFormatter()\n * ```\n */\nexport async function initWasmFormatter(): 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 wasmModule = await import('@sanity-labs/groq-wasm')\n await wasmModule.initWasm()\n wasmAvailable = true\n wasmInitialized = true\n return true\n } catch {\n wasmAvailable = false\n wasmInitialized = true\n return false\n }\n}\n\n/**\n * Check if WASM formatter is available\n */\nexport function isWasmFormatterAvailable(): boolean {\n return wasmAvailable\n}\n\n/**\n * Format a GROQ query using WASM\n *\n * @param query - The GROQ query to format\n * @param width - Maximum line width (default: 80)\n * @returns Formatted query string\n * @throws If WASM is not initialized\n */\nexport function formatWithWasm(query: string, width?: number): string {\n if (!wasmAvailable || !wasmModule) {\n throw new Error('WASM formatter not initialized. Call initWasmFormatter() first.')\n }\n return wasmModule.format(query, width !== undefined ? { width } : undefined)\n}\n\n/**\n * Create a WASM-accelerated GROQ printer\n *\n * Uses WASM formatting when available, falls back to TypeScript printer otherwise.\n */\nexport function createWasmGroqPrinter(builders: typeof doc.builders): Printer {\n // Create TS fallback printer\n const tsPrinter = createGroqPrinter(builders)\n\n return {\n print(path, options, print) {\n // If WASM is available and we have the original text, use WASM\n if (wasmAvailable && wasmModule && options.originalText) {\n try {\n const width = options.printWidth || 80\n const formatted = wasmModule.format(options.originalText, { width })\n // Return as a simple string Doc\n return formatted\n } catch {\n // Fall back to TS printer on error\n }\n }\n\n // Fall back to TypeScript printer\n return tsPrinter.print(path, options, print)\n },\n }\n}\n"],"mappings":"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","names":["groqParse"]}
@@ -0,0 +1,46 @@
1
+ import { doc, Printer, Parser, Plugin, SupportLanguage } from 'prettier';
2
+ import { ExprNode } from 'groq-js';
3
+
4
+ interface GroqAst {
5
+ type: 'groq-root';
6
+ node: ExprNode;
7
+ }
8
+
9
+ /**
10
+ * WASM-based GROQ printer for Prettier
11
+ *
12
+ * Uses @sanity-labs/groq-wasm for high-performance formatting.
13
+ * Falls back to the TypeScript printer if WASM is not available.
14
+ */
15
+
16
+ /**
17
+ * Initialize the WASM formatter
18
+ *
19
+ * Call this once at application startup for best performance.
20
+ * The plugin will fall back to TypeScript formatting if WASM is not available.
21
+ *
22
+ * @returns Promise that resolves to true if WASM is available
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { initWasmFormatter } from 'prettier-plugin-groq'
27
+ *
28
+ * // Optional: Initialize WASM for better performance
29
+ * await initWasmFormatter()
30
+ * ```
31
+ */
32
+ declare function initWasmFormatter(): Promise<boolean>;
33
+ /**
34
+ * Check if WASM formatter is available
35
+ */
36
+ declare function isWasmFormatterAvailable(): boolean;
37
+
38
+ type Builders = typeof doc.builders;
39
+ declare function createGroqPrinter(builders: Builders): Printer<GroqAst | ExprNode>;
40
+
41
+ declare const languages: SupportLanguage[];
42
+ declare const parsers: Record<string, Parser<GroqAst>>;
43
+ declare const printers: Record<string, Printer>;
44
+ declare const plugin: Plugin;
45
+
46
+ export { createGroqPrinter, plugin as default, initWasmFormatter, isWasmFormatterAvailable, languages, parsers, printers };
@@ -0,0 +1,46 @@
1
+ import { doc, Printer, Parser, Plugin, SupportLanguage } from 'prettier';
2
+ import { ExprNode } from 'groq-js';
3
+
4
+ interface GroqAst {
5
+ type: 'groq-root';
6
+ node: ExprNode;
7
+ }
8
+
9
+ /**
10
+ * WASM-based GROQ printer for Prettier
11
+ *
12
+ * Uses @sanity-labs/groq-wasm for high-performance formatting.
13
+ * Falls back to the TypeScript printer if WASM is not available.
14
+ */
15
+
16
+ /**
17
+ * Initialize the WASM formatter
18
+ *
19
+ * Call this once at application startup for best performance.
20
+ * The plugin will fall back to TypeScript formatting if WASM is not available.
21
+ *
22
+ * @returns Promise that resolves to true if WASM is available
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { initWasmFormatter } from 'prettier-plugin-groq'
27
+ *
28
+ * // Optional: Initialize WASM for better performance
29
+ * await initWasmFormatter()
30
+ * ```
31
+ */
32
+ declare function initWasmFormatter(): Promise<boolean>;
33
+ /**
34
+ * Check if WASM formatter is available
35
+ */
36
+ declare function isWasmFormatterAvailable(): boolean;
37
+
38
+ type Builders = typeof doc.builders;
39
+ declare function createGroqPrinter(builders: Builders): Printer<GroqAst | ExprNode>;
40
+
41
+ declare const languages: SupportLanguage[];
42
+ declare const parsers: Record<string, Parser<GroqAst>>;
43
+ declare const printers: Record<string, Printer>;
44
+ declare const plugin: Plugin;
45
+
46
+ export { createGroqPrinter, plugin as default, initWasmFormatter, isWasmFormatterAvailable, languages, parsers, printers };
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import {
2
+ createGroqPrinter,
3
+ index_default,
4
+ initWasmFormatter,
5
+ isWasmFormatterAvailable,
6
+ languages,
7
+ parsers,
8
+ printers
9
+ } from "./chunk-TUFZP5UF.js";
10
+ export {
11
+ createGroqPrinter,
12
+ index_default as default,
13
+ initWasmFormatter,
14
+ isWasmFormatterAvailable,
15
+ languages,
16
+ parsers,
17
+ printers
18
+ };
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@sanity-labs/prettier-plugin-groq",
3
+ "version": "0.1.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Prettier plugin for formatting GROQ queries",
8
+ "author": "Sanity.io <hello@sanity.io>",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/sanity-labs/sanity-lint.git",
13
+ "directory": "packages/prettier-plugin-groq"
14
+ },
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ },
22
+ "./embed": {
23
+ "types": "./dist/embed.d.ts",
24
+ "import": "./dist/embed.js",
25
+ "require": "./dist/embed.cjs"
26
+ }
27
+ },
28
+ "main": "./dist/index.cjs",
29
+ "types": "./dist/index.d.ts",
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "dependencies": {
34
+ "groq-js": "^1.14.0",
35
+ "@sanity-labs/groq-wasm": "0.1.0"
36
+ },
37
+ "peerDependencies": {
38
+ "prettier": "^3.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "prettier": "^3.4.2",
42
+ "tsup": "^8.3.5",
43
+ "typescript": "^5.7.2",
44
+ "vitest": "^2.1.8",
45
+ "@sanity-labs/lint-core": "0.1.0"
46
+ },
47
+ "keywords": [
48
+ "sanity",
49
+ "groq",
50
+ "prettier",
51
+ "prettier-plugin",
52
+ "formatter"
53
+ ],
54
+ "scripts": {
55
+ "build": "tsup",
56
+ "dev": "tsup --watch",
57
+ "typecheck": "tsc --noEmit",
58
+ "clean": "rm -rf dist"
59
+ }
60
+ }