Skip to content

feat(groq-files): add TypeScript 7.1 content mapper for .groq imports - #2037

Closed
stipsan wants to merge 1 commit into
cody/groq-file-types-ea75from
cody/groq-files-content-mapper-ea75
Closed

stipsan wants to merge 1 commit into
cody/groq-file-types-ea75from
cody/groq-files-content-mapper-ea75

Conversation

@stipsan

@stipsan stipsan commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Description

PR B of the @sanity/groq-files stack, on top of #2035 (PR A). Adds the TypeScript 7.1 content mapper so .groq imports are typed from the live buffer with no *.d.groq.ts on disk, with one schema per folder for workspaces that query several projects or datasets.

{
  "contentMappers": [
    {
      "package": "@sanity/groq-files",
      "extensions": [".groq"],
      "options": {
        "resources": [
          {"path": "src/blog", "schema": "schemas/blog.json"},
          {"path": ["src/shop", "src/checkout"], "schema": "schemas/shop.json"}
        ]
      }
    }
  ]
}
  • src/jsonrpc.ts: JSON-RPC 2.0 over stdio with Content-Length framing. Unparseable JSON is -32700 Parse error; valid JSON that is not a request, and a bad Content-Length, is -32600 Invalid Request echoing the id when there is one. Notifications get no reply. done settles after the last in-flight answer once stdin ends.
  • src/contentMapper.ts: initialize / openProject / transform / closeProject. Options are schema (one file, relative to the tsconfig, default ./schema.json) and resources ([{path: string | string[], schema}], mirroring typegen.resources in sanity.cli.ts). A .groq file is typed against the schema of the deepest resource path that contains it, falling back to schema; each resource has its own TypeGenerator. configIdentity is a hash over every schema file and watchedFiles lists them all. Malformed options are option diagnostics with a precise path (["resources", 1, "schema"]), and the well-formed rest still applies. A project without a tsconfig (configFileName === "") requires absolute paths, since TS 7.1 starts the process inside the installed package.
  • src/bin/contentMapper.ts: the always-start entry (dist/bin/contentMapper.js) referenced from package.json typescript.contentMapper.exec.

Why resources inside one entry rather than one contentMappers entry per folder: TypeScript 7.1 registers a mapper by extension only. The tsconfig entry is {package, extensions, options} with no include/glob (contentmapper.Definition in typescript-go), and two entries for .groq are rejected with TS18067: Content mapper file extension '.groq' is registered by more than one content mapper. Separate entries do work with separate extensions (.groq and .shop.groq); the smoke test proves both the rejection and that alternative, and the README documents it.

Registration is the top-level contentMappers field of tsconfig.json (not inside compilerOptions; TS 7.1.0-beta reports TS5023 for that). Sources: Announcing TypeScript 7.1 Beta, microsoft/typescript-go#4712.

What to review

  • packages/@sanity/groq-files/src/contentMapper.ts (readOptions, readResource, resourceFor), jsonrpc.ts, bin/contentMapper.ts
  • package.json typescript.contentMapper, knip entry, README "Several projects or datasets" section
  • src/__tests__/exampleProject.ts: the shared example copy, tsconfig builder and check.ts type assertions used by PR A's tsc test and the TS 7.1 smoke test

Testing

13 unit tests for the mapper and framing: full round trip, utf-8-only host refused, syntax error yields a module plus a sanity-groq diagnostic, option diagnostics (not an object, wrong type, relative paths without a tsconfig, malformed resources entries), two folders with two schemas (blog gets the enum from the example schema, shop gets sku: string; price: number, a product query under blog is Array<never>, a file outside every resource gets the fallback with a pointed note, shopping/ is not inside shop), split and packed frames, parse error vs invalid request, thrown-error mapping, end while a request is in flight, error event.

contentMapper.ts71.test.ts is an opt-in smoke test against a real TypeScript 7.1 tsc (GROQ_FILES_TSC71=...): three cases, all passing locally against typescript@7.1.0-beta:

  1. the example through tsc --runExternalCode with PR A's check.ts assertions, then a syntax error reported as bad.groq(1,17): error sanity-groq2;
  2. one mapper entry with resources typing blog/ and shop/ against two schemas;
  3. two entries for .groq rejected with TS18067, then .groq + .shop.groq entries passing.
pnpm --filter @sanity/groq-files build
GROQ_FILES_TSC71=/path/to/typescript@7.1.0-beta/node_modules/.bin/tsc pnpm test:unit contentMapper.ts71

Build, build:types, typecheck, lint, format, publint, and knip pass.

Notes for release

N/A

Open in Web Open in Cursor 

@changeset-bot

changeset-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bd3379e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sanity/groq-files Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @sanity/cli

Compared against cody/groq-file-types-ea75 (48508c7f)

@sanity/cli

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 2.4 KB -
Internal (gzip) 926 B -
Bundled (raw) 11.16 MB -
Bundled (gzip) 2.10 MB -
Import time 411ms -3ms, -0.7%

bin:sanity

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 782 B -
Internal (gzip) 423 B -
Bundled (raw) 9.91 MB -
Bundled (gzip) 1.78 MB -
Import time 1.57s -8ms, -0.5%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against cody/groq-file-types-ea75 (48508c7f)

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 127.8 KB -
Internal (gzip) 33.1 KB -
Bundled (raw) 22.96 MB -
Bundled (gzip) 3.71 MB -
Import time 317ms -4ms, -1.2%

🗺️ @sanity/cli-core:. treemap too large to embed · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-build

Compared against cody/groq-file-types-ea75 (48508c7f)

@sanity/cli-build/_internal/build

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 118.7 KB -
Internal (gzip) 30.0 KB -
Bundled (raw) 18.11 MB -
Bundled (gzip) 3.66 MB -
Import time 673ms -15ms, -2.2%

@sanity/cli-build/_internal/env

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 1.8 KB -
Internal (gzip) 644 B -
Bundled (raw) 1.30 MB -
Bundled (gzip) 331.9 KB -
Import time 126ms -3ms, -2.5%

@sanity/cli-build/_internal/extract

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 42.8 KB -
Internal (gzip) 10.3 KB -
Bundled (raw) 11.89 MB -
Bundled (gzip) 2.24 MB -
Import time 418ms -3ms, -0.7%

@sanity/cli-build/_internal/manifest

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 31.0 KB -
Internal (gzip) 7.8 KB -
Bundled (raw) 842.0 KB -
Bundled (gzip) 213.9 KB -
Import time 414ms -8ms, -1.8%

🗺️ ./_internal/env · ./_internal/manifest · 2 treemaps too large to embed · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against cody/groq-file-types-ea75 (48508c7f)

Metric Value vs cody/groq-file-types-ea75 (48508c7)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/groq-files/src/contentMapper.ts 100.0%
packages/@sanity/groq-files/src/declarations.ts 100.0%
packages/@sanity/groq-files/src/evaluateGroqModule.ts 100.0%
packages/@sanity/groq-files/src/jsonrpc.ts 93.2%
packages/@sanity/groq-files/src/params.ts 90.8%
packages/@sanity/groq-files/src/plugin.ts 100.0%
packages/@sanity/groq-files/src/runtime.ts 100.0%
packages/@sanity/groq-files/src/schemaFile.ts 100.0%
packages/@sanity/groq-files/src/schemaTypes.ts 96.3%
packages/@sanity/groq-files/src/virtualModule.ts 98.5%

No baseline available — showing current coverage for 10 changed files

Overall Coverage

Metric Coverage
Statements 83.3%
Branches 76.2%
Functions 79.6%
Lines 83.8%

@stipsan
stipsan added this pull request to stack #2039 October 22, 2026 10:06
@cursor
cursor Bot force-pushed the cody/groq-files-content-mapper-ea75 branch from c01a55d to bd3379e Compare October 22, 2026 10:54
@stipsan

stipsan commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

Superseded by sanity-io/codegen#176 (typed .groq imports and the @sanity/codegen/content-mapper) and sanity-io/codegen#177 (runtime loaders for Vite, esbuild, webpack and Turbopack). This work now lives in @sanity/codegen.

@stipsan stipsan closed this Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants