Skip to content

Commit

Permalink
fix(typeEvlauator): handle rest on object splat operations
Browse files Browse the repository at this point in the history
  • Loading branch information
sgulseth committed May 7, 2024
1 parent dd823dd commit 8ef8b24
Show file tree
Hide file tree
Showing 2 changed files with 256 additions and 27 deletions.
56 changes: 29 additions & 27 deletions src/typeEvaluator/typeEvaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function handleDerefNode(node: DerefNode, scope: Scope): TypeNode {
function mapObjectSplat(
node: TypeNode,
scope: Scope,
mapper: (field: Document | ObjectTypeNode) => void,
mapper: (name: string, attribute: ObjectAttribute) => void,
) {
if (node.type === 'union') {
for (const scoped of node.of) {
Expand All @@ -128,7 +128,22 @@ function mapObjectSplat(
}

if (node.type === 'object') {
mapper(node)
// if the rest is unknown the entire object is unknown
if (node.rest !== undefined && node.rest.type === 'unknown') {
return
}

for (const name in node.attributes) {
if (!node.attributes.hasOwnProperty(name)) {
continue
}
mapper(name, node.attributes[name])
}

if (node.rest !== undefined) {
const rest = mapConcrete(node.rest, scope, (rest) => rest)
mapObjectSplat(rest, scope, mapper)
}
}
}
function handleObjectNode(node: ObjectNode, scope: Scope) {
Expand All @@ -147,14 +162,8 @@ function handleObjectNode(node: ObjectNode, scope: Scope) {
if (attr.type === 'ObjectSplat') {
const value = walk({node: attr.value, scope})
$trace('object.splat.value %O', value)
mapObjectSplat(value, scope, (node) => {
for (const name in node.attributes) {
if (!Object.hasOwn(node.attributes, name)) {
continue
}

attributes[name] = node.attributes[name]
}
mapObjectSplat(value, scope, (name, attribute) => {
attributes[name] = attribute
})
}
if (attr.type === 'ObjectConditionalSplat') {
Expand All @@ -163,24 +172,17 @@ function handleObjectNode(node: ObjectNode, scope: Scope) {
if (condition || condition === undefined) {
const value = walk({node: attr.value, scope})

mapObjectSplat(value, scope, (node) => {
for (const name in node.attributes) {
if (!Object.hasOwn(node.attributes, name)) {
continue
}
const attribute = node.attributes[name]

if (condition) {
attributes[name] = attribute
} else if (condition === undefined) {
attributes[name] = {
type: 'objectAttribute',
value: attribute.value,
optional: true,
}
} else {
throw new Error('Unexpected condition')
mapObjectSplat(value, scope, (name, attribute) => {
if (condition) {
attributes[name] = attribute
} else if (condition === undefined) {
attributes[name] = {
type: 'objectAttribute',
value: attribute.value,
optional: true,
}
} else {
throw new Error('Unexpected condition')
}
})
}
Expand Down
227 changes: 227 additions & 0 deletions test/typeEvaluate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2169,6 +2169,233 @@ t.test('function: string::split', (t) => {
t.end()
})

t.test('splat object with inline', (t) => {
const query = `*[_type == "author" || _type == "post" || _type == "test"] {
_type == "author" => {
"bar": _id
},
_type == "test" => {
foo[] {
_type,
_type != "slug" => @
}
}
}`

const ast = parse(query)
const res = typeEvaluate(ast, [
...schemas,
{
name: 'inline1',
type: 'type',
value: {
type: 'object',
attributes: {
_type: {
type: 'objectAttribute',
value: {
type: 'string',
value: 'inline1',
},
},
inlineValue1: {
type: 'objectAttribute',
value: {
type: 'string',
},
},
},
},
},
{
name: 'inline2',
type: 'type',
value: {
type: 'object',
attributes: {
_type: {
type: 'objectAttribute',
value: {
type: 'string',
value: 'inline2',
},
},
inlineValue2: {
type: 'objectAttribute',
value: {
type: 'string',
},
},
},
},
},
{
name: 'test',
type: 'document',
attributes: {
_type: {
type: 'objectAttribute',
value: {
type: 'string',
value: 'test',
},
},
foo: {
type: 'objectAttribute',
value: {
type: 'array',
of: {
type: 'union',
of: [
{
type: 'object',
attributes: {
_key: {
type: 'objectAttribute',
value: {type: 'string'},
},
},
rest: {
type: 'inline',
name: 'inline1',
},
},
{
type: 'object',
attributes: {
_key: {
type: 'objectAttribute',
value: {type: 'string'},
},
},
rest: {
type: 'inline',
name: 'inline2',
},
},
{
type: 'object',
attributes: {
_key: {
type: 'objectAttribute',
value: {type: 'string'},
},
},
rest: {
type: 'inline',
name: 'slug',
},
},
],
},
},
},
},
},
])

t.strictSame(res, {
type: 'array',
of: {
type: 'union',
of: [
{
type: 'object',
attributes: {},
},
{
type: 'object',
attributes: {
bar: {
type: 'objectAttribute',
value: {
type: 'string',
},
},
},
},
{
type: 'object',
attributes: {
foo: {
type: 'objectAttribute',
value: {
type: 'array',
of: {
type: 'union',
of: [
{
type: 'object',
attributes: {
_type: {
type: 'objectAttribute',
value: {
type: 'string',
value: 'inline1',
},
},
_key: {
type: 'objectAttribute',
value: {
type: 'string',
},
},
inlineValue1: {
type: 'objectAttribute',
value: {
type: 'string',
},
},
},
},
{
type: 'object',
attributes: {
_type: {
type: 'objectAttribute',
value: {
type: 'string',
value: 'inline2',
},
},
_key: {
type: 'objectAttribute',
value: {
type: 'string',
},
},
inlineValue2: {
type: 'objectAttribute',
value: {
type: 'string',
},
},
},
},
{
type: 'object',
attributes: {
_type: {
type: 'objectAttribute',
value: {
type: 'string',
value: 'slug',
},
},
},
},
],
},
},
},
},
},
],
},
})
t.end()
})

function findSchemaType(name: string): TypeNode {
const type = schemas.find((s) => s.name === name)
if (!type) {
Expand Down

0 comments on commit 8ef8b24

Please sign in to comment.