Skip to content

Commit

Permalink
Feat(typegen): Added @sanity-typegen-ignore tag for ignoring queries …
Browse files Browse the repository at this point in the history
…when generating types
  • Loading branch information
largis21 committed May 20, 2024
1 parent d66def2 commit 8e5d493
Showing 1 changed file with 81 additions and 2 deletions.
83 changes: 81 additions & 2 deletions packages/@sanity/codegen/src/typescript/findQueriesInSource.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createRequire} from 'node:module'

import {type TransformOptions, traverse} from '@babel/core'
import {type NodePath, type TransformOptions, traverse} from '@babel/core'
import * as babelTypes from '@babel/types'

import {getBabelConfig} from '../getBabelConfig'
Expand All @@ -11,6 +11,8 @@ const require = createRequire(__filename)

const groqTagName = 'groq'

const ignoreValue = '@sanity-typegen-ignore'

/**
* findQueriesInSource takes a source string and returns all GROQ queries in it.
* @param source - The source code to search for queries
Expand All @@ -33,15 +35,22 @@ export function findQueriesInSource(
traverse(file, {
// Look for variable declarations, e.g. `const myQuery = groq`... and extract the query.
// The variable name is used as the name of the query result type
VariableDeclarator({node, scope}) {
VariableDeclarator(path) {
const {node, scope} = path

const init = node.init

// Look for tagged template expressions that are called with the `groq` tag
if (
babelTypes.isTaggedTemplateExpression(init) &&
babelTypes.isIdentifier(init.tag) &&
babelTypes.isIdentifier(node.id) &&
init.tag.name === groqTagName
) {
if (getDeclarationLeadingComment(path)?.trim() === ignoreValue) {
return
}

const queryName = `${node.id.name}`
const queryResult = resolveExpression({
node: init,
Expand All @@ -51,10 +60,80 @@ export function findQueriesInSource(
filename,
resolver,
})

queries.push({name: queryName, result: queryResult})
}
},
})

return queries
}

function getDeclarationLeadingComment(
path: NodePath<babelTypes.VariableDeclarator>,
): string | null {
/*
* We have to consider these cases:
*
* // @sanity-typegen-ignore
* const query = groq`...`
*
* // AST
* VariableDeclaration {
* declarations: [
* VariableDeclarator: {init: tag: {name: "groq"}}
* ],
* leadingComments: ...
* }
*
* // @sanity-typegen-ignore
* const query1 = groq`...`, query2 = groq`...`
*
* // AST
* VariableDeclaration {
* declarations: [
* VariableDeclarator: {init: tag: {name: "groq"}}
* VariableDeclarator: {init: tag: {name: "groq"}}
* ],
* leadingComments: ...
* }
*
* // @sanity-typegen-ignore
* export const query = groq`...`
*
* // AST
* ExportNamedDeclaration {
* declaration: VariableDeclaration {
* declarations: [
* VariableDeclarator: {init: tag: {name: "groq"}}
* VariableDeclarator: {init: tag: {name: "groq"}}
* ],
* },
* leadingComments: ...
* }
*
* In the case where multiple variables are under the same VariableDeclaration the leadingComments
* will still be on the VariableDeclaration
*
* In the case where the variable is exported, the leadingComments are on the
* ExportNamedDeclaration which includes the VariableDeclaration in its own declaration property
*/

const variableDeclaration = path.find((node) => node.isVariableDeclaration())
if (!variableDeclaration) return null

if (variableDeclaration.node.leadingComments) {
return getLastInArray(variableDeclaration.node.leadingComments)?.value || null
}

// If the declaration is exported, the comment lies on the parent of the export declaration
if (variableDeclaration.parent.leadingComments) {
return getLastInArray(variableDeclaration.parent.leadingComments)?.value || null
}

return null
}

function getLastInArray<T>(arr: T[]) {
return arr[arr.length - 1]
}

0 comments on commit 8e5d493

Please sign in to comment.