Skip to content

Commit a62dc33

Browse files
Remove custom scalar generation as an unknown type fallback
1 parent 002860d commit a62dc33

File tree

1 file changed

+45
-69
lines changed

1 file changed

+45
-69
lines changed

ndc-lambda-sdk/src/inference.ts

Lines changed: 45 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,6 @@ type DeriveFunctionSchemaResult = {
161161
}
162162

163163
function deriveFunctionSchema(functionDeclaration: ts.FunctionDeclaration, context: TypeDerivationContext): DeriveFunctionSchemaResult {
164-
let functionIsBroken = false;
165-
const issues: string[] = [];
166-
167164
const functionIdentifier = functionDeclaration.name ?? throwError("Function didn't have an identifier");
168165
const functionName = functionIdentifier.text
169166
const functionSymbol = context.typeChecker.getSymbolAtLocation(functionIdentifier) ?? throwError(`Function '${functionName}' didn't have a symbol`);
@@ -173,52 +170,43 @@ function deriveFunctionSchema(functionDeclaration: ts.FunctionDeclaration, conte
173170
const markedPureInJsDoc = functionSymbol.getJsDocTags().find(e => e.name === "pure") !== undefined;
174171

175172
const functionCallSig = functionType.getCallSignatures()[0] ?? throwError(`Function '${functionName}' didn't have a call signature`)
176-
const functionSchemaArguments: schema.ArgumentDefinition[] = functionCallSig.getParameters().flatMap(paramSymbol => {
173+
const functionSchemaArguments: Result<schema.ArgumentDefinition[], string[]> = Result.traverseAndCollectErrors(functionCallSig.getParameters(), paramSymbol => {
177174
const paramName = paramSymbol.getName();
178175
const paramDesc = ts.displayPartsToString(paramSymbol.getDocumentationComment(context.typeChecker)).trim();
179176
const paramType = context.typeChecker.getTypeOfSymbolAtLocation(paramSymbol, paramSymbol.valueDeclaration ?? throwError(`Function '${functionName}' parameter '${paramName}' didn't have a value declaration`));
180177
const paramTypePath: TypePathSegment[] = [{segmentType: "FunctionParameter", functionName, parameterName: paramName}];
181178

182-
const paramTypeResult = deriveSchemaTypeForTsType(paramType, paramTypePath, context);
183-
184-
if (paramTypeResult instanceof Err) {
185-
// Record the error, discard the parameter, but mark the function
186-
// as broken so we discard the whole thing at the end
187-
issues.push(...paramTypeResult.error)
188-
functionIsBroken = true;
189-
return [];
190-
} else {
191-
issues.push(...paramTypeResult.data.warnings)
192-
return [{
179+
return deriveSchemaTypeForTsType(paramType, paramTypePath, context)
180+
.map(paramTypeResult => ({
193181
argumentName: paramName,
194182
description: paramDesc ? paramDesc : null,
195-
type: paramTypeResult.data.typeDefinition,
196-
}]
197-
}
183+
type: paramTypeResult,
184+
}));
198185
});
199186

200187
const returnType = functionCallSig.getReturnType();
201188
const returnTypeResult = deriveSchemaTypeForTsType(unwrapPromiseType(returnType, context.typeChecker) ?? returnType, [{segmentType: "FunctionReturn", functionName}], context);
202-
let functionDefinition: schema.FunctionDefinition | null = null;
203-
if (returnTypeResult instanceof Err) {
204-
// Record the error, mark the function as broken so we discard the whole thing at the end
205-
issues.push(...returnTypeResult.error)
206-
functionIsBroken = true;
207-
functionDefinition = null;
208-
} else {
209-
issues.push(...returnTypeResult.data.warnings)
210-
functionDefinition = {
189+
190+
const functionDefinition = Result.collectErrors(functionSchemaArguments, returnTypeResult)
191+
.map(([functionSchemaArgs, returnType]) => ({
211192
description: functionDescription ? functionDescription : null,
212193
ndcKind: markedPureInJsDoc ? schema.FunctionNdcKind.Function : schema.FunctionNdcKind.Procedure,
213-
arguments: functionSchemaArguments,
214-
resultType: returnTypeResult.data.typeDefinition
215-
}
216-
}
194+
arguments: functionSchemaArgs,
195+
resultType: returnType
196+
}));
217197

218-
return {
219-
name: functionName,
220-
definition: !functionIsBroken ? functionDefinition : null,
221-
issues: issues
198+
if (functionDefinition instanceof Err) {
199+
return {
200+
name: functionName,
201+
definition: null,
202+
issues: functionDefinition.error
203+
}
204+
} else {
205+
return {
206+
name: functionName,
207+
definition: functionDefinition.data,
208+
issues: []
209+
}
222210
}
223211
}
224212

@@ -244,10 +232,7 @@ function typePathSegmentToString(segment: TypePathSegment): string {
244232
}
245233
}
246234

247-
type DerivedSchemaType =
248-
{ typeDefinition: schema.TypeDefinition, warnings: string[] }
249-
250-
function deriveSchemaTypeForTsType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number = 0): Result<DerivedSchemaType, string[]> {
235+
function deriveSchemaTypeForTsType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number = 0): Result<schema.TypeDefinition, string[]> {
251236
const typeRenderedName = context.typeChecker.typeToString(tsType);
252237

253238
if (recursionDepth > MAX_TYPE_DERIVATION_RECURSION)
@@ -310,68 +295,63 @@ function deriveSchemaTypeForTsType(tsType: ts.Type, typePath: TypePathSegment[],
310295
return new Err([`Union types are not supported, but one was encountered in ${typePathToString(typePath)} (type: ${context.typeChecker.typeToString(tsType)})`]);
311296
}
312297

313-
// We don't know how to deal with this type, so just make it an opaque scalar
314-
const typeName = generateTypeNameFromTypePath(typePath);
315-
context.scalarTypeDefinitions[typeName] = {};
316-
return new Ok({
317-
warnings: [`Unable to derive an NDC type for ${typePathToString(typePath)} (type: ${context.typeChecker.typeToString(tsType)}). Assuming that it is a scalar type.`],
318-
typeDefinition: { type: "named", kind: "scalar", name: typeName }
319-
});
298+
// We don't know how to deal with this type, so reject it with a generic error
299+
return new Err([`Unable to derive an NDC type for ${typePathToString(typePath)} (type: ${context.typeChecker.typeToString(tsType)}).`]);
320300
}
321301

322-
function deriveSchemaTypeIfTsArrayType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number): Result<DerivedSchemaType, string[]> | undefined {
302+
function deriveSchemaTypeIfTsArrayType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number): Result<schema.TypeDefinition, string[]> | undefined {
323303
if (context.typeChecker.isArrayType(tsType) && tsutils.isTypeReference(tsType)) {
324304
const typeArgs = context.typeChecker.getTypeArguments(tsType)
325305
if (typeArgs.length === 1) {
326306
const innerType = typeArgs[0]!;
327307
return deriveSchemaTypeForTsType(innerType, [...typePath, {segmentType: "Array"}], context, recursionDepth + 1)
328-
.map(innerTypeResult => ({ typeDefinition: { type: "array", elementType: innerTypeResult.typeDefinition }, warnings: innerTypeResult.warnings }));
308+
.map(innerType => ({ type: "array", elementType: innerType }));
329309
}
330310
}
331311
}
332312

333-
function deriveSchemaTypeIfScalarType(tsType: ts.Type, context: TypeDerivationContext): Result<DerivedSchemaType, string[]> | undefined {
313+
function deriveSchemaTypeIfScalarType(tsType: ts.Type, context: TypeDerivationContext): Result<schema.TypeDefinition, string[]> | undefined {
334314
if (tsutils.isIntrinsicBooleanType(tsType)) {
335315
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.Boolean] = {};
336-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Boolean }, warnings: [] });
316+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Boolean });
337317
}
338318
if (tsutils.isBooleanLiteralType(tsType)) {
339319
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.Boolean] = {};
340320
const literalValue = tsType.intrinsicName === "true" ? true : false; // Unfortunately the types lie, tsType.value is undefined here :(
341-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Boolean, literalValue: literalValue }, warnings: [] });
321+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Boolean, literalValue: literalValue });
342322
}
343323
if (tsutils.isIntrinsicStringType(tsType)) {
344324
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.String] = {};
345-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.String }, warnings: [] });
325+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.String });
346326
}
347327
if (tsutils.isStringLiteralType(tsType)) {
348328
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.String] = {};
349-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.String, literalValue: tsType.value }, warnings: [] });
329+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.String, literalValue: tsType.value });
350330
}
351331
if (tsutils.isIntrinsicNumberType(tsType)) {
352332
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.Float] = {};
353-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Float }, warnings: [] });
333+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Float });
354334
}
355335
if (tsutils.isNumberLiteralType(tsType)) {
356336
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.Float] = {};
357-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Float, literalValue: tsType.value }, warnings: [] });
337+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.Float, literalValue: tsType.value });
358338
}
359339
if (tsutils.isIntrinsicBigIntType(tsType)) {
360340
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.BigInt] = {};
361-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.BigInt }, warnings: [] });
341+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.BigInt });
362342
}
363343
if (tsutils.isBigIntLiteralType(tsType)) {
364344
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.BigInt] = {};
365345
const literalValue = BigInt(`${tsType.value.negative ? "-" : ""}${tsType.value.base10Value}`);
366-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.BigInt, literalValue: literalValue }, warnings: [] });
346+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.BigInt, literalValue: literalValue });
367347
}
368348
if (isDateType(tsType)) {
369349
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.DateTime] = {};
370-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.DateTime }, warnings: [] });
350+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.DateTime });
371351
}
372352
if (isJSONValueType(tsType, context.ndcLambdaSdkModule)) {
373353
context.scalarTypeDefinitions[schema.BuiltInScalarTypeName.JSON] = {};
374-
return new Ok({ typeDefinition: { type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.JSON }, warnings: [] });
354+
return new Ok({ type: "named", kind: "scalar", name: schema.BuiltInScalarTypeName.JSON });
375355
}
376356
}
377357

@@ -401,37 +381,33 @@ function isJSONValueType(tsType: ts.Type, ndcLambdaSdkModule: ts.ResolvedModuleF
401381
return sourceFile.fileName.startsWith(sdkDirectory);
402382
}
403383

404-
function deriveSchemaTypeIfNullableType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number): Result<DerivedSchemaType, string[]> | undefined {
384+
function deriveSchemaTypeIfNullableType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number): Result<schema.TypeDefinition, string[]> | undefined {
405385
const notNullableResult = unwrapNullableType(tsType);
406386
if (notNullableResult !== null) {
407387
const [notNullableType, nullOrUndefinability] = notNullableResult;
408388
return deriveSchemaTypeForTsType(notNullableType, typePath, context, recursionDepth + 1)
409-
.map(notNullableTypeResult => ({ typeDefinition: { type: "nullable", underlyingType: notNullableTypeResult.typeDefinition, nullOrUndefinability }, warnings: notNullableTypeResult.warnings }))
389+
.map(notNullableType => ({ type: "nullable", underlyingType: notNullableType, nullOrUndefinability }))
410390
}
411391
}
412392

413-
function deriveSchemaTypeIfObjectType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number): Result<DerivedSchemaType, string[]> | undefined {
393+
function deriveSchemaTypeIfObjectType(tsType: ts.Type, typePath: TypePathSegment[], context: TypeDerivationContext, recursionDepth: number): Result<schema.TypeDefinition, string[]> | undefined {
414394
const info = getObjectTypeInfo(tsType, typePath, context.typeChecker, context.functionsFilePath);
415395
if (info) {
416396
// Short-circuit recursion if the type has already been named
417397
if (context.objectTypeDefinitions[info.generatedTypeName]) {
418-
return new Ok({ typeDefinition: { type: 'named', name: info.generatedTypeName, kind: "object" }, warnings: [] });
398+
return new Ok({ type: 'named', name: info.generatedTypeName, kind: "object" });
419399
}
420400

421401
context.objectTypeDefinitions[info.generatedTypeName] = { properties: [] }; // Break infinite recursion
422402

423-
const warnings: string[] = [];
424403
const propertyResults = Result.traverseAndCollectErrors(Array.from(info.members), ([propertyName, propertyType]) => {
425404
return deriveSchemaTypeForTsType(propertyType, [...typePath, { segmentType: "ObjectProperty", typeName: info.generatedTypeName, propertyName }], context, recursionDepth + 1)
426-
.map(propertyTypeResult => {
427-
warnings.push(...propertyTypeResult.warnings)
428-
return { propertyName: propertyName, type: propertyTypeResult.typeDefinition }
429-
});
405+
.map(propertyType => ({ propertyName: propertyName, type: propertyType }));
430406
});
431407

432408
if (propertyResults instanceof Ok) {
433409
context.objectTypeDefinitions[info.generatedTypeName] = { properties: propertyResults.data }
434-
return new Ok({ typeDefinition: { type: 'named', name: info.generatedTypeName, kind: "object" }, warnings })
410+
return new Ok({ type: 'named', name: info.generatedTypeName, kind: "object" })
435411
} else {
436412
// Remove the recursion short-circuit to ensure errors are raised if this type is encountered again
437413
delete context.objectTypeDefinitions[info.generatedTypeName];

0 commit comments

Comments
 (0)