Skip to content

Conversation

@runeb
Copy link
Member

@runeb runeb commented Jul 22, 2025

Description

This PR introduces a node serializer which can be used as a pretty printer. The serializer takes parsed GROQ AST nodes and outputs clean, consistently formatted GROQ query strings.

The serializer implements the complete GROQ specification for string literals, operators, objects, arrays, and all query constructs. It handles proper indentation for nested structures, consistent spacing around operators, and correct escaping of string literals according to the GROQ spec.

Key features:

  • Serializes all GROQ node types including filters, projections, function calls, and pipe operations
  • Proper string escaping for all control characters defined in the GROQ spec (quotes, backslashes, newlines, tabs, etc.)
  • Consistent indentation and spacing for nested objects and arrays
  • Preserves explicit parentheses from the original AST

What to review

Reviewers should focus on:

  1. Core serializer implementation in src/serializer/ - verify that all GROQ node types are handled correctly
  2. String escaping logic in formatValue() - ensure it matches the GROQ specification for string literals
  3. Test coverage in test/serializer.test.ts - added tests covering many serializing scenarios
  4. Round-trip behavior - verify that parse(serialize(parse(query))) produces consistent results
  5. Integration - the serializer is exported from a new beta entry point

Test the formatter by:

  • Importing {serialize} from groq-js/beta
  • Parsing a GROQ query with parse()
  • Formatting the result with serialize(tree)
  • Verifying the output is valid, readable GROQ

Testing

Added test coverage including:

  • Basic value serialization (strings, numbers, booleans, null)
  • String escaping for all GROQ spec control characters (\n, \r, \t, \f, \b, ", \)
  • Object and array serializing with proper indentation
  • Complex nested query structures
  • Operator serializing and spacing
  • Function calls and pipe operations
  • Round-trip parsing validation
  • Property name shorthand vs explicit key serializing

There may be parts of the spec uncovered by current serializing tests!

@runeb runeb requested a review from sgulseth July 22, 2025 21:26
Copy link
Member

@sgulseth sgulseth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue against calling it a formatter. the groq ast doesn't contain whitespaces or comments, so you can't really use it as other formatters(prettier, gofmt, etc etc). It also requires groq query to be a valid query since we don't support partial parsing.
I would rename the api group to serializer, and call the format serializeString(). We can also put it into groq-js/beta so we don't need to commit to the api, and can change it a bit if needed.

@runeb runeb changed the title feat: pretty formatting feat: ast node text serialization for pretty printing Jul 23, 2025
@runeb runeb requested a review from sgulseth July 23, 2025 21:47
@@ -0,0 +1,535 @@
import t from 'tap'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some tests here are testing that X in gives X out. Could we change it to something like:

const queries: string[] = [
	"*",
	"@",
....
]

for (const query of queries) {
	t.test('query: ' + query, async (t) => {
    const expected = parse(query)
    const result = serializeString(tree)
    t.strictSame(parse(result), expected)
  })
}

makes it easier to add a bunch of queries to test and that we generate the same identical queries, kinda similar to round-trip only that we compare the ast and not the generated string

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your idea is much better since it focuses on the AST and wont break without having the query already formatted. No-brainer. I don't have a bunch of queries handy to add just yet, though. I looked into the generated assertions already in the project, but they seem to be individual node assertions not necessarily transferrable to testing the serializer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a collection of groq queries derived from our documentation

@runeb runeb marked this pull request as draft July 28, 2025 23:38
@runeb
Copy link
Member Author

runeb commented Jul 28, 2025

Changing to draft while refactoring beta/experimental

- Rename beta.ts to experimental.ts for clearer naming
- Make experimental module completely self-contained by re-exporting all main module functionality
- Fix TypeScript type compatibility issues between different entry points
- Update package.json exports and typesVersions to support experimental module
- Update test imports to use new experimental module name

This resolves type incompatibility issues where types from different entry points
were seen as incompatible by TypeScript, even when they were functionally identical.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants