Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(codegen): function resolving #7750

Merged
merged 2 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ describe('findQueries with the groq template', () => {
expect(queryResult?.result).toEqual('*[_type == "author"]')
})

test('with function with literal parameters', () => {
const source = `
import { groq } from "groq";
const getType = (type, x = '2') => () => \`\${type}\${x}\`;
const query = groq\`*[_type == "\${getType("author")()}"]\`
const res = sanity.fetch(query);
`

const queries = findQueriesInSource(source, 'test.ts')

const queryResult = queries[0]

expect(queryResult?.result).toEqual('*[_type == "author2"]')
})

test('with block comment', () => {
const source = `
import { groq } from "groq";
Expand Down Expand Up @@ -124,6 +139,18 @@ describe('findQueries with the groq template', () => {
expect(queries[0].result).toBe('*[_type == "foo bar"]')
})

test('can import from export *', () => {
const source = `
import { groq } from "groq";
import {foo} from "./fixtures/exportStar";
const postQuery = groq\`*[_type == "\${foo}"]\`
const res = sanity.fetch(postQueryResult);
`
const queries = findQueriesInSource(source, __filename, undefined)
expect(queries.length).toBe(1)
expect(queries[0].result).toBe('*[_type == "foo"]')
})

test('will ignore declarations with ignore tag', () => {
const source = `
import { groq } from "groq";
Expand Down
59 changes: 51 additions & 8 deletions packages/@sanity/codegen/src/typescript/expressionResolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'node:fs'
import path from 'node:path'

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

Expand Down Expand Up @@ -125,12 +125,13 @@ export function resolveExpression({
}

if (babelTypes.isVariableDeclarator(node)) {
if (!node.init) {
const init = node.init ?? (babelTypes.isAssignmentPattern(node.id) && node.id.right)
if (!init) {
throw new Error(`Unsupported variable declarator`)
}

return resolveExpression({
node: node.init,
node: init,
fnArguments,
scope,
filename,
Expand Down Expand Up @@ -174,11 +175,20 @@ export function resolveExpression({
babelTypes.isFunctionDeclaration(node) ||
babelTypes.isFunctionExpression(node)
) {
const newScope = new Scope(scope.path, scope)

params.forEach((param, i) => {
newScope.push({
id: param as babelTypes.LVal,
init: fnArguments[i] as babelTypes.Expression | undefined,
})
})

return resolveExpression({
node: node.body,
params: node.params,
fnArguments,
scope,
scope: newScope,
filename,
file,
babelConfig,
Expand All @@ -198,7 +208,7 @@ export function resolveExpression({
}

if (babelTypes.isImportDefaultSpecifier(node) || babelTypes.isImportSpecifier(node)) {
return resolveImportSpecifier({node, file, scope, filename, resolver, babelConfig})
return resolveImportSpecifier({node, file, scope, filename, fnArguments, resolver, babelConfig})
}

if (babelTypes.isAssignmentPattern(node)) {
Expand Down Expand Up @@ -245,7 +255,10 @@ function resolveIdentifier({
babelTypes.isIdentifier(param.left) &&
node.name === param.left.name),
)
const argument = fnArguments[paramIndex]
let argument = fnArguments[paramIndex]
if (!argument && paramIndex >= 0 && babelTypes.isAssignmentPattern(params[paramIndex])) {
argument = params[paramIndex].right
}
if (argument && babelTypes.isLiteral(argument)) {
return resolveExpression({
node: argument,
Expand Down Expand Up @@ -320,13 +333,15 @@ function resolveImportSpecifier({
node,
file,
filename,
fnArguments,
resolver,
babelConfig,
}: {
node: babelTypes.ImportDefaultSpecifier | babelTypes.ImportSpecifier | babelTypes.ExportSpecifier
file: babelTypes.File
scope: Scope
filename: string
fnArguments: babelTypes.Node[]
resolver: NodeJS.RequireResolve
babelConfig: TransformOptions
}): resolveExpressionReturnType {
Expand Down Expand Up @@ -382,6 +397,7 @@ function resolveImportSpecifier({
node: binding.path.node,
file: tree,
scope: newScope,
fnArguments,
babelConfig,
filename: resolvedFile,
resolver,
Expand Down Expand Up @@ -413,24 +429,48 @@ function resolveImportSpecifier({
node: namedExport,
importName: newImportName,
filename: resolvedFile,
fnArguments,
resolver,
babelConfig,
})
}

let result: resolveExpressionReturnType | undefined
traverse(tree, {
ExportDeclaration(p) {
if (p.node.type === 'ExportAllDeclaration') {
try {
result = resolveExportSpecifier({
node: p.node,
importName,
filename: resolvedFile,
fnArguments,
resolver,
babelConfig,
})
} catch (e) {
if (e.cause !== `noBinding:${importName}`) throw e
}
}
},
})
if (result) return result

throw new Error(`Could not find binding for import "${importName}" in ${importFileName}`)
}

function resolveExportSpecifier({
node,
importName,
filename,
fnArguments,
babelConfig,
resolver,
}: {
node: babelTypes.ExportNamedDeclaration
node: babelTypes.ExportNamedDeclaration | babelTypes.ExportAllDeclaration
importName: string
filename: string
fnArguments: babelTypes.Node[]
babelConfig: TransformOptions
resolver: NodeJS.RequireResolve
}): resolveExpressionReturnType {
Expand Down Expand Up @@ -463,8 +503,11 @@ function resolveExportSpecifier({
filename: importFileName,
babelConfig,
resolver,
fnArguments,
})
}

throw new Error(`Could not find binding for export "${importName}" in ${importFileName}`)
throw new Error(`Could not find binding for export "${importName}" in ${importFileName}`, {
cause: `noBinding:${importName}`,
})
}
Loading