Comparing version 1.9.0 to 1.10.0
{ | ||
"name": "groq-js", | ||
"version": "1.9.0", | ||
"version": "1.10.0", | ||
"keywords": [ | ||
@@ -67,8 +67,8 @@ "sanity", | ||
"devDependencies": { | ||
"@sanity/pkg-utils": "6.7.1", | ||
"@sanity/pkg-utils": "6.8.14", | ||
"@sanity/semantic-release-preset": "^4.1.7", | ||
"@types/debug": "^4.1.12", | ||
"@types/tap": "^15.0.11", | ||
"@typescript-eslint/eslint-plugin": "^7.7.0", | ||
"@typescript-eslint/parser": "^7.7.0", | ||
"@typescript-eslint/eslint-plugin": "^7.9.0", | ||
"@typescript-eslint/parser": "^7.9.0", | ||
"eslint": "^8.57.0", | ||
@@ -85,3 +85,3 @@ "eslint-config-prettier": "^9.1.0", | ||
"tap": "^16.3.10", | ||
"tsx": "^4.7.2", | ||
"tsx": "^4.10.2", | ||
"typescript": "5.4.5" | ||
@@ -88,0 +88,0 @@ }, |
@@ -19,2 +19,95 @@ import type {FuncCallNode} from '../nodeTypes' | ||
switch (`${node.namespace}.${node.name}`) { | ||
case 'array.compact': { | ||
const arg = walk({node: node.args[0], scope}) | ||
return mapConcrete(arg, scope, (arg) => { | ||
if (arg.type !== 'array') { | ||
return {type: 'null'} | ||
} | ||
const of = mapConcrete(arg.of, scope, (of) => of) | ||
return { | ||
type: 'array', | ||
of: unionWithoutNull(of), | ||
} | ||
}) | ||
} | ||
case 'array.join': { | ||
const arrayArg = walk({node: node.args[0], scope}) | ||
const sepArg = walk({node: node.args[1], scope}) | ||
return mapConcrete(arrayArg, scope, (arrayArg) => | ||
mapConcrete(sepArg, scope, (sepArg) => { | ||
if (arrayArg.type !== 'array') { | ||
return {type: 'null'} | ||
} | ||
if (sepArg.type !== 'string') { | ||
return {type: 'null'} | ||
} | ||
return mapConcrete(arrayArg.of, scope, (of) => { | ||
// we can only join strings, numbers, and booleans | ||
if (of.type !== 'string' && of.type !== 'number' && of.type !== 'boolean') { | ||
return {type: 'unknown'} | ||
} | ||
return {type: 'string'} | ||
}) | ||
}), | ||
) | ||
} | ||
case 'array.unique': { | ||
const arg = walk({node: node.args[0], scope}) | ||
return mapConcrete(arg, scope, (arg) => { | ||
if (arg.type !== 'array') { | ||
return {type: 'null'} | ||
} | ||
return arg | ||
}) | ||
} | ||
case 'global.lower': { | ||
const arg = walk({node: node.args[0], scope}) | ||
return mapConcrete(arg, scope, (arg) => { | ||
if (arg.type === 'string') { | ||
if (arg.value !== undefined) { | ||
return { | ||
type: 'string', | ||
value: arg.value.toLowerCase(), | ||
} | ||
} | ||
return {type: 'string'} | ||
} | ||
return {type: 'null'} | ||
}) | ||
} | ||
case 'global.upper': { | ||
const arg = walk({node: node.args[0], scope}) | ||
return mapConcrete(arg, scope, (arg) => { | ||
if (arg.type === 'string') { | ||
if (arg.value !== undefined) { | ||
return { | ||
type: 'string', | ||
value: arg.value.toUpperCase(), | ||
} | ||
} | ||
return {type: 'string'} | ||
} | ||
return {type: 'null'} | ||
}) | ||
} | ||
case 'dateTime.now': { | ||
return {type: 'string'} | ||
} | ||
case 'global.now': { | ||
return {type: 'string'} | ||
} | ||
case 'global.defined': { | ||
@@ -62,2 +155,24 @@ return {type: 'boolean'} | ||
case 'global.round': { | ||
const numNode = walk({node: node.args[0], scope}) | ||
return mapConcrete(numNode, scope, (num) => { | ||
if (num.type !== 'number') { | ||
return {type: 'null'} | ||
} | ||
if (node.args.length === 2) { | ||
const precisionNode = walk({node: node.args[1], scope}) | ||
return mapConcrete(precisionNode, scope, (precision) => { | ||
if (precision.type !== 'number') { | ||
return {type: 'null'} | ||
} | ||
return {type: 'number'} | ||
}) | ||
} | ||
return {type: 'number'} | ||
}) | ||
} | ||
case 'global.string': { | ||
@@ -64,0 +179,0 @@ const arg = walk({node: node.args[0], scope}) |
import type {TypeNode} from './types' | ||
const {compare} = new Intl.Collator('en') | ||
function typeNodesSorter(a: TypeNode, b: TypeNode): number { | ||
if (a.type === 'null') { | ||
return 1 | ||
} | ||
return compare(hashField(a), hashField(b)) | ||
} | ||
export function hashField(field: TypeNode): string { | ||
@@ -24,4 +32,10 @@ switch (field.type) { | ||
case 'object': { | ||
return `${field.type}:(${Object.entries(field.attributes) | ||
.map(([key, value]) => `${key}:${hashField(value.value)}`) | ||
const attributes = Object.entries(field.attributes) | ||
attributes.sort(([a], [b]) => compare(a, b)) // sort them by name | ||
return `${field.type}:(${attributes | ||
.map( | ||
([key, value]) => | ||
`${key}:${hashField(value.value)}(${value.optional ? 'optional' : 'non-optional'})`, | ||
) | ||
.join(',')}):ref-${field.dereferencesTo}:${field.rest ? hashField(field.rest) : 'no-rest'}` | ||
@@ -31,3 +45,5 @@ } | ||
case 'union': { | ||
return `${field.type}(${field.of.map(hashField).join(',')})` | ||
const sorted = [...field.of] | ||
sorted.sort(typeNodesSorter) | ||
return `${field.type}(${sorted.map(hashField).join(',')})` | ||
} | ||
@@ -50,3 +66,6 @@ | ||
for (const typeNode of typeNodes) { | ||
const sortedTypeNodes = [...typeNodes] | ||
sortedTypeNodes.sort(typeNodesSorter) | ||
for (const typeNode of sortedTypeNodes) { | ||
const hash = hashField(typeNode) | ||
@@ -70,2 +89,6 @@ if (hash === null) { | ||
if (field.type === 'union') { | ||
if (field.of.length === 0) { | ||
return field | ||
} | ||
field.of = removeDuplicateTypeNodes(field.of) | ||
@@ -89,3 +112,2 @@ | ||
const compare = new Intl.Collator('en').compare | ||
field.of.sort((a, b) => { | ||
@@ -92,0 +114,0 @@ if (a.type === 'null') { |
@@ -23,3 +23,5 @@ import debug from 'debug' | ||
NotNode, | ||
ObjectConditionalSplatNode, | ||
ObjectNode, | ||
ObjectSplatNode, | ||
OpCallNode, | ||
@@ -51,3 +53,3 @@ ParentNode, | ||
} from './types' | ||
import {mapConcrete, nullUnion} from './typeHelpers' | ||
import {mapConcrete, nullUnion, resolveInline} from './typeHelpers' | ||
@@ -60,3 +62,3 @@ const $trace = debug('typeEvaluator:evaluate:trace') | ||
$debug.log = console.log.bind(console) // eslint-disable-line no-console | ||
const $warn = debug('typeEvaluator:evaluate::warn') | ||
const $warn = debug('typeEvaluator:evaluate:warn') | ||
@@ -73,2 +75,3 @@ /** | ||
$debug('evaluateQueryType.ast %O', ast) | ||
$debug('evaluateQueryType.schema %O', schema) | ||
const parsed = walk({ | ||
@@ -123,76 +126,336 @@ node: ast, | ||
function mapObjectSplat( | ||
node: TypeNode, | ||
function handleObjectSplatNode( | ||
attr: ObjectSplatNode | ObjectConditionalSplatNode, | ||
scope: Scope, | ||
mapper: (field: Document | ObjectTypeNode) => void, | ||
) { | ||
if (node.type === 'union') { | ||
for (const scoped of node.of) { | ||
mapObjectSplat(scoped, scope, mapper) | ||
): TypeNode { | ||
const value = walk({node: attr.value, scope}) | ||
$trace('object.splat.value %O', value) | ||
return mapConcrete(value, scope, (node) => { | ||
// if the node is not an object it means we are splating over a non-object, so we return unknown | ||
if (node.type !== 'object') { | ||
return {type: 'unknown'} | ||
} | ||
} | ||
if (node.type === 'object') { | ||
mapper(node) | ||
} | ||
const attributes: Record<string, ObjectAttribute> = {} | ||
for (const name in node.attributes) { | ||
if (!node.attributes.hasOwnProperty(name)) { | ||
continue | ||
} | ||
attributes[name] = node.attributes[name] | ||
} | ||
if (node.rest !== undefined) { | ||
// Rest is either an object, inline, or unknown - we need to resolve it if it's an inline | ||
const resolvedRest = resolveInline(node.rest, scope) | ||
// if the rest is unknown the entire object is unknown | ||
if (resolvedRest.type === 'unknown') { | ||
return {type: 'unknown'} | ||
} | ||
if (resolvedRest.type !== 'object') { | ||
return {type: 'null'} | ||
} | ||
for (const name in resolvedRest.attributes) { | ||
// eslint-disable-next-line | ||
if (!resolvedRest.attributes.hasOwnProperty(name)) { | ||
continue | ||
} | ||
attributes[name] = resolvedRest.attributes[name] | ||
} | ||
} | ||
return {type: 'object', attributes} | ||
}) | ||
} | ||
function handleObjectNode(node: ObjectNode, scope: Scope) { | ||
// eslint-disable-next-line max-statements, complexity | ||
function handleObjectNode(node: ObjectNode, scope: Scope): TypeNode { | ||
$trace('object.node %O', node) | ||
$trace('object.scope %O', scope) | ||
const attributes: Record<string, ObjectAttribute> = {} | ||
for (const attr of node.attributes) { | ||
if (node.attributes.length === 0) { | ||
return { | ||
type: 'object', | ||
attributes: {}, | ||
} satisfies ObjectTypeNode | ||
} | ||
// let attributes we a entry of [name, value] or null. We need to keep track of nulls to handle conditional splats | ||
// since we care about the order of the attributes. Later attribute keys will overwrite earlier ones. | ||
const objectAttributes: [number, string, ObjectAttribute][] = [] | ||
const splatVariants: [number, ObjectTypeNode | UnionTypeNode<ObjectTypeNode>][] = [] | ||
// We keep track of conditional splats separately, since we need to merge them into an object or an union of objects at the end. | ||
// keep track of the index of the conditional splat to be able to merge the attributes correctly. | ||
const conditionalVariants: [number, UnionTypeNode<ObjectTypeNode>][] = [] | ||
for (const [idx, attr] of node.attributes.entries()) { | ||
if (attr.type === 'ObjectAttributeValue') { | ||
const field = walk({node: attr.value, scope}) | ||
attributes[attr.name] = { | ||
type: 'objectAttribute', | ||
value: field, | ||
} | ||
const attributeNode = walk({node: attr.value, scope}) | ||
objectAttributes.push([ | ||
idx, | ||
attr.name, | ||
{ | ||
type: 'objectAttribute', | ||
value: attributeNode, | ||
}, | ||
]) | ||
continue | ||
} | ||
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 | ||
} | ||
const attributeNode = handleObjectSplatNode(attr, scope) | ||
$trace('object.splat.result %O', attributeNode) | ||
attributes[name] = node.attributes[name] | ||
switch (attributeNode.type) { | ||
case 'object': { | ||
splatVariants.push([idx, attributeNode]) | ||
continue | ||
} | ||
}) | ||
case 'union': { | ||
for (const node of attributeNode.of) { | ||
// eslint-disable-next-line max-depth | ||
if (node.type !== 'object') { | ||
return {type: 'unknown'} | ||
} | ||
} | ||
splatVariants.push([idx, attributeNode as UnionTypeNode<ObjectTypeNode>]) | ||
continue | ||
} | ||
default: { | ||
return {type: 'unknown'} | ||
} | ||
} | ||
} | ||
if (attr.type === 'ObjectConditionalSplat') { | ||
const condition = resolveCondition(attr.condition, scope) | ||
$trace('object.conditional.splat.condition %O', condition) | ||
if (condition || condition === undefined) { | ||
const value = walk({node: attr.value, scope}) | ||
// condition is never met, skip this attribute | ||
if (condition === false) { | ||
continue | ||
} | ||
mapObjectSplat(value, scope, (node) => { | ||
for (const name in node.attributes) { | ||
if (!Object.hasOwn(node.attributes, name)) { | ||
const attributeNode = handleObjectSplatNode(attr, scope) | ||
$trace('object.conditional.splat.result %O', attributeNode) | ||
// condition is always met, we can treat this as a normal splat | ||
if (condition === true) { | ||
switch (attributeNode.type) { | ||
case 'object': { | ||
splatVariants.push([idx, attributeNode]) | ||
continue | ||
} | ||
case 'union': { | ||
// eslint-disable-next-line max-depth | ||
for (const node of attributeNode.of) { | ||
// eslint-disable-next-line max-depth | ||
if (node.type !== 'object') { | ||
return {type: 'unknown'} | ||
} | ||
} | ||
splatVariants.push([idx, attributeNode as UnionTypeNode<ObjectTypeNode>]) | ||
continue | ||
} | ||
default: { | ||
return {type: 'unknown'} | ||
} | ||
} | ||
} | ||
const variant = mapConcrete(attributeNode, scope, (attributeNode) => { | ||
$trace('object.conditional.splat.result.concrete %O', attributeNode) | ||
if (attributeNode.type !== 'object') { | ||
return {type: 'unknown'} | ||
} | ||
return { | ||
type: 'object', | ||
attributes: attributeNode.attributes, | ||
} satisfies ObjectTypeNode | ||
}) | ||
if (variant.type === 'union') { | ||
for (const node of variant.of) { | ||
// We can only splat objects, so we bail out if we encounter a non-object node. | ||
// eslint-disable-next-line max-depth | ||
if (node.type !== 'object') { | ||
return {type: 'unknown'} | ||
} | ||
} | ||
variant.of.push({type: 'object', attributes: {}} as ObjectTypeNode) // add an empty object to the union, since it's conditional | ||
conditionalVariants.push([idx, variant as UnionTypeNode<ObjectTypeNode>]) | ||
continue | ||
} | ||
// If the variant is not an object or a union of objects, we bail out early. | ||
if (variant.type !== 'object') { | ||
return {type: 'unknown'} | ||
} | ||
conditionalVariants.push([ | ||
idx, | ||
{ | ||
type: 'union', | ||
of: [{type: 'object', attributes: {}}, variant], | ||
}, | ||
]) | ||
continue | ||
} | ||
// @ts-expect-error - we should have handled all cases of ObjectAttributeNode | ||
throw new Error(`Unknown object attribute type: ${attr.type}`) | ||
} | ||
const guaranteedAttributes: [number, string, ObjectAttribute<TypeNode>][] = [] | ||
guaranteedAttributes.push(...objectAttributes) | ||
for (const [idx, splatNode] of splatVariants) { | ||
if (splatNode.type === 'object') { | ||
for (const name in splatNode.attributes) { | ||
if (!splatNode.attributes.hasOwnProperty(name)) { | ||
continue | ||
} | ||
const attribute = splatNode.attributes[name] | ||
guaranteedAttributes.push([idx, name, attribute]) | ||
} | ||
continue | ||
} | ||
// it's a union of objects, so we keep this as a conditional variant | ||
conditionalVariants.push([idx, splatNode]) | ||
} | ||
// make sure they are sorted from lowest index to highest, this ensures that | ||
// attributes with a higher index overwrite attributes with a lower index. | ||
guaranteedAttributes.sort(([a], [b]) => a - b) | ||
// If we have no conditional variants, we can just return the object with the guaranteed attributes. | ||
if (conditionalVariants.length === 0) { | ||
return { | ||
type: 'object', | ||
attributes: Object.fromEntries( | ||
guaranteedAttributes.map(([, name, attribute]) => [name, attribute]), | ||
), | ||
} satisfies ObjectTypeNode | ||
} | ||
// matrix should be a result of if given we have variants [a,b,c] this would lead to a union of [a, a|b, a|c, a|b|c, b|c, c, {EMPTY}] | ||
// if it's given we have variants A + [a|b|c] this would lead to a union of [Aa, Aa|Ab, Aa|Ac, Aa|Ab|Ac, Ab|Ac, Ac, A] | ||
const matrix: (ObjectTypeNode | UnionTypeNode<ObjectTypeNode>)[] = [] | ||
for (const [unionIdx, union] of conditionalVariants) { | ||
const unionGuaranteedBefore: [number, string, ObjectAttribute][] = [] | ||
const unionGuaranteedAfter: [number, string, ObjectAttribute][] = [] | ||
// Collect all guaranteed attributes before and after the conditional variant. | ||
for (const [guaranteedIndex, name, attribute] of guaranteedAttributes) { | ||
if (guaranteedIndex < unionIdx) { | ||
unionGuaranteedBefore.push([guaranteedIndex, name, attribute]) | ||
} | ||
if (guaranteedIndex > unionIdx) { | ||
unionGuaranteedAfter.push([guaranteedIndex, name, attribute]) | ||
} | ||
} | ||
// build a map of variants from other conditions. | ||
const allVariantsAttributes: [number, Record<string, ObjectAttribute>[]][] = [] | ||
for (const [conditionalVariantIdx, otherUnion] of conditionalVariants) { | ||
// We need to build a matrix of all possible combinations of the attributes of the other variants. | ||
// start with an empty object, since it's condtional. | ||
const variantAttributes: Record<string, ObjectAttribute>[] = [] | ||
for (const node of otherUnion.of) { | ||
variantAttributes.push(node.attributes) | ||
} | ||
allVariantsAttributes.push([conditionalVariantIdx, variantAttributes]) | ||
} | ||
/* eslint-disable max-depth */ | ||
for (const node of union.of) { | ||
matrix.push({ | ||
type: 'object', | ||
attributes: { | ||
...Object.fromEntries( | ||
unionGuaranteedBefore.map(([, name, attribute]) => [name, attribute]), | ||
), | ||
...node.attributes, | ||
...Object.fromEntries( | ||
unionGuaranteedAfter.map(([, name, attribute]) => [name, attribute]), | ||
), | ||
}, | ||
} satisfies ObjectTypeNode) | ||
for (const [outerIdx, outerAttributes] of allVariantsAttributes) { | ||
for (const outer of outerAttributes) { | ||
for (const [innerIdx, innerAttributes] of allVariantsAttributes) { | ||
if (outerIdx === innerIdx) { | ||
continue | ||
} | ||
const attribute = node.attributes[name] | ||
if (condition) { | ||
attributes[name] = attribute | ||
} else if (condition === undefined) { | ||
attributes[name] = { | ||
type: 'objectAttribute', | ||
value: attribute.value, | ||
optional: true, | ||
for (const inner of innerAttributes) { | ||
const _before = [...unionGuaranteedBefore] | ||
const _after = [...unionGuaranteedAfter] | ||
for (const name in outer) { | ||
if (!outer.hasOwnProperty(name)) { | ||
continue | ||
} | ||
if (outerIdx === unionIdx) { | ||
continue | ||
} | ||
if (outerIdx < unionIdx) { | ||
_before.push([outerIdx, name, outer[name]]) | ||
} | ||
if (outerIdx > unionIdx) { | ||
_after.push([outerIdx, name, outer[name]]) | ||
} | ||
} | ||
} else { | ||
throw new Error('Unexpected condition') | ||
for (const name in inner) { | ||
if (!inner.hasOwnProperty(name)) { | ||
continue | ||
} | ||
if (outerIdx === unionIdx) { | ||
continue | ||
} | ||
if (innerIdx < unionIdx) { | ||
_before.push([innerIdx, name, inner[name]]) | ||
} | ||
if (innerIdx > unionIdx) { | ||
_after.push([innerIdx, name, inner[name]]) | ||
} | ||
} | ||
_before.sort(([a], [b]) => a - b) | ||
_after.sort(([a], [b]) => a - b) | ||
const before: Record<string, ObjectAttribute> = Object.fromEntries( | ||
_before.map(([, name, attribute]) => [name, attribute]), | ||
) | ||
const after: Record<string, ObjectAttribute> = Object.fromEntries( | ||
_after.map(([, name, attribute]) => [name, attribute]), | ||
) | ||
matrix.push({ | ||
type: 'object', | ||
attributes: { | ||
...before, | ||
...node.attributes, | ||
...after, | ||
}, | ||
}) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
} | ||
/* eslint-disable max-depth */ | ||
} | ||
return { | ||
type: 'object', | ||
attributes, | ||
} satisfies ObjectTypeNode | ||
return optimizeUnions({ | ||
type: 'union', | ||
of: matrix, | ||
}) | ||
} | ||
@@ -202,2 +465,3 @@ | ||
function handleOpCallNode(node: OpCallNode, scope: Scope): TypeNode { | ||
$trace('opcall.node %O', node) | ||
const lhs = walk({node: node.left, scope}) | ||
@@ -208,3 +472,3 @@ const rhs = walk({node: node.right, scope}) | ||
mapConcrete(rhs, scope, (right) => { | ||
$trace('opCallNode "%s" %O', node.op, {left, right}) | ||
$trace('opcall.node.concrete "%s" %O', node.op, {left, right}) | ||
@@ -380,2 +644,3 @@ switch (node.op) { | ||
} | ||
return { | ||
@@ -587,10 +852,18 @@ type: 'union', | ||
function handleParentNode({n}: ParentNode, scope: Scope): TypeNode { | ||
let current: Scope = scope | ||
$trace('handle.parent.currentScope %d %O', n, scope) | ||
let current: Scope | undefined = scope | ||
for (let i = 0; i < n; i++) { | ||
if (!current.parent) { | ||
return {type: 'null'} satisfies NullTypeNode | ||
// make sure we are not in a hidden scope | ||
while (current?.isHidden) { | ||
current = current.parent | ||
} | ||
current = current.parent | ||
current = current?.parent | ||
} | ||
$trace('parent.scope %d %O', n, current.value) | ||
$trace('handle.parent.newScope %d %O', n, current) | ||
if (!current) { | ||
return {type: 'null'} satisfies NullTypeNode | ||
} | ||
if (current.value.of.length === 0) { | ||
@@ -833,5 +1106,15 @@ return {type: 'null'} satisfies NullTypeNode | ||
switch (expr.type) { | ||
case 'AccessAttribute': | ||
case 'AccessElement': | ||
case 'Value': { | ||
const value = walk({node: expr, scope}) | ||
return value.type === 'boolean' && value.value !== false | ||
const value = mapConcrete(walk({node: expr, scope}), scope, (node) => node) | ||
if (value.type === 'boolean') { | ||
return value.value | ||
} | ||
if (value.type === 'null' || value.type === 'object' || value.type === 'array') { | ||
return false | ||
} | ||
return undefined | ||
} | ||
@@ -1061,7 +1344,15 @@ case 'And': { | ||
} | ||
case 'Not': { | ||
const result = resolveCondition(expr.base, scope) | ||
// check if the result is undefined or false. Undefined means that the condition can't be resolved, and we should keep the node | ||
return result === undefined ? undefined : result === false | ||
} | ||
case 'Group': { | ||
return resolveCondition(expr.base, scope) | ||
} | ||
default: { | ||
return true | ||
return undefined | ||
} | ||
@@ -1068,0 +1359,0 @@ } |
@@ -6,2 +6,3 @@ import {optimizeUnions} from './optimizations' | ||
BooleanTypeNode, | ||
InlineTypeNode, | ||
NullTypeNode, | ||
@@ -79,3 +80,3 @@ NumberTypeNode, | ||
type ConcreteTypeNode = | ||
export type ConcreteTypeNode = | ||
| BooleanTypeNode | ||
@@ -88,2 +89,11 @@ | NullTypeNode | ||
export function resolveInline(node: TypeNode, scope: Scope): Exclude<TypeNode, InlineTypeNode> { | ||
if (node.type === 'inline') { | ||
const resolvedInline = scope.context.lookupTypeDeclaration(node) | ||
return resolveInline(resolvedInline, scope) | ||
} | ||
return node | ||
} | ||
/** | ||
@@ -120,3 +130,3 @@ * mapConcrete extracts a _concrete type_ from a type node, applies the mapping | ||
case 'inline': { | ||
const resolvedInline = scope.context.lookupTypeDeclaration(node) | ||
const resolvedInline = resolveInline(node, scope) | ||
return mapConcrete(resolvedInline, scope, mapper, mergeUnions) | ||
@@ -123,0 +133,0 @@ } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
975240
13469