Skip to content

feat: add TypeNode fingerprinting to dedupe more effectively#46

Open
kbrabrand wants to merge 5 commits intomainfrom
feat/deduplicate-inline-types
Open

feat: add TypeNode fingerprinting to dedupe more effectively#46
kbrabrand wants to merge 5 commits intomainfrom
feat/deduplicate-inline-types

Conversation

@kbrabrand
Copy link
Contributor

@kbrabrand kbrabrand commented Feb 10, 2026

TL;DR

Added type deduplication for GROQ query type generation to reduce output file size and improve readability.

What changed?

  • Use hashTypeNode from groq-js for identifying identical type nodes
  • Added a deduplication registry that extracts common types into reusable Inline* type aliases
  • Modified the type generation process to work in phases:
    1. Collect all type nodes from queries
    2. Build a deduplication registry
    3. Generate TypeScript types with references to extracted common types

💡 With this change we accept the fact that we don't have full control of the name of the types, since the order of appearance impacts which name is used. This leads to a cases where types, while correct, may get unintuitive names. This is a trade off..

We could work around this by special casing things, adding additional logic to use top level names as the reusable etc. but it would lead to more code and other edge cases. Open to thoughts/ideas here..

Examples:

// The whole author by slug query result use the InlineAuthor type
export type AuthorBySlugQueryResult = InlineAuthor | null

// Image palette swatches all use the InlineDominant (if that comes first)
palette: {
  dominant: InlineDominant | null
  darkMuted: InlineDominant | null
  vibrant: InlineDominant | null
}

To reduce the chance of these types being referenced in the code by consuming projects I've opted not to export these new types directly – they're made available through references from the exported types.

🚫 This PR depends on changes that hasn't yet landed in groq-js, so it's blocked by the release of a new version with the hashTypeNode function exported. Until the PR has been merged this PR is using a tagged version of groq-js where this is available. So this is ready for review, not merge.

Why make this change?

Generated type files can be pretty large and difficult to read due to repeated type structures across different queries. For example, author and image types that appear in multiple queries were being duplicated in full. This change significantly reduces duplication by extracting common structures into named types, making the generated files easier to read and improving IDE performance when working with these types.

  • CLDX-4784
  • CLDX-2251

How to test?

  1. Run the typegen command on a project with multiple GROQ queries
  2. Verify that the generated types include Inline* type aliases at the top
  3. Check that query result types reference these inline types instead of duplicating the same structure
  4. Confirm that the output file is smaller and more readable than before

Automated tests has been added, and outdated snapshots updated.

Used to derive singular type names from plural array field names
(e.g., "posts" -> "post", "categories" -> "category").
Introduces structural fingerprinting for groq-js TypeNodes to identify
duplicate object types across queries. Object types appearing 2+ times
are candidates for extraction into named type aliases.

Naming priority: _type attribute > parent field name > InlineType fallback.
All extracted types are prefixed with "Inline" (e.g., InlineAuthor).
Types with fewer than 2 non-structural attributes are skipped.
Fingerprints are cached in a WeakMap to avoid recomputation.
Copy link
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@kbrabrand kbrabrand changed the title feat: add naive singularize utility for English plurals feat: add TypeNode fingerprinting to dedupe more effectively Feb 10, 2026
@kbrabrand kbrabrand marked this pull request as ready for review February 10, 2026 13:25
@kbrabrand kbrabrand requested a review from sgulseth February 10, 2026 14:21
Splits type generation into a 3-phase pipeline:
1. Collect TypeNodes from all queries
2. Build deduplication registry by fingerprinting object types
3. Generate TS types, replacing duplicates with references

Duplicate object types appearing across queries are extracted into
shared "Inline"-prefixed type aliases, reducing generated code size
and improving readability.
@kbrabrand kbrabrand force-pushed the feat/deduplicate-inline-types branch from b7bf883 to c00b552 Compare February 10, 2026 14:41
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.

Tests! There's no unit tests that asserts the behavior, it's a bit hard to review without knowing what's expected.

Also this is only deduping types that are exactly the same? 🤔

@@ -0,0 +1,18 @@
export function singularize(name: string): string {
Copy link
Member

Choose a reason for hiding this comment

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

This one seems like a potential foot gun, why do we need to rewrite the names?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A very common case is for the field to be named plural, but since we evaluate and hoist the type inside the array the following field:

authors: Array<{ /* author type */ }>

..if used in multiple places would end up as

type InlineAuthors = { /* author type */ }
authors: Array<InlineAuthors>

I find having a plural names for singluar things confusing.

Copy link
Contributor Author

Tests! There's no unit tests that asserts the behavior, it's a bit hard to review without knowing what's expected.

Also this is only deduping types that are exactly the same? 🤔

🫡 More tests added now. Will have a look at exporting hashField from groq-js and refactor to using it instead of the custom fingerprinting.

@socket-security
Copy link

socket-security bot commented Feb 14, 2026

@kbrabrand kbrabrand force-pushed the feat/deduplicate-inline-types branch from 0014b8b to 5fa7a7c Compare February 14, 2026 22:13
@kbrabrand kbrabrand force-pushed the feat/deduplicate-inline-types branch from 5fa7a7c to 34fe055 Compare February 15, 2026 09:03
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