Comparing version 0.0.0-main-afbec52b to 0.0.0-main-b0fafefe
{ | ||
"name": "grats", | ||
"version": "0.0.11", | ||
"version": "0.0.25", | ||
"main": "dist/src/index.js", | ||
@@ -9,3 +9,4 @@ "bin": "dist/src/cli.js", | ||
"files": [ | ||
"dist" | ||
"dist", | ||
"!dist/src/tests" | ||
], | ||
@@ -15,3 +16,3 @@ "scripts": { | ||
"integration-tests": "node src/tests/integration.mjs", | ||
"build": "tsc --build", | ||
"build": "rm -rf dist/ && tsc --build", | ||
"format": "prettier . --write", | ||
@@ -47,3 +48,23 @@ "lint": "eslint . && prettier . --check" | ||
"pnpm": "^8" | ||
} | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/captbaritone/grats/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/captbaritone/grats.git" | ||
}, | ||
"author": { | ||
"name": "Jordan Eldredge", | ||
"email": "jordan@jordaneldredge.com", | ||
"url": "https://jordaneldredge.com" | ||
}, | ||
"keywords": [ | ||
"graphql", | ||
"typescript", | ||
"resolvers", | ||
"schema", | ||
"code-first", | ||
"implementation-first" | ||
] | ||
} |
@@ -79,8 +79,9 @@ #!/usr/bin/env node | ||
var config = getTsConfigOrReportAndExit(tsconfig).config; | ||
var schemaResult = (0, lib_1.buildSchemaResult)(config); | ||
if (schemaResult.kind === "ERROR") { | ||
console.error(schemaResult.err.formatDiagnosticsWithColorAndContext()); | ||
var schemaAndDocResult = (0, lib_1.buildSchemaAndDocResult)(config); | ||
if (schemaAndDocResult.kind === "ERROR") { | ||
console.error(schemaAndDocResult.err.formatDiagnosticsWithColorAndContext()); | ||
process.exit(1); | ||
} | ||
var loc = (0, Locate_1.locate)(schemaResult.value, entity); | ||
var schema = schemaAndDocResult.value.schema; | ||
var loc = (0, Locate_1.locate)(schema, entity); | ||
if (loc.kind === "ERROR") { | ||
@@ -101,3 +102,3 @@ console.error(loc.err); | ||
// For now we just rebuild the schema on every change. | ||
var schemaResult = (0, lib_1.extractSchema)(config, program.getProgram()); | ||
var schemaResult = (0, lib_1.extractSchemaAndDoc)(config, program.getProgram()); | ||
if (schemaResult.kind === "ERROR") { | ||
@@ -116,8 +117,8 @@ reportDiagnostics(schemaResult.err); | ||
var _a = getTsConfigOrReportAndExit(tsconfig), config = _a.config, configPath = _a.configPath; | ||
var schemaResult = (0, lib_1.buildSchemaResult)(config); | ||
if (schemaResult.kind === "ERROR") { | ||
console.error(schemaResult.err.formatDiagnosticsWithColorAndContext()); | ||
var schemaAndDocResult = (0, lib_1.buildSchemaAndDocResult)(config); | ||
if (schemaAndDocResult.kind === "ERROR") { | ||
console.error(schemaAndDocResult.err.formatDiagnosticsWithColorAndContext()); | ||
process.exit(1); | ||
} | ||
writeSchemaFilesAndReport(schemaResult.value, config, configPath); | ||
writeSchemaFilesAndReport(schemaAndDocResult.value, config, configPath); | ||
} | ||
@@ -127,3 +128,4 @@ /** | ||
*/ | ||
function writeSchemaFilesAndReport(schema, config, configPath) { | ||
function writeSchemaFilesAndReport(schemaAndDoc, config, configPath) { | ||
var schema = schemaAndDoc.schema, doc = schemaAndDoc.doc; | ||
var gratsOptions = config.raw.grats; | ||
@@ -134,3 +136,3 @@ var dest = (0, path_1.resolve)((0, path_1.dirname)(configPath), gratsOptions.tsSchema); | ||
console.error("Grats: Wrote TypeScript schema to `".concat(dest, "`.")); | ||
var schemaStr = (0, printSchema_1.printGratsSDL)(schema, gratsOptions); | ||
var schemaStr = (0, printSchema_1.printGratsSDL)(doc, gratsOptions); | ||
var absOutput = (0, path_1.resolve)((0, path_1.dirname)(configPath), gratsOptions.graphqlSchema); | ||
@@ -137,0 +139,0 @@ (0, fs_1.writeFileSync)(absOutput, schemaStr); |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
@@ -34,2 +45,6 @@ var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
var gratsRoot_1 = require("./gratsRoot"); | ||
var publicDirectives_1 = require("./publicDirectives"); | ||
var codegenHelpers_1 = require("./codegenHelpers"); | ||
var helpers_1 = require("./utils/helpers"); | ||
var RESOLVER_ARGS = ["source", "args", "context", "info"]; | ||
var F = ts.factory; | ||
@@ -47,2 +62,3 @@ // Given a GraphQL SDL, returns the a string of TypeScript code that generates a | ||
this._imports = []; | ||
this._helpers = new Map(); | ||
this._typeDefinitions = new Set(); | ||
@@ -145,3 +161,3 @@ this._graphQLImports = new Set(); | ||
this.description(obj.description), | ||
this.fields(obj), | ||
this.fields(obj, false), | ||
this.interfaces(obj), | ||
@@ -152,14 +168,24 @@ ]); | ||
var _this = this; | ||
var args = ["source", "args", "context", "info"]; | ||
var exported = fieldDirective(field, metadataDirectives_1.EXPORTED_DIRECTIVE); | ||
if (exported != null) { | ||
var exportedMetadata = (0, metadataDirectives_1.parseExportedDirective)(exported); | ||
var module_1 = exportedMetadata.tsModulePath; | ||
var funcName = exportedMetadata.exportedFunctionName; | ||
var argCount = exportedMetadata.argCount; | ||
var metadataDirective = fieldDirective(field, metadataDirectives_1.FIELD_METADATA_DIRECTIVE); | ||
if (metadataDirective == null) { | ||
throw new Error("Expected to find metadata directive."); | ||
} | ||
var metadata = (0, metadataDirectives_1.parseFieldMetadataDirective)(metadataDirective); | ||
if (metadata.tsModulePath != null) { | ||
var module_1 = metadata.tsModulePath; | ||
var funcName = metadata.name; | ||
if (funcName == null) { | ||
throw new Error("Expected to find name in metadata directive."); | ||
} | ||
var argCount = metadata.argCount; | ||
if (argCount == null) { | ||
throw new Error("Expected to find argCount in metadata directive."); | ||
} | ||
var abs = (0, gratsRoot_1.resolveRelativePath)(module_1); | ||
var relative = stripExt(path.relative(path.dirname(this._destination), abs)); | ||
var resolverName = formatResolverFunctionVarName(parentTypeName, funcName); | ||
this.import("./".concat(relative), [{ name: funcName, as: resolverName }]); | ||
var usedArgs = args.slice(0, argCount); | ||
this.import("./".concat(normalizeRelativePathToPosix(relative)), [ | ||
{ name: funcName, as: resolverName }, | ||
]); | ||
var usedArgs = RESOLVER_ARGS.slice(0, argCount); | ||
return this.method(methodName, usedArgs.map(function (name) { | ||
@@ -173,22 +199,60 @@ return _this.param(name); | ||
} | ||
var propertyName = fieldDirective(field, metadataDirectives_1.FIELD_NAME_DIRECTIVE); | ||
if (propertyName != null) { | ||
var name = (0, metadataDirectives_1.parsePropertyNameDirective)(propertyName).name; | ||
var prop = F.createPropertyAccessExpression(F.createIdentifier("source"), F.createIdentifier(name)); | ||
var callExpression = F.createCallExpression(prop, undefined, args.map(function (name) { | ||
return F.createIdentifier(name); | ||
})); | ||
var isFunc = F.createStrictEquality(F.createTypeOfExpression(prop), F.createStringLiteral("function")); | ||
var ternary = F.createConditionalExpression(isFunc, undefined, callExpression, undefined, prop); | ||
return this.method(methodName, args.map(function (name) { | ||
return _this.param(name); | ||
}), [F.createReturnStatement(ternary)]); | ||
if (metadata.name != null) { | ||
var prop = F.createPropertyAccessExpression(F.createIdentifier("source"), F.createIdentifier(metadata.name)); | ||
var valueExpression = prop; | ||
if (metadata.argCount != null) { | ||
valueExpression = F.createCallExpression(prop, undefined, RESOLVER_ARGS.map(function (name) { | ||
return F.createIdentifier(name); | ||
})); | ||
} | ||
return this.method(methodName, RESOLVER_ARGS.map(function (name) { return _this.param(name); }), [F.createReturnStatement(valueExpression)]); | ||
} | ||
// If the resolver name matches the field name, and the field is not backed by a function, | ||
// we can just use the default resolver. | ||
return null; | ||
}; | ||
Codegen.prototype.fields = function (obj) { | ||
// If a field is smantically non-null, we need to wrap the resolver in a | ||
// runtime check to ensure that the resolver does not return null. | ||
Codegen.prototype.maybeApplySemanticNullRuntimeCheck = function (field, method_) { | ||
var _a; | ||
var semanticNonNull = fieldDirective(field, publicDirectives_1.SEMANTIC_NON_NULL_DIRECTIVE); | ||
if (semanticNonNull == null) { | ||
return method_; | ||
} | ||
if (!this._helpers.has(codegenHelpers_1.ASSERT_NON_NULL_HELPER)) { | ||
this._helpers.set(codegenHelpers_1.ASSERT_NON_NULL_HELPER, (0, codegenHelpers_1.createAssertNonNullHelper)()); | ||
} | ||
var method = method_ !== null && method_ !== void 0 ? method_ : this.defaultResolverMethod(); | ||
var bodyStatements = (_a = method.body) === null || _a === void 0 ? void 0 : _a.statements; | ||
if (bodyStatements == null || bodyStatements.length === 0) { | ||
throw new Error("Expected method to have a body"); | ||
} | ||
var foundReturn = false; | ||
var newBodyStatements = bodyStatements.map(function (statement) { | ||
if (ts.isReturnStatement(statement)) { | ||
if (statement.expression == null) { | ||
throw new Error("Expected return statement to have an expression"); | ||
} | ||
foundReturn = true; | ||
// We need to wrap the return statement in a call to the runtime check | ||
return F.createReturnStatement(F.createCallExpression(F.createIdentifier(codegenHelpers_1.ASSERT_NON_NULL_HELPER), [], [statement.expression])); | ||
} | ||
return statement; | ||
}); | ||
if (!foundReturn) { | ||
throw new Error("Expected method to have a return statement"); | ||
} | ||
return __assign(__assign({}, method), { body: F.createBlock(newBodyStatements, true) }); | ||
}; | ||
Codegen.prototype.defaultResolverMethod = function () { | ||
var _this = this; | ||
return this.method("resolve", RESOLVER_ARGS.map(function (name) { return _this.param(name); }), [ | ||
F.createReturnStatement(F.createCallExpression(this.graphQLImport("defaultFieldResolver"), undefined, RESOLVER_ARGS.map(function (name) { return F.createIdentifier(name); }))), | ||
]); | ||
}; | ||
Codegen.prototype.fields = function (obj, isInterface) { | ||
var _this = this; | ||
var fields = Object.entries(obj.getFields()).map(function (_a) { | ||
var _b = __read(_a, 2), name = _b[0], field = _b[1]; | ||
return F.createPropertyAssignment(name, _this.fieldConfig(field, obj.name)); | ||
return F.createPropertyAssignment(name, _this.fieldConfig(field, obj.name, isInterface)); | ||
}); | ||
@@ -221,3 +285,3 @@ return this.method("fields", [], [F.createReturnStatement(this.objectLiteral(fields))]); | ||
F.createPropertyAssignment("name", F.createStringLiteral(obj.name)), | ||
this.fields(obj), | ||
this.fields(obj, true), | ||
this.interfaces(obj), | ||
@@ -298,4 +362,4 @@ ]); | ||
}; | ||
Codegen.prototype.fieldConfig = function (field, parentTypeName) { | ||
return this.objectLiteral(__spreadArray([ | ||
Codegen.prototype.fieldConfig = function (field, parentTypeName, isInterface) { | ||
var props = [ | ||
this.description(field.description), | ||
@@ -307,14 +371,25 @@ this.deprecated(field), | ||
? F.createPropertyAssignment("args", this.argMap(field.args)) | ||
: null | ||
], __read(this.fieldMethods(field, parentTypeName)), false)); | ||
: null, | ||
]; | ||
if (!isInterface) { | ||
(0, helpers_1.extend)(props, this.fieldMethods(field, parentTypeName)); | ||
} | ||
return this.objectLiteral(props); | ||
}; | ||
Codegen.prototype.fieldMethods = function (field, parentTypeName) { | ||
var asyncIterable = fieldDirective(field, metadataDirectives_1.ASYNC_ITERABLE_TYPE_DIRECTIVE); | ||
if (asyncIterable == null) { | ||
return [this.resolveMethod(field, "resolve", parentTypeName)]; | ||
var metadataDirective = fieldDirective(field, metadataDirectives_1.FIELD_METADATA_DIRECTIVE); | ||
if (metadataDirective == null) { | ||
throw new Error("Expected to find metadata directive."); | ||
} | ||
// Note: We assume the default name is used here. When custom operation types are supported | ||
// we'll need to update this. | ||
if (parentTypeName !== "Subscription") { | ||
var resolve = this.resolveMethod(field, "resolve", parentTypeName); | ||
return [this.maybeApplySemanticNullRuntimeCheck(field, resolve)]; | ||
} | ||
return [ | ||
// TODO: Maybe avoid adding `assertNonNull` for subscription resolvers? | ||
this.resolveMethod(field, "subscribe", parentTypeName), | ||
// Identity function (method?) | ||
this.method("resolve", [this.param("payload")], [F.createReturnStatement(F.createIdentifier("payload"))]), | ||
this.maybeApplySemanticNullRuntimeCheck(field, this.method("resolve", [this.param("payload")], [F.createReturnStatement(F.createIdentifier("payload"))])), | ||
]; | ||
@@ -463,4 +538,4 @@ }; | ||
// Helper for the common case of a single string argument. | ||
Codegen.prototype.param = function (name) { | ||
return F.createParameterDeclaration(undefined, undefined, name, undefined, undefined, undefined); | ||
Codegen.prototype.param = function (name, type) { | ||
return F.createParameterDeclaration(undefined, undefined, name, undefined, type, undefined); | ||
}; | ||
@@ -482,3 +557,3 @@ Codegen.prototype.import = function (from, names) { | ||
this.import("graphql", __spreadArray([], __read(this._graphQLImports), false).map(function (name) { return ({ name: name }); })); | ||
return printer.printList(ts.ListFormat.MultiLine, F.createNodeArray(__spreadArray(__spreadArray([], __read(this._imports), false), __read(this._statements), false)), sourceFile); | ||
return printer.printList(ts.ListFormat.MultiLine, F.createNodeArray(__spreadArray(__spreadArray(__spreadArray([], __read(this._imports), false), __read(this._helpers.values()), false), __read(this._statements), false)), sourceFile); | ||
}; | ||
@@ -505,1 +580,5 @@ return Codegen; | ||
} | ||
// https://github.com/sindresorhus/slash/blob/98b618f5a3bfcb5dd374b204868818845b87bb2f/index.js#L8C9-L8C33 | ||
function normalizeRelativePathToPosix(unknownPath) { | ||
return unknownPath.replace(/\\/g, "/"); | ||
} |
@@ -34,2 +34,3 @@ export declare const ISSUE_URL = "https://github.com/captbaritone/grats/issues"; | ||
export declare function inputTypeFieldNotProperty(): string; | ||
export declare function inputInterfaceFieldNotProperty(): string; | ||
export declare function inputFieldUntyped(): string; | ||
@@ -56,2 +57,3 @@ export declare function typeTagOnUnnamedClass(): string; | ||
export declare function wrapperMissingTypeArg(): string; | ||
export declare function invalidWrapperOnInputType(wrapperName: string): string; | ||
export declare function cannotResolveSymbolForDescription(): string; | ||
@@ -92,4 +94,15 @@ export declare function propertyFieldMissingType(): string; | ||
export declare function subscriptionFieldNotAsyncIterable(): string; | ||
export declare function nonSubscriptionFieldAsyncIterable(): string; | ||
export declare function operationTypeNotUnknown(): string; | ||
export declare function expectedNullableArgumentToBeOptional(): string; | ||
export declare function gqlTagInLineComment(): string; | ||
export declare function gqlTagInNonJSDocBlockComment(): string; | ||
export declare function gqlTagInDetachedJSDocBlockComment(): string; | ||
export declare function gqlFieldTagOnInputType(): string; | ||
export declare function gqlFieldParentMissingTag(): string; | ||
export declare function missingSpecifiedByUrl(): string; | ||
export declare function specifiedByOnWrongNode(): string; | ||
export declare function missingGenericType(templateName: string, paramName: string): string; | ||
export declare function nonGraphQLGenericType(templateName: string, paramName: string): string; | ||
export declare function genericTypeUsedAsUnionMember(): string; | ||
export declare function genericTypeImplementsInterface(): string; | ||
export declare function concreteTypeMissingTypename(implementor: string): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defaultArgElementIsNotAssignment = exports.defaultValueIsNotLiteral = exports.ambiguousNumberType = exports.expectedOneNonNullishType = exports.propertyFieldMissingType = exports.cannotResolveSymbolForDescription = exports.wrapperMissingTypeArg = exports.methodMissingType = exports.gqlEntityMissingName = exports.enumVariantMissingInitializer = exports.enumVariantNotStringLiteral = exports.enumTagOnInvalidNode = exports.argNotTyped = exports.argNameNotLiteral = exports.argIsNotProperty = exports.argumentParamIsNotObject = exports.argumentParamIsMissingType = exports.typeNameDoesNotMatchExpected = exports.typeNameTypeNotStringLiteral = exports.typeNameMissingTypeAnnotation = exports.typeNameInitializerWrong = exports.typeNameInitializeNotString = exports.typeNameMissingInitializer = exports.typeNameNotDeclaration = exports.typeTagOnAliasOfNonObjectOrUnknown = exports.typeTagOnUnnamedClass = exports.inputFieldUntyped = exports.inputTypeFieldNotProperty = exports.inputTypeNotLiteral = exports.functionFieldNotNamedExport = exports.functionFieldDefaultExport = exports.functionFieldNotNamed = exports.functionFieldParentTypeNotValid = exports.functionFieldParentTypeMissing = exports.functionFieldNotTopLevel = exports.invalidReturnTypeForFunctionField = exports.invalidParentArgForFunctionField = exports.expectedUnionTypeReference = exports.expectedUnionTypeNode = exports.invalidUnionTagUsage = exports.invalidInputTagUsage = exports.invalidEnumTagUsage = exports.invalidInterfaceTagUsage = exports.invalidScalarTagUsage = exports.invalidTypeTagUsage = exports.invalidGratsTag = exports.wrongCasingForGratsTag = exports.killsParentOnExceptionOnWrongNode = exports.fieldTagOnWrongNode = exports.ISSUE_URL = void 0; | ||
exports.expectedNullableArgumentToBeOptional = exports.operationTypeNotUnknown = exports.nonSubscriptionFieldAsyncIterable = exports.subscriptionFieldNotAsyncIterable = exports.graphQLTagNameHasWhitespace = exports.graphQLNameHasLeadingNewlines = exports.multipleContextTypes = exports.unexpectedParamSpreadForContextParam = exports.expectedTypeAnnotationOnContextToHaveDeclaration = exports.expectedTypeAnnotationOnContextToBeResolvable = exports.expectedTypeAnnotationOfReferenceOnContext = exports.expectedTypeAnnotationOnContext = exports.unresolvedTypeReference = exports.invalidTypePassedToFieldFunction = exports.parameterPropertyMissingType = exports.parameterPropertyNotPublic = exports.parameterWithoutModifiers = exports.duplicateInterfaceTag = exports.duplicateTag = exports.implementsTagOnTypeAlias = exports.implementsTagOnInterface = exports.implementsTagOnClass = exports.mergedInterfaces = exports.nonNullTypeCannotBeOptional = exports.killsParentOnExceptionOnNullable = exports.killsParentOnExceptionWithWrongConfig = exports.expectedNameIdentifier = exports.pluralTypeMissingParameter = exports.unknownGraphQLType = exports.unsupportedTypeLiteral = exports.defaultArgPropertyMissingInitializer = exports.defaultArgPropertyMissingName = void 0; | ||
exports.ambiguousNumberType = exports.expectedOneNonNullishType = exports.propertyFieldMissingType = exports.cannotResolveSymbolForDescription = exports.invalidWrapperOnInputType = exports.wrapperMissingTypeArg = exports.methodMissingType = exports.gqlEntityMissingName = exports.enumVariantMissingInitializer = exports.enumVariantNotStringLiteral = exports.enumTagOnInvalidNode = exports.argNotTyped = exports.argNameNotLiteral = exports.argIsNotProperty = exports.argumentParamIsNotObject = exports.argumentParamIsMissingType = exports.typeNameDoesNotMatchExpected = exports.typeNameTypeNotStringLiteral = exports.typeNameMissingTypeAnnotation = exports.typeNameInitializerWrong = exports.typeNameInitializeNotString = exports.typeNameMissingInitializer = exports.typeNameNotDeclaration = exports.typeTagOnAliasOfNonObjectOrUnknown = exports.typeTagOnUnnamedClass = exports.inputFieldUntyped = exports.inputInterfaceFieldNotProperty = exports.inputTypeFieldNotProperty = exports.inputTypeNotLiteral = exports.functionFieldNotNamedExport = exports.functionFieldDefaultExport = exports.functionFieldNotNamed = exports.functionFieldParentTypeNotValid = exports.functionFieldParentTypeMissing = exports.functionFieldNotTopLevel = exports.invalidReturnTypeForFunctionField = exports.invalidParentArgForFunctionField = exports.expectedUnionTypeReference = exports.expectedUnionTypeNode = exports.invalidUnionTagUsage = exports.invalidInputTagUsage = exports.invalidEnumTagUsage = exports.invalidInterfaceTagUsage = exports.invalidScalarTagUsage = exports.invalidTypeTagUsage = exports.invalidGratsTag = exports.wrongCasingForGratsTag = exports.killsParentOnExceptionOnWrongNode = exports.fieldTagOnWrongNode = exports.ISSUE_URL = void 0; | ||
exports.concreteTypeMissingTypename = exports.genericTypeImplementsInterface = exports.genericTypeUsedAsUnionMember = exports.nonGraphQLGenericType = exports.missingGenericType = exports.specifiedByOnWrongNode = exports.missingSpecifiedByUrl = exports.gqlFieldParentMissingTag = exports.gqlFieldTagOnInputType = exports.gqlTagInDetachedJSDocBlockComment = exports.gqlTagInNonJSDocBlockComment = exports.gqlTagInLineComment = exports.expectedNullableArgumentToBeOptional = exports.operationTypeNotUnknown = exports.subscriptionFieldNotAsyncIterable = exports.graphQLTagNameHasWhitespace = exports.graphQLNameHasLeadingNewlines = exports.multipleContextTypes = exports.unexpectedParamSpreadForContextParam = exports.expectedTypeAnnotationOnContextToHaveDeclaration = exports.expectedTypeAnnotationOnContextToBeResolvable = exports.expectedTypeAnnotationOfReferenceOnContext = exports.expectedTypeAnnotationOnContext = exports.unresolvedTypeReference = exports.invalidTypePassedToFieldFunction = exports.parameterPropertyMissingType = exports.parameterPropertyNotPublic = exports.parameterWithoutModifiers = exports.duplicateInterfaceTag = exports.duplicateTag = exports.implementsTagOnTypeAlias = exports.implementsTagOnInterface = exports.implementsTagOnClass = exports.mergedInterfaces = exports.nonNullTypeCannotBeOptional = exports.killsParentOnExceptionOnNullable = exports.killsParentOnExceptionWithWrongConfig = exports.expectedNameIdentifier = exports.pluralTypeMissingParameter = exports.unknownGraphQLType = exports.unsupportedTypeLiteral = exports.defaultArgPropertyMissingInitializer = exports.defaultArgPropertyMissingName = exports.defaultArgElementIsNotAssignment = exports.defaultValueIsNotLiteral = void 0; | ||
var Extractor_1 = require("./Extractor"); | ||
@@ -11,2 +11,3 @@ exports.ISSUE_URL = "https://github.com/captbaritone/grats/issues"; | ||
parameterProperties: "https://grats.capt.dev/docs/docblock-tags/fields#class-based-fields", | ||
commentSyntax: "https://grats.capt.dev/docs/getting-started/comment-syntax", | ||
}; | ||
@@ -24,3 +25,3 @@ /** | ||
function fieldTagOnWrongNode() { | ||
return "`@".concat(Extractor_1.FIELD_TAG, "` can only be used on method/property declarations or signatures."); | ||
return "`@".concat(Extractor_1.FIELD_TAG, "` can only be used on method/property declarations, signatures, or function declarations."); | ||
} | ||
@@ -58,3 +59,3 @@ exports.fieldTagOnWrongNode = fieldTagOnWrongNode; | ||
function invalidInputTagUsage() { | ||
return "`@".concat(Extractor_1.INPUT_TAG, "` can only be used on type alias declarations. e.g. `type MyInput = { foo: string }`"); | ||
return "`@".concat(Extractor_1.INPUT_TAG, "` can only be used on type alias or interface declarations. e.g. `type MyInput = { foo: string }` or `interface MyInput { foo: string }`"); | ||
} | ||
@@ -115,2 +116,6 @@ exports.invalidInputTagUsage = invalidInputTagUsage; | ||
exports.inputTypeFieldNotProperty = inputTypeFieldNotProperty; | ||
function inputInterfaceFieldNotProperty() { | ||
return "`@".concat(Extractor_1.INPUT_TAG, "` interfaces only support property signature members. e.g. `interface MyInput { foo: string }`"); | ||
} | ||
exports.inputInterfaceFieldNotProperty = inputInterfaceFieldNotProperty; | ||
function inputFieldUntyped() { | ||
@@ -201,2 +206,6 @@ return 'Input field must have an explicit type annotation. Grats uses the type annotation to determine the type of the field, so it must be explicit in order for Grats to "see" the type.'; | ||
exports.wrapperMissingTypeArg = wrapperMissingTypeArg; | ||
function invalidWrapperOnInputType(wrapperName) { | ||
return "Invalid input type. `".concat(wrapperName, "` is not a valid type when used as a GraphQL input value."); | ||
} | ||
exports.invalidWrapperOnInputType = invalidWrapperOnInputType; | ||
function cannotResolveSymbolForDescription() { | ||
@@ -266,3 +275,3 @@ return "Expected TypeScript to be able to resolve this GraphQL entity to a symbol. Is it possible that this type is not defined in this file? Grats needs to follow type references to their declaration in order to determine which GraphQL name is being referenced."; | ||
"If an interface is declared multiple times in a scope, TypeScript merges them.", | ||
"To avoid ambiguity Grats does not support using merged interfaces as GraphQL interfaces.", | ||
"To avoid ambiguity Grats does not support using merged interfaces as GraphQL definitions.", | ||
"Consider using a unique name for your TypeScript interface and renaming it.\n\n", | ||
@@ -357,6 +366,2 @@ "Learn more: ".concat(DOC_URLS.mergedInterfaces), | ||
exports.subscriptionFieldNotAsyncIterable = subscriptionFieldNotAsyncIterable; | ||
function nonSubscriptionFieldAsyncIterable() { | ||
return "Unexpected AsyncIterable. Only fields on `Subscription` should return an `AsyncIterable`. Non-subscription fields are only expected to return a single value."; | ||
} | ||
exports.nonSubscriptionFieldAsyncIterable = nonSubscriptionFieldAsyncIterable; | ||
function operationTypeNotUnknown() { | ||
@@ -370,1 +375,49 @@ return "Operation types `Query`, `Mutation`, and `Subscription` must be defined as type aliases of `unknown`. E.g. `type Query = unknown`. This is because GraphQL servers do not have an agreed upon way to produce root values, and Grats errs on the side of safety. If you are trying to implement dependency injection, consider using the `context` argument passed to each resolver instead. If you have a strong use case for a concrete root value, please file an issue."; | ||
exports.expectedNullableArgumentToBeOptional = expectedNullableArgumentToBeOptional; | ||
function gqlTagInLineComment() { | ||
return "Unexpected Grats tag in line (`//`) comment. Grats looks for tags in JSDoc-style block comments. e.g. `/** @gqlType */`. For more information see: ".concat(DOC_URLS.commentSyntax); | ||
} | ||
exports.gqlTagInLineComment = gqlTagInLineComment; | ||
function gqlTagInNonJSDocBlockComment() { | ||
return "Unexpected Grats tag in non-JSDoc-style block comment. Grats only looks for tags in JSDoc-style block comments which start with `/**`. For more information see: ".concat(DOC_URLS.commentSyntax); | ||
} | ||
exports.gqlTagInNonJSDocBlockComment = gqlTagInNonJSDocBlockComment; | ||
function gqlTagInDetachedJSDocBlockComment() { | ||
return "Unexpected Grats tag in detached docblock. Grats was unable to determine which TypeScript declaration this docblock is associated with. Moving the docblock to a position with is unambiguously \"above\" the relevant declaration may help. For more information see: ".concat(DOC_URLS.commentSyntax); | ||
} | ||
exports.gqlTagInDetachedJSDocBlockComment = gqlTagInDetachedJSDocBlockComment; | ||
function gqlFieldTagOnInputType() { | ||
return "The tag `@".concat(Extractor_1.FIELD_TAG, "` is not needed on fields of input types. All fields are automatically included as part of the input type. This tag can be safely removed."); | ||
} | ||
exports.gqlFieldTagOnInputType = gqlFieldTagOnInputType; | ||
function gqlFieldParentMissingTag() { | ||
return "Unexpected `@".concat(Extractor_1.FIELD_TAG, "`. The parent construct must be either a `@").concat(Extractor_1.TYPE_TAG, "` or `@").concat(Extractor_1.INTERFACE_TAG, "` tag. Are you missing one of these tags?"); | ||
} | ||
exports.gqlFieldParentMissingTag = gqlFieldParentMissingTag; | ||
function missingSpecifiedByUrl() { | ||
return "Expected `@".concat(Extractor_1.SPECIFIED_BY_TAG, "` tag to be followed by a URL. This URL will be used as the `url` argument to the `@specifiedBy` directive in the generated GraphQL schema. See https://spec.graphql.org/draft/#sec--specifiedBy for more information."); | ||
} | ||
exports.missingSpecifiedByUrl = missingSpecifiedByUrl; | ||
function specifiedByOnWrongNode() { | ||
return "Unexpected `@".concat(Extractor_1.SPECIFIED_BY_TAG, "` tag on non-scalar declaration. `@").concat(Extractor_1.SPECIFIED_BY_TAG, "` can only be used on custom scalar declarations. Are you missing a `@").concat(Extractor_1.SCALAR_TAG, "` tag?"); | ||
} | ||
exports.specifiedByOnWrongNode = specifiedByOnWrongNode; | ||
function missingGenericType(templateName, paramName) { | ||
return "Missing type argument for generic GraphQL type. Expected `".concat(templateName, "` to be passed a GraphQL type argument for type parameter `").concat(paramName, "`."); | ||
} | ||
exports.missingGenericType = missingGenericType; | ||
function nonGraphQLGenericType(templateName, paramName) { | ||
return "Expected `".concat(templateName, "` to be passed a GraphQL type argument for type parameter `").concat(paramName, "`."); | ||
} | ||
exports.nonGraphQLGenericType = nonGraphQLGenericType; | ||
function genericTypeUsedAsUnionMember() { | ||
return "Unexpected generic type used as union member. Generic type may not currently be used as members of a union. Grats requires that all union members define a `__typename` field typed as a string literal matching the type's name. Since generic types are synthesized into multiple types with different names, Grats cannot ensure they have a correct `__typename` property and thus cannot be used as members of a union."; | ||
} | ||
exports.genericTypeUsedAsUnionMember = genericTypeUsedAsUnionMember; | ||
function genericTypeImplementsInterface() { | ||
return "Unexpected `implements` on generic `".concat(Extractor_1.TYPE_TAG, "`. Generic types may not currently declare themselves as implementing interfaces. Grats requires that all types which implement an interface define a `__typename` field typed as a string literal matching the type's name. Since generic types are synthesized into multiple types with different names, Grats cannot ensure they have a correct `__typename` property and thus declare themselves as interface implementors."); | ||
} | ||
exports.genericTypeImplementsInterface = genericTypeImplementsInterface; | ||
function concreteTypeMissingTypename(implementor) { | ||
return "Missing `__typename` on `".concat(implementor, "`. The type `").concat(implementor, "` is used in a union or interface, so it must have a `__typename` field."); | ||
} | ||
exports.concreteTypeMissingTypename = concreteTypeMissingTypename; |
@@ -1,6 +0,5 @@ | ||
import { NameNode } from "graphql"; | ||
import { NameNode, DefinitionNode } from "graphql"; | ||
import { DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import * as ts from "typescript"; | ||
import { NameDefinition } from "./TypeContext"; | ||
import { GratsDefinitionNode } from "./GraphQLConstructor"; | ||
export declare const LIBRARY_IMPORT_NAME = "grats"; | ||
@@ -18,6 +17,7 @@ export declare const LIBRARY_NAME = "Grats"; | ||
export declare const ALL_TAGS: string[]; | ||
export declare const SPECIFIED_BY_TAG = "specifiedBy"; | ||
export type ExtractionSnapshot = { | ||
readonly definitions: GratsDefinitionNode[]; | ||
readonly unresolvedNames: Map<ts.Node, NameNode>; | ||
readonly nameDefinitions: Map<ts.Node, NameDefinition>; | ||
readonly definitions: DefinitionNode[]; | ||
readonly unresolvedNames: Map<ts.EntityName, NameNode>; | ||
readonly nameDefinitions: Map<ts.DeclarationStatement, NameDefinition>; | ||
readonly contextReferences: Array<ts.Node>; | ||
@@ -24,0 +24,0 @@ readonly typesWithTypename: Set<string>; |
@@ -30,3 +30,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.extract = exports.ALL_TAGS = exports.KILLS_PARENT_ON_EXCEPTION_TAG = exports.IMPLEMENTS_TAG_DEPRECATED = exports.INPUT_TAG = exports.UNION_TAG = exports.ENUM_TAG = exports.INTERFACE_TAG = exports.SCALAR_TAG = exports.FIELD_TAG = exports.TYPE_TAG = exports.LIBRARY_NAME = exports.LIBRARY_IMPORT_NAME = void 0; | ||
exports.extract = exports.SPECIFIED_BY_TAG = exports.ALL_TAGS = exports.KILLS_PARENT_ON_EXCEPTION_TAG = exports.IMPLEMENTS_TAG_DEPRECATED = exports.INPUT_TAG = exports.UNION_TAG = exports.ENUM_TAG = exports.INTERFACE_TAG = exports.SCALAR_TAG = exports.FIELD_TAG = exports.TYPE_TAG = exports.LIBRARY_NAME = exports.LIBRARY_IMPORT_NAME = void 0; | ||
var graphql_1 = require("graphql"); | ||
@@ -42,2 +42,4 @@ var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var Errors_1 = require("./Errors"); | ||
var comments_1 = require("./comments"); | ||
var helpers_1 = require("./utils/helpers"); | ||
exports.LIBRARY_IMPORT_NAME = "grats"; | ||
@@ -65,2 +67,3 @@ exports.LIBRARY_NAME = "Grats"; | ||
var DEPRECATED_TAG = "deprecated"; | ||
exports.SPECIFIED_BY_TAG = "specifiedBy"; | ||
var OPERATION_TYPES = new Set(["Query", "Mutation", "Subscription"]); | ||
@@ -106,4 +109,6 @@ /** | ||
var _this = this; | ||
var seenCommentPositions = new Set(); | ||
(0, JSDoc_1.traverseJSDocTags)(sourceFile, function (node, tag) { | ||
var e_1, _a; | ||
seenCommentPositions.add(tag.parent.pos); | ||
switch (tag.tagName.text) { | ||
@@ -132,22 +137,40 @@ case exports.TYPE_TAG: | ||
} | ||
else if (!(ts.isParameter(node) || | ||
ts.isMethodDeclaration(node) || | ||
ts.isGetAccessorDeclaration(node) || | ||
ts.isPropertyDeclaration(node) || | ||
ts.isMethodSignature(node) || | ||
ts.isPropertySignature(node))) { | ||
// Right now this happens via deep traversal | ||
// Note: Keep this in sync with `collectFields` | ||
_this.reportUnhandled(node, "field", E.fieldTagOnWrongNode()); | ||
else { | ||
// Non-function fields must be defined as a decent of something that | ||
// is annotated with @gqlType or @gqlInterface. | ||
// | ||
// The actual field will get extracted when we traverse the parent, but | ||
// we need to report an error if the parent is not a valid type or is not | ||
// annotated with @gqlType or @gqlInterface. Otherwise, the user may get | ||
// confused as to why the field is not showing up in the schema. | ||
var parent = getFieldParent(node); | ||
// If there was no valid parent, report an error. | ||
if (parent === null) { | ||
_this.reportUnhandled(node, "field", E.fieldTagOnWrongNode()); | ||
} | ||
else if (_this.hasTag(parent, exports.INPUT_TAG)) { | ||
// You don't need to add `@gqlField` to input types, but it's an | ||
// easy mistake to think you might need to. We report a helpful | ||
// error in this case. | ||
_this.report(tag, E.gqlFieldTagOnInputType()); | ||
} | ||
else if (!_this.hasTag(parent, exports.TYPE_TAG) && | ||
!_this.hasTag(parent, exports.INTERFACE_TAG)) { | ||
_this.report(tag, E.gqlFieldParentMissingTag()); | ||
} | ||
} | ||
break; | ||
case exports.KILLS_PARENT_ON_EXCEPTION_TAG: { | ||
var hasFieldTag = ts.getJSDocTags(node).some(function (t) { | ||
return t.tagName.text === exports.FIELD_TAG; | ||
}); | ||
if (!hasFieldTag) { | ||
if (!_this.hasTag(node, exports.FIELD_TAG)) { | ||
_this.report(tag.tagName, E.killsParentOnExceptionOnWrongNode()); | ||
} | ||
// TODO: Report invalid location as well | ||
break; | ||
} | ||
case exports.SPECIFIED_BY_TAG: { | ||
if (!_this.hasTag(node, exports.SCALAR_TAG)) { | ||
_this.report(tag.tagName, E.specifiedByOnWrongNode()); | ||
} | ||
break; | ||
} | ||
default: | ||
@@ -179,2 +202,4 @@ { | ||
}); | ||
var errors = (0, comments_1.detectInvalidComments)(sourceFile, seenCommentPositions); | ||
(0, helpers_1.extend)(this.errors, errors); | ||
if (this.errors.length > 0) { | ||
@@ -237,2 +262,5 @@ return (0, Result_1.err)(this.errors); | ||
} | ||
else if (ts.isInterfaceDeclaration(node)) { | ||
this.inputInterfaceDeclaration(node, tag); | ||
} | ||
else { | ||
@@ -291,3 +319,3 @@ this.report(tag, E.invalidInputTagUsage()); | ||
} | ||
this.recordTypeName(node.name, name, "UNION"); | ||
this.recordTypeName(node, name, "UNION"); | ||
this.definitions.push(this.gql.unionTypeDefinition(node, name, types, description)); | ||
@@ -312,6 +340,5 @@ }; | ||
} | ||
var returnType = this.collectReturnType(node.type); | ||
if (returnType == null) | ||
var type = this.collectType(node.type, { kind: "OUTPUT" }); | ||
if (type == null) | ||
return null; | ||
var type = returnType.type, isStream = returnType.isStream; | ||
var args = null; | ||
@@ -333,11 +360,8 @@ var argsParam = node.parameters[1]; | ||
var directives = [ | ||
this.gql.exportedDirective(funcName, { | ||
this.gql.fieldMetadataDirective(funcName, { | ||
tsModulePath: tsModulePath, | ||
exportedFunctionName: funcName.text, | ||
name: funcName.text, | ||
argCount: node.parameters.length, | ||
}), | ||
]; | ||
if (isStream) { | ||
directives.push(this.gql.asyncIterableDirective(node.type)); | ||
} | ||
var deprecated = this.collectDeprecated(node); | ||
@@ -361,5 +385,4 @@ if (deprecated != null) { | ||
} | ||
var nameNode = typeParam.type.typeName; | ||
var typeName = this.gql.name(nameNode, TypeContext_1.UNRESOLVED_REFERENCE_NAME); | ||
this.markUnresolvedType(nameNode, typeName); | ||
var typeName = this.gql.name(typeParam.type.typeName, TypeContext_1.UNRESOLVED_REFERENCE_NAME); | ||
this.markUnresolvedType(typeParam.type.typeName, typeName); | ||
return typeName; | ||
@@ -392,4 +415,6 @@ }; | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "SCALAR"); | ||
this.definitions.push(this.gql.scalarTypeDefinition(node, name, description)); | ||
this.recordTypeName(node, name, "SCALAR"); | ||
// TODO: Can a scalar be deprecated? | ||
var specifiedByDirective = this.collectSpecifiedBy(node); | ||
this.definitions.push(this.gql.scalarTypeDefinition(node, name, specifiedByDirective == null ? null : [specifiedByDirective], description)); | ||
}; | ||
@@ -401,3 +426,3 @@ Extractor.prototype.inputTypeAliasDeclaration = function (node, tag) { | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "INPUT_OBJECT"); | ||
this.recordTypeName(node, name, "INPUT_OBJECT"); | ||
var fields = this.collectInputFields(node); | ||
@@ -407,5 +432,36 @@ var deprecatedDirective = this.collectDeprecated(node); | ||
}; | ||
Extractor.prototype.collectInputFields = function (node) { | ||
Extractor.prototype.inputInterfaceDeclaration = function (node, tag) { | ||
var e_3, _a; | ||
var name = this.entityName(node, tag); | ||
if (name == null) | ||
return null; | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node, name, "INPUT_OBJECT"); | ||
var fields = []; | ||
try { | ||
for (var _b = __values(node.members), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var member = _c.value; | ||
if (!ts.isPropertySignature(member)) { | ||
this.reportUnhandled(member, "input field", E.inputTypeFieldNotProperty()); | ||
continue; | ||
} | ||
var field = this.collectInputField(member); | ||
if (field != null) | ||
fields.push(field); | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
this.interfaceDeclarations.push(node); | ||
var deprecatedDirective = this.collectDeprecated(node); | ||
this.definitions.push(this.gql.inputObjectTypeDefinition(node, name, fields, deprecatedDirective == null ? null : [deprecatedDirective], description)); | ||
}; | ||
Extractor.prototype.collectInputFields = function (node) { | ||
var e_4, _a; | ||
var fields = []; | ||
if (!ts.isTypeLiteralNode(node.type)) { | ||
@@ -426,3 +482,3 @@ return this.reportUnhandled(node, "input", E.inputTypeNotLiteral()); | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
finally { | ||
@@ -432,3 +488,3 @@ try { | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
@@ -444,3 +500,3 @@ return fields.length === 0 ? null : fields; | ||
} | ||
var inner = this.collectType(node.type); | ||
var inner = this.collectType(node.type, { kind: "INPUT" }); | ||
if (inner == null) | ||
@@ -464,3 +520,3 @@ return null; | ||
var interfaces = this.collectInterfaces(node); | ||
this.recordTypeName(node.name, name, "TYPE"); | ||
this.recordTypeName(node, name, "TYPE"); | ||
this.checkForTypenameProperty(node, name.value); | ||
@@ -484,3 +540,3 @@ this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description)); | ||
var interfaces = this.collectInterfaces(node); | ||
this.recordTypeName(node.name, name, "INTERFACE"); | ||
this.recordTypeName(node, name, "TYPE"); | ||
this.checkForTypenameProperty(node, name.value); | ||
@@ -510,3 +566,3 @@ this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description)); | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "TYPE"); | ||
this.recordTypeName(node, name, "TYPE"); | ||
this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description)); | ||
@@ -613,3 +669,5 @@ }; | ||
.map(function (type) { return type.expression; }) | ||
.filter(function (expression) { return ts.isIdentifier(expression); }) | ||
.filter(function (expression) { | ||
return ts.isIdentifier(expression); | ||
}) | ||
.map(function (expression) { | ||
@@ -641,3 +699,3 @@ var namedType = _this.gql.namedType(expression, TypeContext_1.UNRESOLVED_REFERENCE_NAME); | ||
var fields = this.collectFields(node); | ||
this.recordTypeName(node.name, name, "INTERFACE"); | ||
this.recordTypeName(node, name, "INTERFACE"); | ||
this.definitions.push(this.gql.interfaceTypeDefinition(node, name, fields, interfaces, description)); | ||
@@ -649,3 +707,3 @@ }; | ||
ts.forEachChild(node, function (node) { | ||
var e_4, _a; | ||
var e_5, _a; | ||
if (ts.isConstructorDeclaration(node)) { | ||
@@ -663,3 +721,3 @@ try { | ||
} | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
catch (e_5_1) { e_5 = { error: e_5_1 }; } | ||
finally { | ||
@@ -669,3 +727,3 @@ try { | ||
} | ||
finally { if (e_4) throw e_4.error; } | ||
finally { if (e_5) throw e_5.error; } | ||
} | ||
@@ -727,9 +785,10 @@ } | ||
} | ||
var directives = []; | ||
if (id.text !== name.value) { | ||
directives = [ | ||
this.gql.propertyNameDirective(node.name, { name: id.text }), | ||
]; | ||
} | ||
var type = this.collectType(node.type); | ||
var directives = [ | ||
this.gql.fieldMetadataDirective(node.name, { | ||
name: id.text == name.value ? null : id.text, | ||
tsModulePath: null, | ||
argCount: null, | ||
}), | ||
]; | ||
var type = this.collectType(node.type, { kind: "OUTPUT" }); | ||
if (type == null) | ||
@@ -749,3 +808,3 @@ return null; | ||
Extractor.prototype.collectArgs = function (argsParam) { | ||
var e_5, _a; | ||
var e_6, _a; | ||
var args = []; | ||
@@ -775,3 +834,3 @@ var argsType = argsParam.type; | ||
} | ||
catch (e_5_1) { e_5 = { error: e_5_1 }; } | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
finally { | ||
@@ -781,3 +840,3 @@ try { | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
@@ -787,3 +846,3 @@ return args; | ||
Extractor.prototype.collectArgDefaults = function (node) { | ||
var e_6, _a; | ||
var e_7, _a; | ||
var defaults = new Map(); | ||
@@ -800,3 +859,3 @@ try { | ||
} | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
@@ -806,3 +865,3 @@ try { | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
@@ -838,3 +897,3 @@ return defaults; | ||
Extractor.prototype.collectArrayLiteral = function (node) { | ||
var e_7, _a; | ||
var e_8, _a; | ||
var values = []; | ||
@@ -854,3 +913,3 @@ var errors = false; | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
@@ -860,3 +919,3 @@ try { | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
@@ -869,3 +928,3 @@ if (errors) { | ||
Extractor.prototype.collectObjectLiteral = function (node) { | ||
var e_8, _a; | ||
var e_9, _a; | ||
var fields = []; | ||
@@ -885,3 +944,3 @@ var errors = false; | ||
} | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
finally { | ||
@@ -891,3 +950,3 @@ try { | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
finally { if (e_9) throw e_9.error; } | ||
} | ||
@@ -930,3 +989,3 @@ if (errors) { | ||
} | ||
var type = this.collectType(node.type); | ||
var type = this.collectType(node.type, { kind: "INPUT" }); | ||
if (type == null) | ||
@@ -940,3 +999,10 @@ return null; | ||
} | ||
if (node.questionToken) { | ||
var defaultValue = null; | ||
if (defaults != null) { | ||
var def = defaults.get(node.name.text); | ||
if (def != null) { | ||
defaultValue = this.collectConstValue(def); | ||
} | ||
} | ||
if (node.questionToken && defaultValue == null) { | ||
// Question mark means we can handle the argument being undefined in the | ||
@@ -946,2 +1012,4 @@ // object literal, but if we are going to type the GraphQL arg as | ||
// | ||
// ... unless there is a default value. In that case, the default will be | ||
// used argument is omitted or references an undefined variable. | ||
// TODO: This will catch { a?: string } but not { a?: string | undefined }. | ||
@@ -954,9 +1022,2 @@ if (type.kind === graphql_1.Kind.NON_NULL_TYPE) { | ||
var description = this.collectDescription(node); | ||
var defaultValue = null; | ||
if (defaults != null) { | ||
var def = defaults.get(node.name.text); | ||
if (def != null) { | ||
defaultValue = this.collectConstValue(def); | ||
} | ||
} | ||
var deprecatedDirective = this.collectDeprecated(node); | ||
@@ -972,3 +1033,3 @@ return this.gql.inputValueDefinition(node, this.gql.name(node.name, node.name.text), type, deprecatedDirective == null ? null : [deprecatedDirective], defaultValue, description); | ||
var values = this.collectEnumValues(node); | ||
this.recordTypeName(node.name, name, "ENUM"); | ||
this.recordTypeName(node, name, "ENUM"); | ||
this.definitions.push(this.gql.enumTypeDefinition(node, name, values, description)); | ||
@@ -985,7 +1046,7 @@ }; | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "ENUM"); | ||
this.recordTypeName(node, name, "ENUM"); | ||
this.definitions.push(this.gql.enumTypeDefinition(node, name, values, description)); | ||
}; | ||
Extractor.prototype.enumTypeAliasVariants = function (node) { | ||
var e_9, _a; | ||
var e_10, _a; | ||
// Semantically we only support deriving enums from type aliases that | ||
@@ -1020,3 +1081,3 @@ // are unions of string literals. However, in the edge case of a union | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
catch (e_10_1) { e_10 = { error: e_10_1 }; } | ||
finally { | ||
@@ -1026,3 +1087,3 @@ try { | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
finally { if (e_10) throw e_10.error; } | ||
} | ||
@@ -1032,3 +1093,3 @@ return values; | ||
Extractor.prototype.collectEnumValues = function (node) { | ||
var e_10, _a; | ||
var e_11, _a; | ||
var values = []; | ||
@@ -1048,3 +1109,3 @@ try { | ||
} | ||
catch (e_10_1) { e_10 = { error: e_10_1 }; } | ||
catch (e_11_1) { e_11 = { error: e_11_1 }; } | ||
finally { | ||
@@ -1054,3 +1115,3 @@ try { | ||
} | ||
finally { if (e_10) throw e_10.error; } | ||
finally { if (e_11) throw e_11.error; } | ||
} | ||
@@ -1126,6 +1187,5 @@ return values; | ||
} | ||
var returnType = this.collectReturnType(node.type); | ||
if (returnType == null) | ||
var type = this.collectType(node.type, { kind: "OUTPUT" }); | ||
if (type == null) | ||
return null; | ||
var type = returnType.type, isStream = returnType.isStream; | ||
// We already reported an error | ||
@@ -1147,11 +1207,9 @@ if (type == null) | ||
return null; | ||
var directives = []; | ||
if (id.text !== name.value) { | ||
directives = [ | ||
this.gql.propertyNameDirective(node.name, { name: id.text }), | ||
]; | ||
} | ||
if (isStream) { | ||
directives.push(this.gql.asyncIterableDirective(node.type)); | ||
} | ||
var directives = [ | ||
this.gql.fieldMetadataDirective(node.name, { | ||
name: id.text === name.value ? null : id.text, | ||
tsModulePath: null, | ||
argCount: isCallable(node) ? node.parameters.length : null, | ||
}), | ||
]; | ||
var deprecated = this.collectDeprecated(node); | ||
@@ -1167,47 +1225,2 @@ if (deprecated != null) { | ||
}; | ||
Extractor.prototype.collectReturnType = function (node) { | ||
if (ts.isTypeReferenceNode(node)) { | ||
var identifier = this.expectNameIdentifier(node.typeName); | ||
if (identifier == null) | ||
return null; | ||
if (identifier.text == "AsyncIterable") { | ||
if (node.typeArguments == null || node.typeArguments.length === 0) { | ||
// TODO: Better error? | ||
return this.report(node, E.wrapperMissingTypeArg()); | ||
} | ||
var t_1 = this.collectType(node.typeArguments[0]); | ||
if (t_1 == null) | ||
return null; | ||
return { type: t_1, isStream: true }; | ||
} | ||
} | ||
var inner = this.maybeUnwrapPromise(node); | ||
if (inner == null) | ||
return null; | ||
var t = this.collectType(inner); | ||
if (t == null) | ||
return null; | ||
return { type: t, isStream: false }; | ||
}; | ||
Extractor.prototype.collectPropertyType = function (node) { | ||
// TODO: Handle function types here. | ||
var inner = this.maybeUnwrapPromise(node); | ||
if (inner == null) | ||
return null; | ||
return this.collectType(inner); | ||
}; | ||
Extractor.prototype.maybeUnwrapPromise = function (node) { | ||
if (ts.isTypeReferenceNode(node)) { | ||
var identifier = this.expectNameIdentifier(node.typeName); | ||
if (identifier == null) | ||
return null; | ||
if (identifier.text === "Promise") { | ||
if (node.typeArguments == null || node.typeArguments.length === 0) { | ||
return this.report(node, E.wrapperMissingTypeArg()); | ||
} | ||
return node.typeArguments[0]; | ||
} | ||
} | ||
return node; | ||
}; | ||
Extractor.prototype.collectDescription = function (node) { | ||
@@ -1240,2 +1253,14 @@ var docs = | ||
}; | ||
Extractor.prototype.collectSpecifiedBy = function (node) { | ||
var tag = this.findTag(node, exports.SPECIFIED_BY_TAG); | ||
if (tag == null) | ||
return null; | ||
var urlComment = tag.comment && ts.getTextOfJSDocComment(tag.comment); | ||
if (urlComment == null) { | ||
return this.report(tag, "Expected @specifiedBy tag to be followed by a URL."); | ||
} | ||
// FIXME: Use the _value_'s location not the tag's | ||
var reason = this.gql.constArgument(tag, this.gql.name(tag, "url"), this.gql.string(tag, urlComment)); | ||
return this.gql.constDirective(tag.tagName, this.gql.name(node, exports.SPECIFIED_BY_TAG), [reason]); | ||
}; | ||
Extractor.prototype.property = function (node) { | ||
@@ -1252,3 +1277,3 @@ var tag = this.findTag(node, exports.FIELD_TAG); | ||
} | ||
var inner = this.collectPropertyType(node.type); | ||
var inner = this.collectType(node.type, { kind: "OUTPUT" }); | ||
// We already reported an error | ||
@@ -1267,7 +1292,7 @@ if (inner == null) | ||
} | ||
if (id.text !== name.value) { | ||
directives = [ | ||
this.gql.propertyNameDirective(node.name, { name: id.text }), | ||
]; | ||
} | ||
directives.push(this.gql.fieldMetadataDirective(node.name, { | ||
name: id.text === name.value ? null : id.text, | ||
tsModulePath: null, | ||
argCount: null, | ||
})); | ||
var killsParentOnExceptionDirective = this.killsParentOnExceptionDirective(node); | ||
@@ -1281,6 +1306,6 @@ if (killsParentOnExceptionDirective != null) { | ||
// For input nodes and field may only be optional if `null` is a valid value. | ||
Extractor.prototype.collectType = function (node) { | ||
Extractor.prototype.collectType = function (node, ctx) { | ||
var _this = this; | ||
if (ts.isTypeReferenceNode(node)) { | ||
var type = this.typeReference(node); | ||
var type = this.typeReference(node, ctx); | ||
if (type == null) | ||
@@ -1291,3 +1316,3 @@ return null; | ||
else if (ts.isArrayTypeNode(node)) { | ||
var element = this.collectType(node.elementType); | ||
var element = this.collectType(node.elementType, ctx); | ||
if (element == null) | ||
@@ -1302,3 +1327,3 @@ return null; | ||
} | ||
var type = this.collectType(types[0]); | ||
var type = this.collectType(types[0], ctx); | ||
if (type == null) | ||
@@ -1321,3 +1346,3 @@ return null; | ||
else if (ts.isParenthesizedTypeNode(node)) { | ||
return this.collectType(node.type); | ||
return this.collectType(node.type, ctx); | ||
} | ||
@@ -1340,3 +1365,3 @@ else if (node.kind === ts.SyntaxKind.StringKeyword) { | ||
}; | ||
Extractor.prototype.typeReference = function (node) { | ||
Extractor.prototype.typeReference = function (node, ctx) { | ||
var identifier = this.expectNameIdentifier(node.typeName); | ||
@@ -1346,14 +1371,37 @@ if (identifier == null) | ||
var typeName = identifier.text; | ||
// Some types are not valid as input types. Validate that here: | ||
if (ctx.kind === "INPUT") { | ||
switch (typeName) { | ||
case "AsyncIterable": | ||
return this.report(node, "`AsyncIterable` is not a valid as an input type."); | ||
case "Promise": | ||
return this.report(node, "`Promise` is not a valid as an input type."); | ||
} | ||
} | ||
switch (typeName) { | ||
case "Array": | ||
case "Iterator": | ||
case "ReadonlyArray": { | ||
case "ReadonlyArray": | ||
case "AsyncIterable": { | ||
if (node.typeArguments == null) { | ||
return this.report(node, E.pluralTypeMissingParameter()); | ||
} | ||
var element = this.collectType(node.typeArguments[0]); | ||
var element = this.collectType(node.typeArguments[0], ctx); | ||
if (element == null) | ||
return null; | ||
return this.gql.nonNullType(node, this.gql.listType(node, element)); | ||
var listType = this.gql.listType(node, element); | ||
if (typeName === "AsyncIterable") { | ||
listType.isAsyncIterable = true; | ||
} | ||
return this.gql.nonNullType(node, listType); | ||
} | ||
case "Promise": { | ||
if (node.typeArguments == null) { | ||
return this.report(node, E.wrapperMissingTypeArg()); | ||
} | ||
var element = this.collectType(node.typeArguments[0], ctx); | ||
if (element == null) | ||
return null; | ||
return element; | ||
} | ||
default: { | ||
@@ -1403,2 +1451,8 @@ // We may not have encountered the definition of this type yet. So, we | ||
}; | ||
Extractor.prototype.hasTag = function (node, tagName) { | ||
var tags = ts | ||
.getJSDocTags(node) | ||
.filter(function (tag) { return tag.tagName.escapedText === tagName; }); | ||
return tags.length > 0; | ||
}; | ||
// It is a GraphQL best practice to model all fields as nullable. This allows | ||
@@ -1432,1 +1486,35 @@ // the server to handle field level executions by simply returning null for | ||
} | ||
function isCallable(node) { | ||
return ts.isMethodDeclaration(node) || ts.isMethodSignature(node); | ||
} | ||
// Given a node annotated as @gqlField, finds the parent node that is | ||
// expected to be annotated with @gqlType or @gqlInterface. | ||
// | ||
// Note that this is basically a reverse encoding of the traversal | ||
// we do from the @gqlType or @gqlInterface node to the fields. | ||
// This code needs to stay in sync with the traversal code, but should do so | ||
// safely since, if it doesn't match we'd end up with test errors. | ||
function getFieldParent(node) { | ||
if (ts.isMethodDeclaration(node) || | ||
ts.isGetAccessorDeclaration(node) || | ||
ts.isPropertyDeclaration(node)) { | ||
return node.parent; | ||
} | ||
else if (ts.isParameter(node)) { | ||
if (ts.isConstructorDeclaration(node.parent)) { | ||
return node.parent.parent; | ||
} | ||
return null; | ||
} | ||
else if (ts.isPropertySignature(node) || ts.isMethodSignature(node)) { | ||
if (ts.isTypeLiteralNode(node.parent) && | ||
ts.isTypeAliasDeclaration(node.parent.parent)) { | ||
return node.parent.parent; | ||
} | ||
else if (ts.isInterfaceDeclaration(node.parent)) { | ||
return node.parent; | ||
} | ||
return null; | ||
} | ||
return null; | ||
} |
@@ -1,15 +0,10 @@ | ||
import { ListTypeNode, NamedTypeNode, Location as GraphQLLocation, NameNode, Token, TypeNode, NonNullTypeNode, StringValueNode, ConstValueNode, ConstDirectiveNode, ConstArgumentNode, UnionTypeDefinitionNode, FieldDefinitionNode, InputValueDefinitionNode, FloatValueNode, IntValueNode, NullValueNode, BooleanValueNode, ConstListValueNode, ConstObjectValueNode, ConstObjectFieldNode, ObjectTypeDefinitionNode, EnumValueDefinitionNode, ScalarTypeDefinitionNode, InputObjectTypeDefinitionNode, EnumTypeDefinitionNode, InterfaceTypeDefinitionNode, DefinitionNode, Location, ASTNode } from "graphql"; | ||
import { ListTypeNode, NamedTypeNode, Location as GraphQLLocation, NameNode, TypeNode, NonNullTypeNode, StringValueNode, ConstValueNode, ConstDirectiveNode, ConstArgumentNode, UnionTypeDefinitionNode, FieldDefinitionNode, InputValueDefinitionNode, FloatValueNode, IntValueNode, NullValueNode, BooleanValueNode, ConstListValueNode, ConstObjectValueNode, ConstObjectFieldNode, ObjectTypeDefinitionNode, EnumValueDefinitionNode, ScalarTypeDefinitionNode, InputObjectTypeDefinitionNode, EnumTypeDefinitionNode, InterfaceTypeDefinitionNode, ASTNode, ObjectTypeExtensionNode } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { ExportedMetadata, PropertyNameMetadata } from "./metadataDirectives"; | ||
export type GratsDefinitionNode = DefinitionNode | AbstractFieldDefinitionNode; | ||
export type AbstractFieldDefinitionNode = { | ||
readonly kind: "AbstractFieldDefinition"; | ||
readonly loc: Location; | ||
readonly onType: NameNode; | ||
readonly field: FieldDefinitionNode; | ||
}; | ||
import { TsLocatableNode } from "./utils/DiagnosticError"; | ||
export declare class GraphQLConstructor { | ||
exportedDirective(node: ts.Node, exported: ExportedMetadata): ConstDirectiveNode; | ||
propertyNameDirective(node: ts.Node, propertyName: PropertyNameMetadata): ConstDirectiveNode; | ||
asyncIterableDirective(node: ts.Node): ConstDirectiveNode; | ||
fieldMetadataDirective(node: ts.Node, metadata: { | ||
tsModulePath: string | null; | ||
name: string | null; | ||
argCount: number | null; | ||
}): ConstDirectiveNode; | ||
killsParentOnExceptionDirective(node: ts.Node): ConstDirectiveNode; | ||
@@ -20,3 +15,3 @@ unionTypeDefinition(node: ts.Node, name: NameNode, types: NamedTypeNode[], description: StringValueNode | null): UnionTypeDefinitionNode; | ||
enumTypeDefinition(node: ts.Node, name: NameNode, values: readonly EnumValueDefinitionNode[], description: StringValueNode | null): EnumTypeDefinitionNode; | ||
abstractFieldDefinition(node: ts.Node, onType: NameNode, field: FieldDefinitionNode): AbstractFieldDefinitionNode; | ||
abstractFieldDefinition(node: ts.Node, onType: NameNode, field: FieldDefinitionNode): ObjectTypeExtensionNode; | ||
fieldDefinition(node: ts.Node, name: NameNode, type: TypeNode, args: readonly InputValueDefinitionNode[] | null, directives: readonly ConstDirectiveNode[], description: StringValueNode | null): FieldDefinitionNode; | ||
@@ -26,3 +21,3 @@ constObjectField(node: ts.Node, name: NameNode, value: ConstValueNode): ConstObjectFieldNode; | ||
enumValueDefinition(node: ts.Node, name: NameNode, directives: readonly ConstDirectiveNode[] | undefined, description: StringValueNode | null): EnumValueDefinitionNode; | ||
scalarTypeDefinition(node: ts.Node, name: NameNode, description: StringValueNode | null): ScalarTypeDefinitionNode; | ||
scalarTypeDefinition(node: ts.Node, name: NameNode, directives: readonly ConstDirectiveNode[] | null, description: StringValueNode | null): ScalarTypeDefinitionNode; | ||
inputObjectTypeDefinition(node: ts.Node, name: NameNode, fields: InputValueDefinitionNode[] | null, directives: readonly ConstDirectiveNode[] | null, description: StringValueNode | null): InputObjectTypeDefinitionNode; | ||
@@ -45,4 +40,3 @@ name(node: ts.Node, value: string): NameNode; | ||
_optionalList<T>(input: readonly T[] | null): readonly T[] | undefined; | ||
_loc(node: ts.Node): GraphQLLocation; | ||
_dummyToken(sourceFile: ts.SourceFile, pos: number): Token; | ||
} | ||
export declare function loc(node: TsLocatableNode): GraphQLLocation; |
@@ -14,20 +14,24 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.GraphQLConstructor = void 0; | ||
exports.loc = exports.GraphQLConstructor = void 0; | ||
var graphql_1 = require("graphql"); | ||
var metadataDirectives_1 = require("./metadataDirectives"); | ||
var helpers_1 = require("./utils/helpers"); | ||
var GraphQLConstructor = /** @class */ (function () { | ||
function GraphQLConstructor() { | ||
} | ||
/* Metadata Directives */ | ||
GraphQLConstructor.prototype.exportedDirective = function (node, exported) { | ||
return (0, metadataDirectives_1.makeExportedDirective)(this._loc(node), exported); | ||
GraphQLConstructor.prototype.fieldMetadataDirective = function (node, metadata) { | ||
var args = []; | ||
if (metadata.tsModulePath != null) { | ||
args.push(this.constArgument(node, this.name(node, metadataDirectives_1.TS_MODULE_PATH_ARG), this.string(node, metadata.tsModulePath))); | ||
} | ||
if (metadata.name != null) { | ||
args.push(this.constArgument(node, this.name(node, metadataDirectives_1.FIELD_NAME_ARG), this.string(node, metadata.name))); | ||
} | ||
if (metadata.argCount != null) { | ||
args.push(this.constArgument(node, this.name(node, metadataDirectives_1.ARG_COUNT), this.int(node, metadata.argCount.toString()))); | ||
} | ||
return this.constDirective(node, this.name(node, metadataDirectives_1.FIELD_METADATA_DIRECTIVE), args); | ||
}; | ||
GraphQLConstructor.prototype.propertyNameDirective = function (node, propertyName) { | ||
return (0, metadataDirectives_1.makePropertyNameDirective)(this._loc(node), propertyName); | ||
}; | ||
GraphQLConstructor.prototype.asyncIterableDirective = function (node) { | ||
return (0, metadataDirectives_1.makeAsyncIterableDirective)(this._loc(node)); | ||
}; | ||
GraphQLConstructor.prototype.killsParentOnExceptionDirective = function (node) { | ||
return (0, metadataDirectives_1.makeKillsParentOnExceptionDirective)(this._loc(node)); | ||
return (0, metadataDirectives_1.makeKillsParentOnExceptionDirective)(loc(node)); | ||
}; | ||
@@ -38,3 +42,3 @@ /* Top Level Types */ | ||
kind: graphql_1.Kind.UNION_TYPE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -48,3 +52,3 @@ name: name, | ||
kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -60,3 +64,3 @@ directives: undefined, | ||
kind: graphql_1.Kind.INTERFACE_TYPE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -72,3 +76,3 @@ directives: undefined, | ||
kind: graphql_1.Kind.ENUM_TYPE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -82,6 +86,7 @@ name: name, | ||
return { | ||
kind: "AbstractFieldDefinition", | ||
loc: this._loc(node), | ||
onType: onType, | ||
field: field, | ||
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION, | ||
loc: loc(node), | ||
name: onType, | ||
fields: [field], | ||
mayBeInterface: true, | ||
}; | ||
@@ -93,3 +98,3 @@ }; | ||
kind: graphql_1.Kind.FIELD_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -103,3 +108,3 @@ name: name, | ||
GraphQLConstructor.prototype.constObjectField = function (node, name, value) { | ||
return { kind: graphql_1.Kind.OBJECT_FIELD, loc: this._loc(node), name: name, value: value }; | ||
return { kind: graphql_1.Kind.OBJECT_FIELD, loc: loc(node), name: name, value: value }; | ||
}; | ||
@@ -109,3 +114,3 @@ GraphQLConstructor.prototype.inputValueDefinition = function (node, name, type, directives, defaultValue, description) { | ||
kind: graphql_1.Kind.INPUT_VALUE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -121,3 +126,3 @@ name: name, | ||
kind: graphql_1.Kind.ENUM_VALUE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -128,9 +133,9 @@ name: name, | ||
}; | ||
GraphQLConstructor.prototype.scalarTypeDefinition = function (node, name, description) { | ||
GraphQLConstructor.prototype.scalarTypeDefinition = function (node, name, directives, description) { | ||
return { | ||
kind: graphql_1.Kind.SCALAR_TYPE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
name: name, | ||
directives: undefined, | ||
directives: this._optionalList(directives), | ||
}; | ||
@@ -141,3 +146,3 @@ }; | ||
kind: graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
@@ -151,3 +156,8 @@ name: name, | ||
GraphQLConstructor.prototype.name = function (node, value) { | ||
return { kind: graphql_1.Kind.NAME, loc: this._loc(node), value: value }; | ||
return { | ||
kind: graphql_1.Kind.NAME, | ||
loc: loc(node), | ||
value: value, | ||
tsIdentifier: (0, helpers_1.uniqueId)(), | ||
}; | ||
}; | ||
@@ -157,3 +167,3 @@ GraphQLConstructor.prototype.namedType = function (node, value) { | ||
kind: graphql_1.Kind.NAMED_TYPE, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
name: this.name(node, value), | ||
@@ -163,3 +173,3 @@ }; | ||
GraphQLConstructor.prototype.object = function (node, fields) { | ||
return { kind: graphql_1.Kind.OBJECT, loc: this._loc(node), fields: fields }; | ||
return { kind: graphql_1.Kind.OBJECT, loc: loc(node), fields: fields }; | ||
}; | ||
@@ -171,3 +181,3 @@ /* Helpers */ | ||
} | ||
return { kind: graphql_1.Kind.NON_NULL_TYPE, loc: this._loc(node), type: type }; | ||
return { kind: graphql_1.Kind.NON_NULL_TYPE, loc: loc(node), type: type }; | ||
}; | ||
@@ -182,12 +192,12 @@ GraphQLConstructor.prototype.nullableType = function (type) { | ||
GraphQLConstructor.prototype.listType = function (node, type) { | ||
return { kind: graphql_1.Kind.LIST_TYPE, loc: this._loc(node), type: type }; | ||
return { kind: graphql_1.Kind.LIST_TYPE, loc: loc(node), type: type }; | ||
}; | ||
GraphQLConstructor.prototype.list = function (node, values) { | ||
return { kind: graphql_1.Kind.LIST, loc: this._loc(node), values: values }; | ||
return { kind: graphql_1.Kind.LIST, loc: loc(node), values: values }; | ||
}; | ||
GraphQLConstructor.prototype.withLocation = function (node, value) { | ||
return __assign(__assign({}, value), { loc: this._loc(node) }); | ||
return __assign(__assign({}, value), { loc: loc(node) }); | ||
}; | ||
GraphQLConstructor.prototype.constArgument = function (node, name, value) { | ||
return { kind: graphql_1.Kind.ARGUMENT, loc: this._loc(node), name: name, value: value }; | ||
return { kind: graphql_1.Kind.ARGUMENT, loc: loc(node), name: name, value: value }; | ||
}; | ||
@@ -197,3 +207,3 @@ GraphQLConstructor.prototype.constDirective = function (node, name, args) { | ||
kind: graphql_1.Kind.DIRECTIVE, | ||
loc: this._loc(node), | ||
loc: loc(node), | ||
name: name, | ||
@@ -204,15 +214,15 @@ arguments: this._optionalList(args), | ||
GraphQLConstructor.prototype.string = function (node, value, block) { | ||
return { kind: graphql_1.Kind.STRING, loc: this._loc(node), value: value, block: block }; | ||
return { kind: graphql_1.Kind.STRING, loc: loc(node), value: value, block: block }; | ||
}; | ||
GraphQLConstructor.prototype.float = function (node, value) { | ||
return { kind: graphql_1.Kind.FLOAT, loc: this._loc(node), value: value }; | ||
return { kind: graphql_1.Kind.FLOAT, loc: loc(node), value: value }; | ||
}; | ||
GraphQLConstructor.prototype.int = function (node, value) { | ||
return { kind: graphql_1.Kind.INT, loc: this._loc(node), value: value }; | ||
return { kind: graphql_1.Kind.INT, loc: loc(node), value: value }; | ||
}; | ||
GraphQLConstructor.prototype.null = function (node) { | ||
return { kind: graphql_1.Kind.NULL, loc: this._loc(node) }; | ||
return { kind: graphql_1.Kind.NULL, loc: loc(node) }; | ||
}; | ||
GraphQLConstructor.prototype.boolean = function (node, value) { | ||
return { kind: graphql_1.Kind.BOOLEAN, loc: this._loc(node), value: value }; | ||
return { kind: graphql_1.Kind.BOOLEAN, loc: loc(node), value: value }; | ||
}; | ||
@@ -225,18 +235,19 @@ GraphQLConstructor.prototype._optionalList = function (input) { | ||
}; | ||
// TODO: This is potentially quite expensive, and we only need it if we report | ||
// an error at one of these locations. We could consider some trick to return a | ||
// proxy object that would lazily compute the line/column info. | ||
GraphQLConstructor.prototype._loc = function (node) { | ||
var sourceFile = node.getSourceFile(); | ||
var source = new graphql_1.Source(sourceFile.text, sourceFile.fileName); | ||
var startToken = this._dummyToken(sourceFile, node.getStart()); | ||
var endToken = this._dummyToken(sourceFile, node.getEnd()); | ||
return new graphql_1.Location(startToken, endToken, source); | ||
}; | ||
GraphQLConstructor.prototype._dummyToken = function (sourceFile, pos) { | ||
var _a = sourceFile.getLineAndCharacterOfPosition(pos), line = _a.line, character = _a.character; | ||
return new graphql_1.Token(graphql_1.TokenKind.SOF, pos, pos, line, character, undefined); | ||
}; | ||
return GraphQLConstructor; | ||
}()); | ||
exports.GraphQLConstructor = GraphQLConstructor; | ||
// TODO: This is potentially quite expensive, and we only need it if we report | ||
// an error at one of these locations. We could consider some trick to return a | ||
// proxy object that would lazily compute the line/column info. | ||
function loc(node) { | ||
var sourceFile = node.getSourceFile(); | ||
var source = new graphql_1.Source(sourceFile.text, sourceFile.fileName); | ||
var startToken = _dummyToken(sourceFile, node.getStart()); | ||
var endToken = _dummyToken(sourceFile, node.getEnd()); | ||
return new graphql_1.Location(startToken, endToken, source); | ||
} | ||
exports.loc = loc; | ||
function _dummyToken(sourceFile, pos) { | ||
var _a = sourceFile.getLineAndCharacterOfPosition(pos), line = _a.line, character = _a.character; | ||
return new graphql_1.Token(graphql_1.TokenKind.SOF, pos, pos, line, character, undefined); | ||
} |
@@ -6,2 +6,3 @@ import * as ts from "typescript"; | ||
nullableByDefault: boolean; | ||
strictSemanticNullability: boolean; | ||
reportTypeScriptTypeErrors: boolean; | ||
@@ -8,0 +9,0 @@ schemaHeader: string | null; |
@@ -13,2 +13,13 @@ "use strict"; | ||
}; | ||
var __values = (this && this.__values) || function(o) { | ||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
if (m) return m.call(o); | ||
if (o && typeof o.length === "number") return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -18,6 +29,32 @@ exports.validateGratsOptions = void 0; | ||
var DEFAULT_TYPESCRIPT_HEADER = "/**\n * Executable schema generated by Grats (https://grats.capt.dev)\n * Do not manually edit. Regenerate by running `npx grats`.\n */"; | ||
var VALID_CONFIG_KEYS = new Set([ | ||
"graphqlSchema", | ||
"tsSchema", | ||
"nullableByDefault", | ||
"strictSemanticNullability", | ||
"reportTypeScriptTypeErrors", | ||
"schemaHeader", | ||
"tsSchemaHeader", | ||
]); | ||
// TODO: Make this return diagnostics | ||
function validateGratsOptions(options) { | ||
var _a, _b; | ||
var gratsOptions = __assign({}, ((_b = (_a = options.raw) === null || _a === void 0 ? void 0 : _a.grats) !== null && _b !== void 0 ? _b : {})); | ||
var e_1, _a; | ||
var _b, _c; | ||
var gratsOptions = __assign({}, ((_c = (_b = options.raw) === null || _b === void 0 ? void 0 : _b.grats) !== null && _c !== void 0 ? _c : {})); | ||
try { | ||
for (var _d = __values(Object.keys(gratsOptions)), _e = _d.next(); !_e.done; _e = _d.next()) { | ||
var key = _e.value; | ||
if (!VALID_CONFIG_KEYS.has(key)) { | ||
// TODO: Suggest similar? | ||
throw new Error("Grats: Unknown Grats config option `".concat(key, "`")); | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_e && !_e.done && (_a = _d.return)) _a.call(_d); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
if (gratsOptions.nullableByDefault === undefined) { | ||
@@ -29,2 +66,12 @@ gratsOptions.nullableByDefault = true; | ||
} | ||
if (gratsOptions.strictSemanticNullability === undefined) { | ||
gratsOptions.strictSemanticNullability = false; | ||
} | ||
else if (typeof gratsOptions.strictSemanticNullability !== "boolean") { | ||
throw new Error("Grats: The Grats config option `strictSemanticNullability` must be a boolean if provided."); | ||
} | ||
else if (gratsOptions.strictSemanticNullability && | ||
!gratsOptions.nullableByDefault) { | ||
throw new Error("Grats: The Grats config option `strictSemanticNullability` cannot be true if `nullableByDefault` is false."); | ||
} | ||
if (gratsOptions.reportTypeScriptTypeErrors === undefined) { | ||
@@ -53,5 +100,11 @@ gratsOptions.reportTypeScriptTypeErrors = false; | ||
} | ||
else if (Array.isArray(gratsOptions.schemaHeader)) { | ||
if (!gratsOptions.schemaHeader.every(function (segment) { return typeof segment === "string"; })) { | ||
throw new Error("Grats: If the Grats config option `schemaHeader` is an array, it must be an array of strings."); | ||
} | ||
gratsOptions.schemaHeader = gratsOptions.schemaHeader.join(""); | ||
} | ||
else if (typeof gratsOptions.schemaHeader !== "string" && | ||
gratsOptions.schemaHeader !== null) { | ||
throw new Error("Grats: The Grats config option `schemaHeader` must be a string or `null` if provided."); | ||
throw new Error("Grats: The Grats config option `schemaHeader` must be a string, an array of strings, or `null` if provided."); | ||
} | ||
@@ -61,5 +114,11 @@ if (gratsOptions.tsSchemaHeader === undefined) { | ||
} | ||
else if (Array.isArray(gratsOptions.tsSchemaHeader)) { | ||
if (!gratsOptions.tsSchemaHeader.every(function (segment) { return typeof segment === "string"; })) { | ||
throw new Error("Grats: If the Grats config option `tsSchemaHeader` is an array, it must be an array of strings."); | ||
} | ||
gratsOptions.tsSchemaHeader = gratsOptions.tsSchemaHeader.join(""); | ||
} | ||
else if (typeof gratsOptions.tsSchemaHeader !== "string" && | ||
gratsOptions.tsSchemaHeader !== null) { | ||
throw new Error("Grats: The Grats config option `tsSchemaHeader` must be a string or `null` if provided."); | ||
throw new Error("Grats: The Grats config option `tsSchemaHeader` must be a string, an array of strings, or `null` if provided."); | ||
} | ||
@@ -66,0 +125,0 @@ return __assign(__assign({}, options), { raw: __assign(__assign({}, options.raw), { grats: gratsOptions }) }); |
@@ -1,5 +0,5 @@ | ||
import { ParsedCommandLineGrats } from "./lib"; | ||
import { ParsedCommandLineGrats } from "./gratsConfig"; | ||
import { ReportableDiagnostics } from "./utils/DiagnosticError"; | ||
import { Result } from "./utils/Result"; | ||
export { printSDLWithoutDirectives } from "./printSchema"; | ||
export { printSDLWithoutMetadata } from "./printSchema"; | ||
export * from "./Types"; | ||
@@ -6,0 +6,0 @@ export * from "./lib"; |
@@ -17,9 +17,9 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getParsedTsConfig = exports.codegen = exports.extract = exports.printSDLWithoutDirectives = void 0; | ||
exports.getParsedTsConfig = exports.codegen = exports.extract = exports.printSDLWithoutMetadata = void 0; | ||
var ts = require("typescript"); | ||
var lib_1 = require("./lib"); | ||
var gratsConfig_1 = require("./gratsConfig"); | ||
var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var Result_1 = require("./utils/Result"); | ||
var printSchema_1 = require("./printSchema"); | ||
Object.defineProperty(exports, "printSDLWithoutDirectives", { enumerable: true, get: function () { return printSchema_1.printSDLWithoutDirectives; } }); | ||
Object.defineProperty(exports, "printSDLWithoutMetadata", { enumerable: true, get: function () { return printSchema_1.printSDLWithoutMetadata; } }); | ||
__exportStar(require("./Types"), exports); | ||
@@ -46,4 +46,4 @@ __exportStar(require("./lib"), exports); | ||
} | ||
return (0, Result_1.ok)((0, lib_1.validateGratsOptions)(parsed)); | ||
return (0, Result_1.ok)((0, gratsConfig_1.validateGratsOptions)(parsed)); | ||
} | ||
exports.getParsedTsConfig = getParsedTsConfig; |
@@ -1,4 +0,4 @@ | ||
import { GratsDefinitionNode } from "./GraphQLConstructor"; | ||
import { TypeContext } from "./TypeContext"; | ||
import { DefaultMap } from "./utils/helpers"; | ||
import { DefinitionNode } from "graphql"; | ||
export type InterfaceImplementor = { | ||
@@ -12,2 +12,2 @@ kind: "TYPE" | "INTERFACE"; | ||
*/ | ||
export declare function computeInterfaceMap(typeContext: TypeContext, docs: GratsDefinitionNode[]): InterfaceMap; | ||
export declare function computeInterfaceMap(typeContext: TypeContext, docs: DefinitionNode[]): InterfaceMap; |
@@ -37,3 +37,3 @@ "use strict"; | ||
var implementor = _g.value; | ||
var resolved = typeContext.resolveNamedType(implementor.name); | ||
var resolved = typeContext.resolveUnresolvedNamedType(implementor.name); | ||
if (resolved.kind === "ERROR") { | ||
@@ -62,3 +62,3 @@ // We trust that these errors will be reported elsewhere. | ||
var implementor = _j.value; | ||
var resolved = typeContext.resolveNamedType(implementor.name); | ||
var resolved = typeContext.resolveUnresolvedNamedType(implementor.name); | ||
if (resolved.kind === "ERROR") { | ||
@@ -65,0 +65,0 @@ // We trust that these errors will be reported elsewhere. |
@@ -1,2 +0,2 @@ | ||
import { GraphQLSchema } from "graphql"; | ||
import { DocumentNode, GraphQLSchema } from "graphql"; | ||
import { DiagnosticsWithoutLocationResult, ReportableDiagnostics } from "./utils/DiagnosticError"; | ||
@@ -6,8 +6,11 @@ import { Result } from "./utils/Result"; | ||
import { ParsedCommandLineGrats } from "./gratsConfig"; | ||
export * from "./gratsConfig"; | ||
export declare function buildSchemaResult(options: ParsedCommandLineGrats): Result<GraphQLSchema, ReportableDiagnostics>; | ||
export declare function buildSchemaResultWithHost(options: ParsedCommandLineGrats, compilerHost: ts.CompilerHost): Result<GraphQLSchema, ReportableDiagnostics>; | ||
export type SchemaAndDoc = { | ||
schema: GraphQLSchema; | ||
doc: DocumentNode; | ||
}; | ||
export declare function buildSchemaAndDocResult(options: ParsedCommandLineGrats): Result<SchemaAndDoc, ReportableDiagnostics>; | ||
export declare function buildSchemaAndDocResultWithHost(options: ParsedCommandLineGrats, compilerHost: ts.CompilerHost): Result<SchemaAndDoc, ReportableDiagnostics>; | ||
/** | ||
* The core transformation pipeline of Grats. | ||
*/ | ||
export declare function extractSchema(options: ParsedCommandLineGrats, program: ts.Program): DiagnosticsWithoutLocationResult<GraphQLSchema>; | ||
export declare function extractSchemaAndDoc(options: ParsedCommandLineGrats, program: ts.Program): DiagnosticsWithoutLocationResult<SchemaAndDoc>; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
var __values = (this && this.__values) || function(o) { | ||
@@ -44,3 +30,3 @@ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.extractSchema = exports.buildSchemaResultWithHost = exports.buildSchemaResult = void 0; | ||
exports.extractSchemaAndDoc = exports.buildSchemaAndDocResultWithHost = exports.buildSchemaAndDocResult = void 0; | ||
var graphql_1 = require("graphql"); | ||
@@ -60,9 +46,11 @@ var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var filterNonGqlInterfaces_1 = require("./transforms/filterNonGqlInterfaces"); | ||
var resolveTypes_1 = require("./transforms/resolveTypes"); | ||
var validateAsyncIterable_1 = require("./validations/validateAsyncIterable"); | ||
var applyDefaultNullability_1 = require("./transforms/applyDefaultNullability"); | ||
__exportStar(require("./gratsConfig"), exports); | ||
var mergeExtensions_1 = require("./transforms/mergeExtensions"); | ||
var sortSchemaAst_1 = require("./transforms/sortSchemaAst"); | ||
var validateSemanticNullability_1 = require("./validations/validateSemanticNullability"); | ||
var resolveTypes_1 = require("./transforms/resolveTypes"); | ||
// Construct a schema, using GraphQL schema language | ||
// Exported for tests that want to intercept diagnostic errors. | ||
function buildSchemaResult(options) { | ||
function buildSchemaAndDocResult(options) { | ||
// https://stackoverflow.com/a/66604532/1263117 | ||
@@ -72,16 +60,16 @@ var compilerHost = ts.createCompilerHost(options.options, | ||
true); | ||
return buildSchemaResultWithHost(options, compilerHost); | ||
return buildSchemaAndDocResultWithHost(options, compilerHost); | ||
} | ||
exports.buildSchemaResult = buildSchemaResult; | ||
function buildSchemaResultWithHost(options, compilerHost) { | ||
exports.buildSchemaAndDocResult = buildSchemaAndDocResult; | ||
function buildSchemaAndDocResultWithHost(options, compilerHost) { | ||
var program = ts.createProgram(options.fileNames, options.options, compilerHost); | ||
return new Result_1.ResultPipe(extractSchema(options, program)) | ||
return new Result_1.ResultPipe(extractSchemaAndDoc(options, program)) | ||
.mapErr(function (e) { return new DiagnosticError_1.ReportableDiagnostics(compilerHost, e); }) | ||
.result(); | ||
} | ||
exports.buildSchemaResultWithHost = buildSchemaResultWithHost; | ||
exports.buildSchemaAndDocResultWithHost = buildSchemaAndDocResultWithHost; | ||
/** | ||
* The core transformation pipeline of Grats. | ||
*/ | ||
function extractSchema(options, program) { | ||
function extractSchemaAndDoc(options, program) { | ||
return new Result_1.ResultPipe((0, snapshotsFromProgram_1.extractSnapshotsFromProgram)(program, options)) | ||
@@ -96,6 +84,9 @@ .map(function (snapshots) { return combineSnapshots(snapshots); }) | ||
var validationResult = (0, Result_1.concatResults)((0, validateMergedInterfaces_1.validateMergedInterfaces)(checker, snapshot.interfaceDeclarations), (0, validateContextReferences_1.validateContextReferences)(ctx, snapshot.contextReferences)); | ||
return (new Result_1.ResultPipe(validationResult) | ||
var docResult = new Result_1.ResultPipe(validationResult) | ||
// Add the metadata directive definitions to definitions | ||
// found in the snapshot. | ||
.map(function () { return (0, metadataDirectives_1.addMetadataDirectives)(snapshot.definitions); }) | ||
// Filter out any `implements` clauses that are not GraphQL interfaces. | ||
.map(function (definitions) { return (0, filterNonGqlInterfaces_1.filterNonGqlInterfaces)(ctx, definitions); }) | ||
.andThen(function (definitions) { return (0, resolveTypes_1.resolveTypes)(ctx, definitions); }) | ||
// If you define a field on an interface using the functional style, we need to add | ||
@@ -107,18 +98,24 @@ // that field to each concrete type as well. This must be done after all types are created, | ||
.map(function (definitions) { return ({ kind: graphql_1.Kind.DOCUMENT, definitions: definitions }); }) | ||
// Filter out any `implements` clauses that are not GraphQL interfaces. | ||
.map(function (doc) { return (0, filterNonGqlInterfaces_1.filterNonGqlInterfaces)(ctx, doc); }) | ||
// Ensure all subscription fields return an AsyncIterable. | ||
.andThen(function (doc) { return (0, validateAsyncIterable_1.validateAsyncIterable)(doc); }) | ||
// Apply default nullability to fields and arguments, and detect any misuse of | ||
// `@killsParentOnException`. | ||
.andThen(function (doc) { return (0, applyDefaultNullability_1.applyDefaultNullability)(doc, config); }) | ||
// Resolve TypeScript type references to the GraphQL types they represent (or error). | ||
.andThen(function (doc) { return (0, resolveTypes_1.resolveTypes)(ctx, doc); }) | ||
// Ensure all subscription fields, and _only_ subscription fields, return an AsyncIterable. | ||
.andThen(function (doc) { return (0, validateAsyncIterable_1.validateAsyncIterable)(doc); }) | ||
// Validate the document node against the GraphQL spec. | ||
// Build and validate the schema with regards to the GraphQL spec. | ||
.andThen(function (doc) { return buildSchemaFromDoc(doc); }) | ||
// Merge any `extend` definitions into their base definitions. | ||
.map(function (doc) { return (0, mergeExtensions_1.mergeExtensions)(doc); }) | ||
// Sort the definitions in the document to ensure a stable output. | ||
.map(function (doc) { return (0, sortSchemaAst_1.sortSchemaAst)(doc); }) | ||
.result(); | ||
if (docResult.kind === "ERROR") { | ||
return docResult; | ||
} | ||
var doc = docResult.value; | ||
// Build and validate the schema with regards to the GraphQL spec. | ||
return (new Result_1.ResultPipe(buildSchemaFromDoc(doc)) | ||
// Ensure that every type which implements an interface or is a member of a | ||
// union has a __typename field. | ||
.andThen(function (schema) { return (0, validateTypenames_1.validateTypenames)(schema, typesWithTypename); }) | ||
.map(function (schema) { return (0, graphql_1.lexicographicSortSchema)(schema); }) | ||
.andThen(function (schema) { return (0, validateSemanticNullability_1.validateSemanticNullability)(schema, config); }) | ||
// Combine the schema and document into a single result. | ||
.map(function (schema) { return ({ schema: schema, doc: doc }); }) | ||
.result()); | ||
@@ -128,3 +125,3 @@ }) | ||
} | ||
exports.extractSchema = extractSchema; | ||
exports.extractSchemaAndDoc = extractSchemaAndDoc; | ||
// Given a SDL AST, build and validate a GraphQLSchema. | ||
@@ -131,0 +128,0 @@ function buildSchemaFromDoc(doc) { |
@@ -1,25 +0,72 @@ | ||
import { ConstDirectiveNode, DocumentNode, Location } from "graphql"; | ||
import { GratsDefinitionNode } from "./GraphQLConstructor"; | ||
export declare const FIELD_NAME_DIRECTIVE = "propertyName"; | ||
export declare const EXPORTED_DIRECTIVE = "exported"; | ||
export declare const ASYNC_ITERABLE_TYPE_DIRECTIVE = "asyncIterable"; | ||
import { ConstDirectiveNode, DefinitionNode, DocumentNode, Location } from "graphql"; | ||
/** | ||
* In most cases we can use directives to annotate constructs | ||
* however, it't not possible to annotate an individual TypeNode. | ||
* Additionally, we can't use sets or maps to "tag" nodes because | ||
* there are places where we immutably update the AST to make changes. | ||
* | ||
* Instead, we cheat and add properties to some nodes. These types use | ||
* interface merging to add our own properties to the AST. | ||
* | ||
* We try to use this approach sparingly. | ||
*/ | ||
declare module "graphql" { | ||
interface ListTypeNode { | ||
/** | ||
* Grats metadata: Whether the list type was defined as an AsyncIterable. | ||
* Used to ensure that all fields on `Subscription` return an AsyncIterable. | ||
*/ | ||
isAsyncIterable?: boolean; | ||
} | ||
interface NameNode { | ||
/** | ||
* Grats metadata: A unique identifier for the node. Used to track | ||
* data about nodes in lookup data structures. | ||
*/ | ||
tsIdentifier: number; | ||
} | ||
interface ObjectTypeDefinitionNode { | ||
/** | ||
* Grats metadata: Indicates that the type was materialized as part of | ||
* generic type resolution. | ||
*/ | ||
wasSynthesized?: boolean; | ||
} | ||
interface UnionTypeDefinitionNode { | ||
/** | ||
* Grats metadata: Indicates that the type was materialized as part of | ||
* generic type resolution. | ||
*/ | ||
wasSynthesized?: boolean; | ||
} | ||
interface InterfaceTypeDefinitionNode { | ||
/** | ||
* Grats metadata: Indicates that the type was materialized as part of | ||
* generic type resolution. | ||
*/ | ||
wasSynthesized?: boolean; | ||
} | ||
interface ObjectTypeExtensionNode { | ||
/** | ||
* Grats metadata: Indicates that we don't know yet if this is extending an interface | ||
* or a type. | ||
*/ | ||
mayBeInterface?: boolean; | ||
} | ||
} | ||
export declare const FIELD_METADATA_DIRECTIVE = "metadata"; | ||
export declare const FIELD_NAME_ARG = "name"; | ||
export declare const TS_MODULE_PATH_ARG = "tsModulePath"; | ||
export declare const ARG_COUNT = "argCount"; | ||
export declare const ASYNC_ITERABLE_ARG = "asyncIterable"; | ||
export declare const KILLS_PARENT_ON_EXCEPTION_DIRECTIVE = "killsParentOnException"; | ||
export declare const METADATA_DIRECTIVE_NAMES: Set<string>; | ||
export declare const DIRECTIVES_AST: DocumentNode; | ||
export declare function addMetadataDirectives(definitions: Array<GratsDefinitionNode>): Array<GratsDefinitionNode>; | ||
export type AsyncIterableTypeMetadata = true; | ||
export type PropertyNameMetadata = { | ||
name: string; | ||
export declare function addMetadataDirectives(definitions: Array<DefinitionNode>): Array<DefinitionNode>; | ||
export type FieldMetadata = { | ||
tsModulePath: string | null; | ||
name: string | null; | ||
argCount: number | null; | ||
}; | ||
export type ExportedMetadata = { | ||
tsModulePath: string; | ||
exportedFunctionName: string; | ||
argCount: number; | ||
}; | ||
export declare function makePropertyNameDirective(loc: Location, propertyName: PropertyNameMetadata): ConstDirectiveNode; | ||
export declare function makeExportedDirective(loc: Location, exported: ExportedMetadata): ConstDirectiveNode; | ||
export declare function makeAsyncIterableDirective(loc: Location): ConstDirectiveNode; | ||
export declare function makeKillsParentOnExceptionDirective(loc: Location): ConstDirectiveNode; | ||
export declare function parseAsyncIterableTypeDirective(directive: ConstDirectiveNode): AsyncIterableTypeMetadata; | ||
export declare function parsePropertyNameDirective(directive: ConstDirectiveNode): PropertyNameMetadata; | ||
export declare function parseExportedDirective(directive: ConstDirectiveNode): ExportedMetadata; | ||
export declare function parseFieldMetadataDirective(directive: ConstDirectiveNode): FieldMetadata; |
@@ -28,19 +28,16 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseExportedDirective = exports.parsePropertyNameDirective = exports.parseAsyncIterableTypeDirective = exports.makeKillsParentOnExceptionDirective = exports.makeAsyncIterableDirective = exports.makeExportedDirective = exports.makePropertyNameDirective = exports.addMetadataDirectives = exports.DIRECTIVES_AST = exports.METADATA_DIRECTIVE_NAMES = exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE = exports.ASYNC_ITERABLE_TYPE_DIRECTIVE = exports.EXPORTED_DIRECTIVE = exports.FIELD_NAME_DIRECTIVE = void 0; | ||
exports.parseFieldMetadataDirective = exports.makeKillsParentOnExceptionDirective = exports.addMetadataDirectives = exports.DIRECTIVES_AST = exports.METADATA_DIRECTIVE_NAMES = exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE = exports.ASYNC_ITERABLE_ARG = exports.ARG_COUNT = exports.TS_MODULE_PATH_ARG = exports.FIELD_NAME_ARG = exports.FIELD_METADATA_DIRECTIVE = void 0; | ||
var graphql_1 = require("graphql"); | ||
exports.FIELD_NAME_DIRECTIVE = "propertyName"; | ||
var FIELD_NAME_ARG = "name"; | ||
exports.EXPORTED_DIRECTIVE = "exported"; | ||
var TS_MODULE_PATH_ARG = "tsModulePath"; | ||
var ARG_COUNT = "argCount"; | ||
var EXPORTED_FUNCTION_NAME_ARG = "functionName"; | ||
exports.ASYNC_ITERABLE_TYPE_DIRECTIVE = "asyncIterable"; | ||
var helpers_1 = require("./utils/helpers"); | ||
exports.FIELD_METADATA_DIRECTIVE = "metadata"; | ||
exports.FIELD_NAME_ARG = "name"; | ||
exports.TS_MODULE_PATH_ARG = "tsModulePath"; | ||
exports.ARG_COUNT = "argCount"; | ||
exports.ASYNC_ITERABLE_ARG = "asyncIterable"; | ||
exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE = "killsParentOnException"; | ||
exports.METADATA_DIRECTIVE_NAMES = new Set([ | ||
exports.FIELD_NAME_DIRECTIVE, | ||
exports.EXPORTED_DIRECTIVE, | ||
exports.ASYNC_ITERABLE_TYPE_DIRECTIVE, | ||
exports.FIELD_METADATA_DIRECTIVE, | ||
exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE, | ||
]); | ||
exports.DIRECTIVES_AST = (0, graphql_1.parse)("\n directive @".concat(exports.ASYNC_ITERABLE_TYPE_DIRECTIVE, " on FIELD_DEFINITION\n directive @").concat(exports.FIELD_NAME_DIRECTIVE, "(").concat(FIELD_NAME_ARG, ": String!) on FIELD_DEFINITION\n directive @").concat(exports.EXPORTED_DIRECTIVE, "(\n ").concat(TS_MODULE_PATH_ARG, ": String!,\n ").concat(EXPORTED_FUNCTION_NAME_ARG, ": String!\n ").concat(ARG_COUNT, ": Int!\n ) on FIELD_DEFINITION\n directive @").concat(exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE, " on FIELD_DEFINITION\n")); | ||
exports.DIRECTIVES_AST = (0, graphql_1.parse)("\n directive @".concat(exports.FIELD_METADATA_DIRECTIVE, "(\n \"\"\"\n Name of property/method/function. Defaults to field name. For\n function-backed fields, this is the function's export name.\n \"\"\"\n ").concat(exports.FIELD_NAME_ARG, ": String\n \"\"\"\n Path of the TypeScript module to import if the field is a function.\n \"\"\"\n ").concat(exports.TS_MODULE_PATH_ARG, ": String\n \"\"\"\n Number of arguments. No value means property access\n \"\"\"\n ").concat(exports.ARG_COUNT, ": Int\n ) on FIELD_DEFINITION\n directive @").concat(exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE, " on FIELD_DEFINITION\n")); | ||
function addMetadataDirectives(definitions) { | ||
@@ -50,33 +47,2 @@ return __spreadArray(__spreadArray([], __read(exports.DIRECTIVES_AST.definitions), false), __read(definitions), false); | ||
exports.addMetadataDirectives = addMetadataDirectives; | ||
function makePropertyNameDirective(loc, propertyName) { | ||
return { | ||
kind: graphql_1.Kind.DIRECTIVE, | ||
loc: loc, | ||
name: { kind: graphql_1.Kind.NAME, loc: loc, value: exports.FIELD_NAME_DIRECTIVE }, | ||
arguments: [makeStringArg(loc, FIELD_NAME_ARG, propertyName.name)], | ||
}; | ||
} | ||
exports.makePropertyNameDirective = makePropertyNameDirective; | ||
function makeExportedDirective(loc, exported) { | ||
return { | ||
kind: graphql_1.Kind.DIRECTIVE, | ||
loc: loc, | ||
name: { kind: graphql_1.Kind.NAME, loc: loc, value: exports.EXPORTED_DIRECTIVE }, | ||
arguments: [ | ||
makeStringArg(loc, TS_MODULE_PATH_ARG, exported.tsModulePath), | ||
makeStringArg(loc, EXPORTED_FUNCTION_NAME_ARG, exported.exportedFunctionName), | ||
makeIntArg(loc, ARG_COUNT, exported.argCount), | ||
], | ||
}; | ||
} | ||
exports.makeExportedDirective = makeExportedDirective; | ||
function makeAsyncIterableDirective(loc) { | ||
return { | ||
kind: graphql_1.Kind.DIRECTIVE, | ||
loc: loc, | ||
name: { kind: graphql_1.Kind.NAME, loc: loc, value: exports.ASYNC_ITERABLE_TYPE_DIRECTIVE }, | ||
arguments: [], | ||
}; | ||
} | ||
exports.makeAsyncIterableDirective = makeAsyncIterableDirective; | ||
function makeKillsParentOnExceptionDirective(loc) { | ||
@@ -86,3 +52,8 @@ return { | ||
loc: loc, | ||
name: { kind: graphql_1.Kind.NAME, loc: loc, value: exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE }, | ||
name: { | ||
kind: graphql_1.Kind.NAME, | ||
loc: loc, | ||
value: exports.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE, | ||
tsIdentifier: (0, helpers_1.uniqueId)(), | ||
}, | ||
arguments: [], | ||
@@ -92,27 +63,13 @@ }; | ||
exports.makeKillsParentOnExceptionDirective = makeKillsParentOnExceptionDirective; | ||
function parseAsyncIterableTypeDirective(directive) { | ||
if (directive.name.value !== exports.ASYNC_ITERABLE_TYPE_DIRECTIVE) { | ||
throw new Error("Expected directive to be ".concat(exports.ASYNC_ITERABLE_TYPE_DIRECTIVE)); | ||
function parseFieldMetadataDirective(directive) { | ||
if (directive.name.value !== exports.FIELD_METADATA_DIRECTIVE) { | ||
throw new Error("Expected directive to be ".concat(exports.FIELD_METADATA_DIRECTIVE)); | ||
} | ||
return true; | ||
} | ||
exports.parseAsyncIterableTypeDirective = parseAsyncIterableTypeDirective; | ||
function parsePropertyNameDirective(directive) { | ||
if (directive.name.value !== exports.FIELD_NAME_DIRECTIVE) { | ||
throw new Error("Expected directive to be ".concat(exports.FIELD_NAME_DIRECTIVE)); | ||
} | ||
return { name: getStringArg(directive, FIELD_NAME_ARG) }; | ||
} | ||
exports.parsePropertyNameDirective = parsePropertyNameDirective; | ||
function parseExportedDirective(directive) { | ||
if (directive.name.value !== exports.EXPORTED_DIRECTIVE) { | ||
throw new Error("Expected directive to be ".concat(exports.EXPORTED_DIRECTIVE)); | ||
} | ||
return { | ||
tsModulePath: getStringArg(directive, TS_MODULE_PATH_ARG), | ||
exportedFunctionName: getStringArg(directive, EXPORTED_FUNCTION_NAME_ARG), | ||
argCount: getIntArg(directive, ARG_COUNT), | ||
name: getStringArg(directive, exports.FIELD_NAME_ARG), | ||
tsModulePath: getStringArg(directive, exports.TS_MODULE_PATH_ARG), | ||
argCount: getIntArg(directive, exports.ARG_COUNT), | ||
}; | ||
} | ||
exports.parseExportedDirective = parseExportedDirective; | ||
exports.parseFieldMetadataDirective = parseFieldMetadataDirective; | ||
function getStringArg(directive, argName) { | ||
@@ -122,3 +79,3 @@ var _a; | ||
if (!arg) { | ||
throw new Error("Expected to find argument ".concat(argName)); | ||
return null; | ||
} | ||
@@ -134,3 +91,3 @@ if (arg.value.kind !== graphql_1.Kind.STRING) { | ||
if (!arg) { | ||
throw new Error("Expected to find argument ".concat(argName)); | ||
return null; | ||
} | ||
@@ -142,17 +99,1 @@ if (arg.value.kind !== graphql_1.Kind.INT) { | ||
} | ||
function makeStringArg(loc, argName, value) { | ||
return { | ||
kind: graphql_1.Kind.ARGUMENT, | ||
loc: loc, | ||
name: { kind: graphql_1.Kind.NAME, loc: loc, value: argName }, | ||
value: { kind: graphql_1.Kind.STRING, loc: loc, value: value }, | ||
}; | ||
} | ||
function makeIntArg(loc, argName, value) { | ||
return { | ||
kind: graphql_1.Kind.ARGUMENT, | ||
loc: loc, | ||
name: { kind: graphql_1.Kind.NAME, loc: loc, value: argName }, | ||
value: { kind: graphql_1.Kind.INT, loc: loc, value: value.toString() }, | ||
}; | ||
} |
@@ -1,3 +0,3 @@ | ||
import { GraphQLSchema } from "graphql"; | ||
import { ConfigOptions } from "./lib"; | ||
import { DocumentNode, GraphQLSchema } from "graphql"; | ||
import { ConfigOptions } from "./gratsConfig"; | ||
/** | ||
@@ -8,2 +8,3 @@ * Prints code for a TypeScript module that exports a GraphQLSchema. | ||
export declare function printExecutableSchema(schema: GraphQLSchema, config: ConfigOptions, destination: string): string; | ||
export declare function applyTypeScriptHeader(config: ConfigOptions, code: string): string; | ||
/** | ||
@@ -13,3 +14,4 @@ * Prints SDL, potentially omitting directives depending upon the config. | ||
*/ | ||
export declare function printGratsSDL(schema: GraphQLSchema, config: ConfigOptions): string; | ||
export declare function printSDLWithoutDirectives(schema: GraphQLSchema): string; | ||
export declare function printGratsSDL(doc: DocumentNode, config: ConfigOptions): string; | ||
export declare function applySDLHeader(config: ConfigOptions, sdl: string): string; | ||
export declare function printSDLWithoutMetadata(doc: DocumentNode): string; |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.printSDLWithoutDirectives = exports.printGratsSDL = exports.printExecutableSchema = void 0; | ||
exports.printSDLWithoutMetadata = exports.applySDLHeader = exports.printGratsSDL = exports.applyTypeScriptHeader = exports.printExecutableSchema = void 0; | ||
var graphql_1 = require("graphql"); | ||
@@ -24,8 +13,9 @@ var codegen_1 = require("./codegen"); | ||
var code = (0, codegen_1.codegen)(schema, destination); | ||
if (config.tsSchemaHeader) { | ||
return "".concat(config.tsSchemaHeader, "\n").concat(code); | ||
} | ||
return code; | ||
return applyTypeScriptHeader(config, code); | ||
} | ||
exports.printExecutableSchema = printExecutableSchema; | ||
function applyTypeScriptHeader(config, code) { | ||
return formatHeader(config.tsSchemaHeader, code); | ||
} | ||
exports.applyTypeScriptHeader = applyTypeScriptHeader; | ||
/** | ||
@@ -35,15 +25,33 @@ * Prints SDL, potentially omitting directives depending upon the config. | ||
*/ | ||
function printGratsSDL(schema, config) { | ||
var sdl = printSDLWithoutDirectives(schema); | ||
if (config.schemaHeader) { | ||
return "".concat(config.schemaHeader, "\n").concat(sdl); | ||
} | ||
return sdl; | ||
function printGratsSDL(doc, config) { | ||
var sdl = printSDLWithoutMetadata(doc); | ||
return applySDLHeader(config, sdl) + "\n"; | ||
} | ||
exports.printGratsSDL = printGratsSDL; | ||
function printSDLWithoutDirectives(schema) { | ||
return (0, graphql_1.printSchema)(new graphql_1.GraphQLSchema(__assign(__assign({}, schema.toConfig()), { directives: schema.getDirectives().filter(function (directive) { | ||
return !metadataDirectives_1.METADATA_DIRECTIVE_NAMES.has(directive.name); | ||
}) }))); | ||
function applySDLHeader(config, sdl) { | ||
return formatHeader(config.schemaHeader, sdl); | ||
} | ||
exports.printSDLWithoutDirectives = printSDLWithoutDirectives; | ||
exports.applySDLHeader = applySDLHeader; | ||
function printSDLWithoutMetadata(doc) { | ||
var trimmed = (0, graphql_1.visit)(doc, { | ||
DirectiveDefinition: function (t) { | ||
return metadataDirectives_1.METADATA_DIRECTIVE_NAMES.has(t.name.value) ? null : t; | ||
}, | ||
Directive: function (t) { | ||
return metadataDirectives_1.METADATA_DIRECTIVE_NAMES.has(t.name.value) ? null : t; | ||
}, | ||
ScalarTypeDefinition: function (t) { | ||
return graphql_1.specifiedScalarTypes.some(function (scalar) { return scalar.name === t.name.value; }) | ||
? null | ||
: t; | ||
}, | ||
}); | ||
return (0, graphql_1.print)(trimmed); | ||
} | ||
exports.printSDLWithoutMetadata = printSDLWithoutMetadata; | ||
function formatHeader(header, code) { | ||
if (header !== null) { | ||
return "".concat(header, "\n").concat(code); | ||
} | ||
return code; | ||
} |
import { DefinitionNode } from "graphql"; | ||
import { TypeContext } from "../TypeContext"; | ||
import { DiagnosticsResult } from "../utils/DiagnosticError"; | ||
import { GratsDefinitionNode } from "../GraphQLConstructor"; | ||
/** | ||
@@ -13,2 +12,2 @@ * Grats allows you to define GraphQL fields on TypeScript interfaces using | ||
*/ | ||
export declare function addInterfaceFields(ctx: TypeContext, docs: GratsDefinitionNode[]): DiagnosticsResult<DefinitionNode[]>; | ||
export declare function addInterfaceFields(ctx: TypeContext, docs: DefinitionNode[]): DiagnosticsResult<DefinitionNode[]>; |
@@ -32,4 +32,4 @@ "use strict"; | ||
var helpers_1 = require("../utils/helpers"); | ||
var Extractor_1 = require("../Extractor"); | ||
var metadataDirectives_1 = require("../metadataDirectives"); | ||
var Extractor_1 = require("../Extractor"); | ||
/** | ||
@@ -51,6 +51,6 @@ * Grats allows you to define GraphQL fields on TypeScript interfaces using | ||
var doc = docs_1_1.value; | ||
if (doc.kind === "AbstractFieldDefinition") { | ||
if (doc.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION && doc.mayBeInterface) { | ||
var abstractDocResults = addAbstractFieldDefinition(ctx, doc, interfaceGraph); | ||
if (abstractDocResults.kind === "ERROR") { | ||
(0, helpers_1.extend)(errors, abstractDocResults.err); | ||
errors.push(abstractDocResults.err); | ||
} | ||
@@ -83,5 +83,5 @@ else { | ||
var e_2, _a; | ||
var _b; | ||
var _b, _c; | ||
var newDocs = []; | ||
var definitionResult = ctx.getNameDefinition(doc.onType); | ||
var definitionResult = ctx.gqlNameDefinitionForGqlName(doc.name); | ||
if (definitionResult.kind === "ERROR") { | ||
@@ -91,2 +91,3 @@ return definitionResult; | ||
var nameDefinition = definitionResult.value; | ||
var field = (0, helpers_1.nullThrows)((_b = doc.fields) === null || _b === void 0 ? void 0 : _b[0]); | ||
switch (nameDefinition.kind) { | ||
@@ -97,4 +98,4 @@ case "TYPE": | ||
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION, | ||
name: doc.onType, | ||
fields: [doc.field], | ||
name: doc.name, | ||
fields: [field], | ||
loc: doc.loc, | ||
@@ -108,17 +109,18 @@ }); | ||
// need to annotate it with the details of the implementation. | ||
var directives = (_b = doc.field.directives) === null || _b === void 0 ? void 0 : _b.filter(function (directive) { | ||
return directive.name.value !== metadataDirectives_1.EXPORTED_DIRECTIVE; | ||
var directives = (_c = field.directives) === null || _c === void 0 ? void 0 : _c.filter(function (directive) { | ||
return directive.name.value !== metadataDirectives_1.FIELD_METADATA_DIRECTIVE; | ||
}); | ||
newDocs.push({ | ||
kind: graphql_1.Kind.INTERFACE_TYPE_EXTENSION, | ||
name: doc.onType, | ||
fields: [__assign(__assign({}, doc.field), { directives: directives })], | ||
name: doc.name, | ||
fields: [__assign(__assign({}, field), { directives: directives })], | ||
}); | ||
try { | ||
for (var _c = __values(interfaceGraph.get(nameDefinition.name.value)), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var implementor = _d.value; | ||
for (var _d = __values(interfaceGraph.get(nameDefinition.name.value)), _e = _d.next(); !_e.done; _e = _d.next()) { | ||
var implementor = _e.value; | ||
var name = { | ||
kind: graphql_1.Kind.NAME, | ||
value: implementor.name, | ||
loc: doc.loc, // Bit of a lie, but I don't see a better option. | ||
loc: doc.loc, | ||
tsIdentifier: (0, helpers_1.uniqueId)(), | ||
}; | ||
@@ -130,3 +132,3 @@ switch (implementor.kind) { | ||
name: name, | ||
fields: [doc.field], | ||
fields: [field], | ||
loc: doc.loc, | ||
@@ -139,3 +141,3 @@ }); | ||
name: name, | ||
fields: [__assign(__assign({}, doc.field), { directives: directives })], | ||
fields: [__assign(__assign({}, field), { directives: directives })], | ||
loc: doc.loc, | ||
@@ -150,3 +152,3 @@ }); | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
if (_e && !_e.done && (_a = _d.return)) _a.call(_d); | ||
} | ||
@@ -159,15 +161,7 @@ finally { if (e_2) throw e_2.error; } | ||
// Extending any other type of definition is not supported. | ||
var loc = doc.onType.loc; | ||
if (loc == null) { | ||
throw new Error("Expected onType to have a location."); | ||
} | ||
var relatedLoc = nameDefinition.name.loc; | ||
if (relatedLoc == null) { | ||
throw new Error("Expected nameDefinition to have a location."); | ||
} | ||
return (0, Result_1.err)([ | ||
(0, DiagnosticError_1.gqlErr)(loc, E.invalidTypePassedToFieldFunction(), [ | ||
(0, DiagnosticError_1.gqlRelated)(relatedLoc, "This is the type that was passed to `@".concat(Extractor_1.FIELD_TAG, "`.")), | ||
]), | ||
]); | ||
var loc = (0, helpers_1.nullThrows)(doc.name.loc); | ||
var relatedLoc = (0, helpers_1.nullThrows)(nameDefinition.name.loc); | ||
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)(loc, E.invalidTypePassedToFieldFunction(), [ | ||
(0, DiagnosticError_1.gqlRelated)(relatedLoc, "This is the type that was passed to `@".concat(Extractor_1.FIELD_TAG, "`.")), | ||
])); | ||
} | ||
@@ -174,0 +168,0 @@ } |
import { DocumentNode } from "graphql"; | ||
import { DiagnosticsResult } from "../utils/DiagnosticError"; | ||
import { ConfigOptions } from "../gratsConfig"; | ||
export declare function applyDefaultNullability(doc: DocumentNode, { nullableByDefault }: ConfigOptions): DiagnosticsResult<DocumentNode>; | ||
/** | ||
* Grats has options to make all fields nullable by default to conform to | ||
* GraphQL best practices. This transform applies this option to the schema. | ||
*/ | ||
export declare function applyDefaultNullability(doc: DocumentNode, { nullableByDefault, strictSemanticNullability }: ConfigOptions): DiagnosticsResult<DocumentNode>; |
@@ -13,2 +13,27 @@ "use strict"; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { | ||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { | ||
if (ar || !(i in from)) { | ||
if (!ar) ar = Array.prototype.slice.call(from, 0, i); | ||
ar[i] = from[i]; | ||
} | ||
} | ||
return to.concat(ar || Array.prototype.slice.call(from)); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -21,7 +46,12 @@ exports.applyDefaultNullability = void 0; | ||
var metadataDirectives_1 = require("../metadataDirectives"); | ||
var publicDirectives_1 = require("../publicDirectives"); | ||
var GraphQLConstructor_1 = require("../GraphQLConstructor"); | ||
var helpers_1 = require("../utils/helpers"); | ||
/** | ||
* Grats has options to make all fields nullable by default to conform to | ||
* GraphQL best practices. This transform applies this option to the schema. | ||
*/ | ||
function applyDefaultNullability(doc, _a) { | ||
var _b; | ||
var nullableByDefault = _a.nullableByDefault; | ||
var nullableByDefault = _a.nullableByDefault, strictSemanticNullability = _a.strictSemanticNullability; | ||
var gql = new GraphQLConstructor_1.GraphQLConstructor(); | ||
@@ -31,3 +61,3 @@ var errors = []; | ||
_b[graphql_1.Kind.FIELD_DEFINITION] = function (t) { | ||
var _a; | ||
var _a, _b; | ||
var killsParent = (_a = t.directives) === null || _a === void 0 ? void 0 : _a.find(function (d) { return d.name.value === metadataDirectives_1.KILLS_PARENT_ON_EXCEPTION_DIRECTIVE; }); | ||
@@ -44,3 +74,3 @@ if (killsParent) { | ||
// Set the location of the NON_NULL_TYPE wrapper to the location of the | ||
// @killsParentOnException directive so that type errors created by graphql-js | ||
// `@killsParentOnException` directive so that type errors created by graphql-js | ||
// are reported at the correct location. | ||
@@ -50,3 +80,9 @@ return __assign(__assign({}, t), { type: __assign(__assign({}, t.type), { loc: killsParent.loc }) }); | ||
if (nullableByDefault && t.type.kind === graphql_1.Kind.NON_NULL_TYPE) { | ||
return __assign(__assign({}, t), { type: gql.nullableType(t.type) }); | ||
var type = gql.nullableType(t.type); | ||
var directives = (_b = t.directives) !== null && _b !== void 0 ? _b : []; | ||
if (strictSemanticNullability) { | ||
var semanticNullability = (0, publicDirectives_1.makeSemanticNonNullDirective)((0, helpers_1.loc)(t.type)); | ||
directives = __spreadArray(__spreadArray([], __read(directives), false), [semanticNullability], false); | ||
} | ||
return __assign(__assign({}, t), { directives: directives, type: type }); | ||
} | ||
@@ -59,4 +95,7 @@ return t; | ||
} | ||
if (strictSemanticNullability) { | ||
return (0, Result_1.ok)(__assign(__assign({}, newDoc), { definitions: (0, publicDirectives_1.addSemanticNonNullDirective)(newDoc.definitions) })); | ||
} | ||
return (0, Result_1.ok)(newDoc); | ||
} | ||
exports.applyDefaultNullability = applyDefaultNullability; |
@@ -1,2 +0,2 @@ | ||
import { DocumentNode } from "graphql"; | ||
import { DefinitionNode } from "graphql"; | ||
import { TypeContext } from "../TypeContext"; | ||
@@ -9,2 +9,2 @@ /** | ||
*/ | ||
export declare function filterNonGqlInterfaces(ctx: TypeContext, doc: DocumentNode): DocumentNode; | ||
export declare function filterNonGqlInterfaces(ctx: TypeContext, definitions: DefinitionNode[]): DefinitionNode[]; |
@@ -22,10 +22,12 @@ "use strict"; | ||
*/ | ||
function filterNonGqlInterfaces(ctx, doc) { | ||
var _a; | ||
return (0, graphql_1.visit)(doc, (_a = {}, | ||
_a[graphql_1.Kind.INTERFACE_TYPE_DEFINITION] = function (t) { return filterInterfaces(ctx, t); }, | ||
_a[graphql_1.Kind.OBJECT_TYPE_DEFINITION] = function (t) { return filterInterfaces(ctx, t); }, | ||
_a[graphql_1.Kind.OBJECT_TYPE_EXTENSION] = function (t) { return filterInterfaces(ctx, t); }, | ||
_a[graphql_1.Kind.INTERFACE_TYPE_EXTENSION] = function (t) { return filterInterfaces(ctx, t); }, | ||
_a)); | ||
function filterNonGqlInterfaces(ctx, definitions) { | ||
return definitions.map(function (def) { | ||
if (def.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION || | ||
def.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION || | ||
def.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || | ||
def.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) { | ||
return filterInterfaces(ctx, def); | ||
} | ||
return def; | ||
}); | ||
} | ||
@@ -32,0 +34,0 @@ exports.filterNonGqlInterfaces = filterNonGqlInterfaces; |
@@ -1,10 +0,10 @@ | ||
import { DocumentNode } from "graphql"; | ||
import { DefinitionNode } from "graphql"; | ||
import { TypeContext } from "../TypeContext"; | ||
import { DiagnosticsResult } from "../utils/DiagnosticError"; | ||
import { TypeContext } from "../TypeContext"; | ||
/** | ||
* During the extraction process when we observe a type reference in a GraphQL | ||
* position we don't actually resolve that to its GraphQL type name during | ||
* extraction. Instead we rely on this transform to resolve those references and | ||
* ensure that they point to `@gql` types. | ||
* During extraction we are operating purely syntactically, so we don't actually know | ||
* which types are being referred to. This function resolves those references. | ||
* | ||
* It also materializes any generic type references into concrete types. | ||
*/ | ||
export declare function resolveTypes(ctx: TypeContext, doc: DocumentNode): DiagnosticsResult<DocumentNode>; | ||
export declare function resolveTypes(ctx: TypeContext, definitions: Array<DefinitionNode>): DiagnosticsResult<DefinitionNode[]>; |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __values = (this && this.__values) || function(o) { | ||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
if (m) return m.call(o); | ||
if (o && typeof o.length === "number") return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.resolveTypes = void 0; | ||
var graphql_1 = require("graphql"); | ||
var GraphQLConstructor_1 = require("../GraphQLConstructor"); | ||
var ts = require("typescript"); | ||
var Result_1 = require("../utils/Result"); | ||
var DiagnosticError_1 = require("../utils/DiagnosticError"); | ||
var helpers_1 = require("../utils/helpers"); | ||
var E = require("../Errors"); | ||
/** | ||
* During the extraction process when we observe a type reference in a GraphQL | ||
* position we don't actually resolve that to its GraphQL type name during | ||
* extraction. Instead we rely on this transform to resolve those references and | ||
* ensure that they point to `@gql` types. | ||
* During extraction we are operating purely syntactically, so we don't actually know | ||
* which types are being referred to. This function resolves those references. | ||
* | ||
* It also materializes any generic type references into concrete types. | ||
*/ | ||
function resolveTypes(ctx, doc) { | ||
var _a; | ||
var errors = []; | ||
var newDoc = (0, graphql_1.visit)(doc, (_a = {}, | ||
_a[graphql_1.Kind.NAME] = function (t) { | ||
var namedTypeResult = ctx.resolveNamedType(t); | ||
if (namedTypeResult.kind === "ERROR") { | ||
errors.push(namedTypeResult.err); | ||
return t; | ||
function resolveTypes(ctx, definitions) { | ||
var templateExtractor = new TemplateExtractor(ctx); | ||
return templateExtractor.materializeGenericTypeReferences(definitions); | ||
} | ||
exports.resolveTypes = resolveTypes; | ||
/** | ||
* Template extraction happens in two phases and resolves named type references | ||
* as a side effect. | ||
* | ||
* 1. We walk all declarations checking if they contain type references in | ||
* GraphQL positions which point back to the declaration's type parameters. If | ||
* so, they are considered templates and are removed from the list of "real" | ||
* declarations. | ||
* 2. We walk the remaining "real" declarations and resolve any type references, | ||
* if a reference refers to a template we first validate and resolve its type | ||
* arguments and then use those as inputs to materialize a new type to match | ||
* those type arguments. | ||
* | ||
* ## Two Types of Recursion | ||
* | ||
* 1. Type arguments may themselves be parameterized, and so we must | ||
* process generic type references recursively in a depth-first manner. | ||
* | ||
* 2. When materializing templates we may encounter more parameterized | ||
* references to other templates. In this way, template materialization can be | ||
* recursive, and we must take care to avoid infinite loops. We must also take | ||
* care to correctly track our scope such that type references in templates | ||
* which refer to generic types resolve to the correct type. | ||
*/ | ||
var TemplateExtractor = /** @class */ (function () { | ||
function TemplateExtractor(ctx) { | ||
this.ctx = ctx; | ||
this._templates = new Map(); | ||
this._definitions = []; | ||
this._definedTemplates = new Set(); | ||
this._errors = []; | ||
} | ||
TemplateExtractor.prototype.materializeGenericTypeReferences = function (definitions) { | ||
var _this = this; | ||
// We filter out all template declarations and index them as a first pass. | ||
var filtered = definitions.filter(function (definition) { | ||
return !_this.maybeExtractAsTemplate(definition); | ||
}); | ||
// Now we can visit the remaining "real" definitions and materialize any | ||
// generic type references. | ||
filtered.forEach(function (definition) { | ||
_this._definitions.push(_this.materializeTemplatesForNode(definition)); | ||
}); | ||
if (this._errors.length > 0) { | ||
return (0, Result_1.err)(this._errors); | ||
} | ||
return (0, Result_1.ok)(this._definitions); | ||
}; | ||
/** | ||
* Walks GraphQL ASTs and expands generic types into their concrete types | ||
* adding their materialized definitions to the `_definitions` array as we go. | ||
* | ||
* **Note:** Here we also detect generics being used as members of a union and | ||
* report that as an error. | ||
*/ | ||
TemplateExtractor.prototype.materializeTemplatesForNode = function (node) { | ||
var _a; | ||
var _this = this; | ||
return (0, graphql_1.visit)(node, (_a = {}, | ||
_a[graphql_1.Kind.NAME] = function (node) { | ||
var referenceNode = _this.getReferenceNode(node); | ||
if (referenceNode == null) | ||
return node; | ||
var name = _this.resolveTypeReferenceOrReport(referenceNode); | ||
if (name == null) | ||
return node; | ||
return __assign(__assign({}, node), { value: name }); | ||
}, | ||
_a)); | ||
}; | ||
TemplateExtractor.prototype.resolveTypeReferenceOrReport = function (node, generics) { | ||
var e_1, _a; | ||
var _b; | ||
var declaration = this.asNullable(this.ctx.tsDeclarationForTsName(node.typeName)); | ||
if (declaration == null) | ||
return null; | ||
if (generics != null) { | ||
var genericName = generics.get(declaration); | ||
if (genericName != null) { | ||
return genericName; | ||
} | ||
return namedTypeResult.value; | ||
}, | ||
_a)); | ||
if (errors.length > 0) { | ||
return (0, Result_1.err)(errors); | ||
} | ||
var template = this._templates.get(declaration); | ||
if (template != null) { | ||
var templateName = template.declarationTemplate.name.value; | ||
var typeArguments = (_b = node.typeArguments) !== null && _b !== void 0 ? _b : []; | ||
var genericIndexes = new Map(); | ||
try { | ||
for (var _c = __values(template.genericNodes), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var _e = __read(_d.value, 2), node_1 = _e[0], index = _e[1]; | ||
genericIndexes.set(index, node_1); | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
var names = []; | ||
for (var i = 0; i < template.typeParameters.length; i++) { | ||
var exampleGenericNode = genericIndexes.get(i); | ||
if (exampleGenericNode == null) { | ||
continue; | ||
} | ||
var param = template.typeParameters[i]; | ||
var paramName = param.name.text; | ||
var arg = typeArguments[i]; | ||
if (arg == null) { | ||
return this.report(node, E.missingGenericType(templateName, paramName), [ | ||
(0, DiagnosticError_1.tsErr)(param, "Type parameter `".concat(paramName, "` is defined here")), | ||
(0, DiagnosticError_1.tsErr)(exampleGenericNode, "and expects a GraphQL type because it was used in a GraphQL position here."), | ||
]); | ||
} | ||
if (!ts.isTypeReferenceNode(arg)) { | ||
return this.report(arg, E.nonGraphQLGenericType(templateName, paramName), [ | ||
(0, DiagnosticError_1.tsErr)(param, "Type parameter `".concat(paramName, "` is defined here")), | ||
(0, DiagnosticError_1.tsErr)(exampleGenericNode, "and expects a GraphQL type because it was used in a GraphQL position here."), | ||
]); | ||
} | ||
var name = this.resolveTypeReferenceOrReport(arg, generics); | ||
// resolveTypeReference will report an error if the definition is not found. | ||
if (name == null) | ||
return null; | ||
names.push(name); | ||
} | ||
return this.materializeTemplate(node, names, template); | ||
} | ||
var nameResult = this.ctx.gqlNameForTsName(node.typeName); | ||
return this.asNullable(nameResult); | ||
}; | ||
TemplateExtractor.prototype.materializeTemplate = function (referenceLoc, typeParams, template) { | ||
var e_2, _a, _b; | ||
var _this = this; | ||
var paramsPrefix = typeParams.join(""); | ||
var derivedName = paramsPrefix + template.declarationTemplate.name.value; | ||
if (this._definedTemplates.has(derivedName)) { | ||
// We've either already materialized this permutation or we're in the middle | ||
// of doing so. | ||
return derivedName; | ||
} | ||
this._definedTemplates.add(derivedName); | ||
var genericsContext = new Map(); | ||
try { | ||
for (var _c = __values(new Set(template.genericNodes.values())), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var i = _d.value; | ||
var name = (0, helpers_1.nullThrows)(typeParams[i]); | ||
var param = (0, helpers_1.nullThrows)(template.typeParameters[i]); | ||
genericsContext.set(param, name); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
var gqlLoc = (0, GraphQLConstructor_1.loc)(referenceLoc); | ||
var original = template.declarationTemplate; | ||
var renamedDefinition = renameDefinition(original, derivedName, gqlLoc); | ||
var definition = (0, graphql_1.visit)(renamedDefinition, (_b = {}, | ||
_b[graphql_1.Kind.NAMED_TYPE] = function (node) { | ||
var referenceNode = _this.getReferenceNode(node.name); | ||
if (referenceNode == null) | ||
return node; | ||
var name = _this.resolveTypeReferenceOrReport(referenceNode, genericsContext); | ||
if (name == null) | ||
return node; | ||
return __assign(__assign({}, node), { name: __assign(__assign({}, node.name), { value: name }) }); | ||
}, | ||
_b)); | ||
this._definitions.push(definition); | ||
return derivedName; | ||
}; | ||
TemplateExtractor.prototype.maybeExtractAsTemplate = function (definition) { | ||
var _a; | ||
var _this = this; | ||
if (!mayReferenceGenerics(definition)) { | ||
return false; | ||
} | ||
var declaration = this.ctx.tsDeclarationForGqlDefinition(definition); | ||
var typeParams = getTypeParameters(declaration); | ||
if (typeParams == null || typeParams.length === 0) { | ||
return false; | ||
} | ||
var genericNodes = new Map(); | ||
(0, graphql_1.visit)(definition, (_a = {}, | ||
_a[graphql_1.Kind.NAMED_TYPE] = function (node) { | ||
var e_3, _a; | ||
var referenceNode = _this.getReferenceNode(node.name); | ||
if (referenceNode == null) | ||
return; | ||
var references = findAllReferences(referenceNode); | ||
try { | ||
for (var references_1 = __values(references), references_1_1 = references_1.next(); !references_1_1.done; references_1_1 = references_1.next()) { | ||
var reference = references_1_1.value; | ||
var declarationResult = _this.ctx.tsDeclarationForTsName(reference.typeName); | ||
if (declarationResult.kind === "ERROR") { | ||
_this._errors.push(declarationResult.err); | ||
return; | ||
} | ||
var declaration_1 = declarationResult.value; | ||
// If the type points to a type param... | ||
if (!ts.isTypeParameterDeclaration(declaration_1)) { | ||
return; | ||
} | ||
// And it's one of our parent type's type params... | ||
var genericIndex = typeParams.indexOf(declaration_1); | ||
if (genericIndex !== -1) { | ||
genericNodes.set(reference.typeName, genericIndex); | ||
} | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (references_1_1 && !references_1_1.done && (_a = references_1.return)) _a.call(references_1); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
}, | ||
_a)); | ||
if (genericNodes.size === 0) { | ||
return false; | ||
} | ||
if (definition.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION) { | ||
if (definition.interfaces && definition.interfaces.length > 0) { | ||
var loc_1 = (0, helpers_1.loc)(definition.interfaces[0].name); | ||
this._errors.push((0, DiagnosticError_1.gqlErr)(loc_1, E.genericTypeImplementsInterface())); | ||
} | ||
} | ||
this._templates.set(declaration, { | ||
declarationTemplate: definition, | ||
genericNodes: genericNodes, | ||
typeParameters: typeParams, | ||
}); | ||
return true; | ||
}; | ||
// --- Helpers --- | ||
TemplateExtractor.prototype.getReferenceNode = function (name) { | ||
var node = this.ctx.getEntityName(name); | ||
if (node == null) { | ||
return null; | ||
} | ||
if (ts.isTypeReferenceNode(node.parent)) | ||
return node.parent; | ||
// Heritage clauses are not actually type references since they have | ||
// runtime semantics. Instead they are an "ExpressionWithTypeArguments" | ||
if (ts.isExpressionWithTypeArguments(node.parent) && | ||
ts.isIdentifier(node.parent.expression)) { | ||
return new EntityNameWithTypeArguments(node.parent.expression, node.parent.typeArguments); | ||
} | ||
return null; | ||
}; | ||
TemplateExtractor.prototype.asNullable = function (result) { | ||
if (result.kind === "ERROR") { | ||
this._errors.push(result.err); | ||
return null; | ||
} | ||
return result.value; | ||
}; | ||
TemplateExtractor.prototype.report = function (node, message, relatedInformation) { | ||
this._errors.push((0, DiagnosticError_1.tsErr)(node, message, relatedInformation)); | ||
return null; | ||
}; | ||
return TemplateExtractor; | ||
}()); | ||
function mayReferenceGenerics(definition) { | ||
return (definition.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || | ||
definition.kind === graphql_1.Kind.UNION_TYPE_DEFINITION || | ||
definition.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION || | ||
definition.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION); | ||
} | ||
function getTypeParameters(declaration) { | ||
var _a, _b, _c; | ||
if (ts.isTypeAliasDeclaration(declaration)) { | ||
return (_a = declaration.typeParameters) !== null && _a !== void 0 ? _a : null; | ||
} | ||
return (0, Result_1.ok)(newDoc); | ||
if (ts.isInterfaceDeclaration(declaration)) { | ||
return (_b = declaration.typeParameters) !== null && _b !== void 0 ? _b : null; | ||
} | ||
if (ts.isClassDeclaration(declaration)) { | ||
return (_c = declaration.typeParameters) !== null && _c !== void 0 ? _c : null; | ||
} | ||
// TODO: Handle other types of declarations which have generics. | ||
return null; | ||
} | ||
exports.resolveTypes = resolveTypes; | ||
/** | ||
* Abstraction that can be derived from a typeReference or an expression with | ||
* type arguments. Gives us a common shape which can model both a | ||
* `ts.TypeReferenceNode` and a `ts.ExpressionWithTypeArguments` while also | ||
* being able to use it to report diagnostics | ||
*/ | ||
var EntityNameWithTypeArguments = /** @class */ (function () { | ||
function EntityNameWithTypeArguments(typeName, typeArguments) { | ||
this.typeName = typeName; | ||
this.typeArguments = typeArguments; | ||
} | ||
EntityNameWithTypeArguments.prototype.getStart = function () { | ||
return this.typeName.getStart(); | ||
}; | ||
EntityNameWithTypeArguments.prototype.getEnd = function () { | ||
if (this.typeArguments == null || this.typeArguments.length === 0) { | ||
return this.typeName.getEnd(); | ||
} | ||
return this.typeArguments[this.typeArguments.length - 1].getEnd(); | ||
}; | ||
EntityNameWithTypeArguments.prototype.getSourceFile = function () { | ||
return this.typeName.getSourceFile(); | ||
}; | ||
return EntityNameWithTypeArguments; | ||
}()); | ||
// Given a type reference, recursively walk its type arguments and return all | ||
// type references in the current scope. | ||
function findAllReferences(node) { | ||
var e_4, _a; | ||
var references = []; | ||
if (node.typeArguments != null) { | ||
try { | ||
for (var _b = __values(node.typeArguments), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var arg = _c.value; | ||
if (ts.isTypeReferenceNode(arg)) { | ||
(0, helpers_1.extend)(references, findAllReferences(arg)); | ||
} | ||
} | ||
} | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
} | ||
references.push(node); | ||
return references; | ||
} | ||
function renameDefinition(original, newName, loc) { | ||
var name = __assign(__assign({}, original.name), { value: newName, loc: loc }); | ||
return __assign(__assign({}, original), { loc: loc, name: name, wasSynthesized: true }); | ||
} |
@@ -9,2 +9,3 @@ "use strict"; | ||
var helpers_1 = require("../utils/helpers"); | ||
var TAG_REGEX = /@(gql)|(killsParentOnException)/i; | ||
// Given a ts.Program, extract a set of ExtractionSnapshots from it. | ||
@@ -17,3 +18,3 @@ // In the future this part might be able to be incremental, were we only run extraction | ||
// If the file doesn't contain any GraphQL definitions, skip it. | ||
if (!/@gql/i.test(sourceFile.text)) { | ||
if (!TAG_REGEX.test(sourceFile.text)) { | ||
return false; | ||
@@ -20,0 +21,0 @@ } |
@@ -1,4 +0,4 @@ | ||
import { NameNode } from "graphql"; | ||
import { InputObjectTypeDefinitionNode, InterfaceTypeDefinitionNode, NameNode, ObjectTypeDefinitionNode, UnionTypeDefinitionNode } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { DiagnosticResult, DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import { DiagnosticResult } from "./utils/DiagnosticError"; | ||
import { ExtractionSnapshot } from "./Extractor"; | ||
@@ -10,2 +10,3 @@ export declare const UNRESOLVED_REFERENCE_NAME = "__UNRESOLVED_REFERENCE__"; | ||
}; | ||
type TsIdentifier = number; | ||
/** | ||
@@ -25,13 +26,20 @@ * Used to track TypeScript references. | ||
checker: ts.TypeChecker; | ||
_symbolToName: Map<ts.Symbol, NameDefinition>; | ||
_unresolvedTypes: Map<NameNode, ts.Symbol>; | ||
_declarationToName: Map<ts.Declaration, NameDefinition>; | ||
_unresolvedNodes: Map<TsIdentifier, ts.EntityName>; | ||
_idToDeclaration: Map<TsIdentifier, ts.Declaration>; | ||
static fromSnapshot(checker: ts.TypeChecker, snapshot: ExtractionSnapshot): TypeContext; | ||
constructor(checker: ts.TypeChecker); | ||
_recordTypeName(node: ts.Node, name: NameNode, kind: NameDefinition["kind"]): void; | ||
_markUnresolvedType(node: ts.Node, name: NameNode): void; | ||
private _recordTypeName; | ||
private _markUnresolvedType; | ||
findSymbolDeclaration(startSymbol: ts.Symbol): ts.Declaration | null; | ||
resolveSymbol(startSymbol: ts.Symbol): ts.Symbol; | ||
resolveNamedType(unresolved: NameNode): DiagnosticResult<NameNode>; | ||
private resolveSymbol; | ||
resolveUnresolvedNamedType(unresolved: NameNode): DiagnosticResult<NameNode>; | ||
unresolvedNameIsGraphQL(unresolved: NameNode): boolean; | ||
getNameDefinition(nameNode: NameNode): DiagnosticsResult<NameDefinition>; | ||
gqlNameDefinitionForGqlName(nameNode: NameNode): DiagnosticResult<NameDefinition>; | ||
gqlNameForTsName(node: ts.EntityName): DiagnosticResult<string>; | ||
private maybeTsDeclarationForTsName; | ||
tsDeclarationForTsName(node: ts.EntityName): DiagnosticResult<ts.Declaration>; | ||
tsDeclarationForGqlDefinition(definition: ObjectTypeDefinitionNode | UnionTypeDefinitionNode | InputObjectTypeDefinitionNode | InterfaceTypeDefinitionNode): ts.Declaration; | ||
getEntityName(name: NameNode): ts.EntityName | null; | ||
} | ||
export {}; |
@@ -62,4 +62,5 @@ "use strict"; | ||
function TypeContext(checker) { | ||
this._symbolToName = new Map(); | ||
this._unresolvedTypes = new Map(); | ||
this._declarationToName = new Map(); | ||
this._unresolvedNodes = new Map(); | ||
this._idToDeclaration = new Map(); | ||
this.checker = checker; | ||
@@ -101,21 +102,8 @@ } | ||
TypeContext.prototype._recordTypeName = function (node, name, kind) { | ||
var symbol = this.checker.getSymbolAtLocation(node); | ||
if (symbol == null) { | ||
// FIXME: Make this a diagnostic | ||
throw new Error("Could not resolve type reference. You probably have a TypeScript error."); | ||
} | ||
if (this._symbolToName.has(symbol)) { | ||
// Ensure we never try to record the same name twice. | ||
throw new Error("Unexpected double recording of typename."); | ||
} | ||
this._symbolToName.set(symbol, { name: name, kind: kind }); | ||
this._idToDeclaration.set(name.tsIdentifier, node); | ||
this._declarationToName.set(node, { name: name, kind: kind }); | ||
}; | ||
// Record that a type reference `node` | ||
// Record that a type references `node` | ||
TypeContext.prototype._markUnresolvedType = function (node, name) { | ||
var symbol = this.checker.getSymbolAtLocation(node); | ||
if (symbol == null) { | ||
// | ||
throw new Error("Could not resolve type reference. You probably have a TypeScript error."); | ||
} | ||
this._unresolvedTypes.set(name, this.resolveSymbol(symbol)); | ||
this._unresolvedNodes.set(name.tsIdentifier, node); | ||
}; | ||
@@ -142,12 +130,18 @@ TypeContext.prototype.findSymbolDeclaration = function (startSymbol) { | ||
}; | ||
TypeContext.prototype.resolveNamedType = function (unresolved) { | ||
var symbol = this._unresolvedTypes.get(unresolved); | ||
if (symbol == null) { | ||
if (unresolved.value === exports.UNRESOLVED_REFERENCE_NAME) { | ||
// This is a logic error on our side. | ||
throw new Error("Unexpected unresolved reference name."); | ||
} | ||
TypeContext.prototype.resolveUnresolvedNamedType = function (unresolved) { | ||
if (unresolved.value !== exports.UNRESOLVED_REFERENCE_NAME) { | ||
return (0, Result_1.ok)(unresolved); | ||
} | ||
var nameDefinition = this._symbolToName.get(symbol); | ||
var typeReference = this.getEntityName(unresolved); | ||
if (typeReference == null) { | ||
throw new Error("Unexpected unresolved reference name."); | ||
} | ||
var declarationResult = this.tsDeclarationForTsName(typeReference); | ||
if (declarationResult.kind === "ERROR") { | ||
return (0, Result_1.err)(declarationResult.err); | ||
} | ||
if (ts.isTypeParameterDeclaration(declarationResult.value)) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(unresolved), "Type parameters are not supported in this context.")); | ||
} | ||
var nameDefinition = this._declarationToName.get(declarationResult.value); | ||
if (nameDefinition == null) { | ||
@@ -159,25 +153,74 @@ return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(unresolved), E.unresolvedTypeReference())); | ||
TypeContext.prototype.unresolvedNameIsGraphQL = function (unresolved) { | ||
var symbol = this._unresolvedTypes.get(unresolved); | ||
return symbol != null && this._symbolToName.has(symbol); | ||
var referenceNode = this.getEntityName(unresolved); | ||
if (referenceNode == null) | ||
return false; | ||
var declaration = this.maybeTsDeclarationForTsName(referenceNode); | ||
if (declaration == null) | ||
return false; | ||
return this._declarationToName.has(declaration); | ||
}; | ||
// TODO: Merge this with resolveNamedType | ||
TypeContext.prototype.getNameDefinition = function (nameNode) { | ||
var typeNameResult = this.resolveNamedType(nameNode); | ||
if (typeNameResult.kind === "ERROR") { | ||
return (0, Result_1.err)([typeNameResult.err]); | ||
TypeContext.prototype.gqlNameDefinitionForGqlName = function (nameNode) { | ||
var referenceNode = this.getEntityName(nameNode); | ||
if (referenceNode == null) { | ||
throw new Error("Expected to find reference node for name node."); | ||
} | ||
var symbol = this._unresolvedTypes.get(nameNode); | ||
if (symbol == null) { | ||
// This should have already been handled by resolveNamedType | ||
throw new Error("Expected to find unresolved type."); | ||
var declaration = this.maybeTsDeclarationForTsName(referenceNode); | ||
if (declaration == null) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(nameNode), E.unresolvedTypeReference())); | ||
} | ||
var nameDefinition = this._symbolToName.get(symbol); | ||
if (nameDefinition == null) { | ||
// This should have already been handled by resolveNamedType | ||
var definition = this._declarationToName.get(declaration); | ||
if (definition == null) { | ||
throw new Error("Expected to find name definition."); | ||
} | ||
return (0, Result_1.ok)(nameDefinition); | ||
return (0, Result_1.ok)(definition); | ||
}; | ||
// Note! This assumes you have already handled any type parameters. | ||
TypeContext.prototype.gqlNameForTsName = function (node) { | ||
var declarationResult = this.tsDeclarationForTsName(node); | ||
if (declarationResult.kind === "ERROR") { | ||
return (0, Result_1.err)(declarationResult.err); | ||
} | ||
if (ts.isTypeParameterDeclaration(declarationResult.value)) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, "Type parameter not valid", [ | ||
(0, DiagnosticError_1.tsErr)(declarationResult.value, "Defined here"), | ||
])); | ||
} | ||
var nameDefinition = this._declarationToName.get(declarationResult.value); | ||
if (nameDefinition == null) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, E.unresolvedTypeReference())); | ||
} | ||
return (0, Result_1.ok)(nameDefinition.name.value); | ||
}; | ||
TypeContext.prototype.maybeTsDeclarationForTsName = function (node) { | ||
var symbol = this.checker.getSymbolAtLocation(node); | ||
if (symbol == null) { | ||
return null; | ||
} | ||
return this.findSymbolDeclaration(symbol); | ||
}; | ||
TypeContext.prototype.tsDeclarationForTsName = function (node) { | ||
var declaration = this.maybeTsDeclarationForTsName(node); | ||
if (!declaration) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, E.unresolvedTypeReference())); | ||
} | ||
return (0, Result_1.ok)(declaration); | ||
}; | ||
TypeContext.prototype.tsDeclarationForGqlDefinition = function (definition) { | ||
var name = definition.name; | ||
var declaration = this._idToDeclaration.get(name.tsIdentifier); | ||
if (!declaration) { | ||
throw new Error("Could not find declaration for ".concat(name.value)); | ||
} | ||
return declaration; | ||
}; | ||
TypeContext.prototype.getEntityName = function (name) { | ||
var _a; | ||
var entityName = (_a = this._unresolvedNodes.get(name.tsIdentifier)) !== null && _a !== void 0 ? _a : null; | ||
if (entityName == null && name.value === exports.UNRESOLVED_REFERENCE_NAME) { | ||
throw new Error("Expected unresolved reference to have a node."); | ||
} | ||
return entityName; | ||
}; | ||
return TypeContext; | ||
}()); | ||
exports.TypeContext = TypeContext; |
@@ -19,4 +19,17 @@ import { GraphQLError, Location, Source } from "graphql"; | ||
export declare function gqlRelated(loc: Location, message: string): ts.DiagnosticRelatedInformation; | ||
export declare function tsErr(node: ts.Node, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.DiagnosticWithLocation; | ||
export declare function rangeErr(file: ts.SourceFile, commentRange: ts.CommentRange, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.DiagnosticWithLocation; | ||
/** | ||
* A generic version of the methods on ts.Node that we need | ||
* to create diagnostics. | ||
* | ||
* This interface allows us to create diagnostics from our | ||
* own classes. | ||
*/ | ||
export interface TsLocatableNode { | ||
getStart(): number; | ||
getEnd(): number; | ||
getSourceFile(): ts.SourceFile; | ||
} | ||
export declare function tsErr(node: TsLocatableNode, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.DiagnosticWithLocation; | ||
export declare function tsRelated(node: ts.Node, message: string): ts.DiagnosticRelatedInformation; | ||
export declare function graphqlSourceToSourceFile(source: Source): ts.SourceFile; |
@@ -30,3 +30,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.graphqlSourceToSourceFile = exports.tsRelated = exports.tsErr = exports.gqlRelated = exports.gqlErr = exports.graphQlErrorToDiagnostic = exports.FAKE_ERROR_CODE = exports.ReportableDiagnostics = void 0; | ||
exports.graphqlSourceToSourceFile = exports.tsRelated = exports.tsErr = exports.rangeErr = exports.gqlRelated = exports.gqlErr = exports.graphQlErrorToDiagnostic = exports.FAKE_ERROR_CODE = exports.ReportableDiagnostics = void 0; | ||
var ts = require("typescript"); | ||
@@ -149,2 +149,17 @@ var ReportableDiagnostics = /** @class */ (function () { | ||
exports.gqlRelated = gqlRelated; | ||
function rangeErr(file, commentRange, message, relatedInformation) { | ||
var start = commentRange.pos; | ||
var length = commentRange.end - commentRange.pos; | ||
return { | ||
messageText: message, | ||
file: file, | ||
code: exports.FAKE_ERROR_CODE, | ||
category: ts.DiagnosticCategory.Error, | ||
start: start, | ||
length: length, | ||
relatedInformation: relatedInformation, | ||
source: "Grats", | ||
}; | ||
} | ||
exports.rangeErr = rangeErr; | ||
function tsErr(node, message, relatedInformation) { | ||
@@ -151,0 +166,0 @@ var start = node.getStart(); |
@@ -15,1 +15,3 @@ import { Location } from "graphql"; | ||
}): T; | ||
export declare function uniqueId(): number; | ||
export declare function nullThrows<T>(value: T | null | undefined): T; |
@@ -14,3 +14,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.astNode = exports.loc = exports.extend = exports.DefaultMap = void 0; | ||
exports.nullThrows = exports.uniqueId = exports.astNode = exports.loc = exports.extend = exports.DefaultMap = void 0; | ||
var DefaultMap = /** @class */ (function () { | ||
@@ -63,1 +63,13 @@ function DefaultMap(getDefault) { | ||
exports.astNode = astNode; | ||
var i = 0; | ||
function uniqueId() { | ||
return i++; | ||
} | ||
exports.uniqueId = uniqueId; | ||
function nullThrows(value) { | ||
if (value == null) { | ||
throw new Error("Grats Error. Expected value to be non-nullish. This error represents an error in Grats. Please report it."); | ||
} | ||
return value; | ||
} | ||
exports.nullThrows = nullThrows; |
import { DocumentNode } from "graphql"; | ||
import { DiagnosticsResult } from "../utils/DiagnosticError"; | ||
/** | ||
* Ensure that all fields on `Subscription` return an AsyncIterable, and that no other | ||
* fields do. | ||
* Ensure that all fields on `Subscription` return an AsyncIterable and transform | ||
* the return type of subscription fields to not treat AsyncIterable as as list type. | ||
*/ | ||
export declare function validateAsyncIterable(doc: DocumentNode): DiagnosticsResult<DocumentNode>; |
"use strict"; | ||
var __values = (this && this.__values) || function(o) { | ||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
if (m) return m.call(o); | ||
if (o && typeof o.length === "number") return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
return __assign.apply(this, arguments); | ||
}; | ||
@@ -19,7 +19,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); | ||
var E = require("../Errors"); | ||
var metadataDirectives_1 = require("../metadataDirectives"); | ||
var helpers_1 = require("../utils/helpers"); | ||
/** | ||
* Ensure that all fields on `Subscription` return an AsyncIterable, and that no other | ||
* fields do. | ||
* Ensure that all fields on `Subscription` return an AsyncIterable and transform | ||
* the return type of subscription fields to not treat AsyncIterable as as list type. | ||
*/ | ||
@@ -29,9 +28,28 @@ function validateAsyncIterable(doc) { | ||
var errors = []; | ||
var visitNode = function (t) { | ||
var validateFieldsResult = validateField(t); | ||
if (validateFieldsResult != null) { | ||
errors.push(validateFieldsResult); | ||
var visitNode = { | ||
enter: function (t) { | ||
// Note: We assume the default name is used here. When custom operation types are supported | ||
// we'll need to update this. | ||
if (t.name.value !== "Subscription") { | ||
// Don't visit nodes that aren't the Subscription type. | ||
return false; | ||
} | ||
}, | ||
}; | ||
var visitSubscriptionField = function (field) { | ||
var inner = innerType(field.type); // Remove any non-null wrapper types | ||
if (inner.kind !== graphql_1.Kind.LIST_TYPE || !inner.isAsyncIterable) { | ||
errors.push((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(field.type), E.subscriptionFieldNotAsyncIterable())); | ||
return field; | ||
} | ||
var itemType = inner.type; | ||
// If either field.type or item type is nullable, the field should be nullable | ||
if (isNullable(field.type) || isNullable(itemType)) { | ||
var innerInner = innerType(itemType); | ||
return __assign(__assign({}, field), { type: innerInner }); | ||
} | ||
// If _both_ are non-nullable, we will preserve the non-nullability. | ||
return __assign(__assign({}, field), { type: itemType }); | ||
}; | ||
(0, graphql_1.visit)(doc, (_a = {}, | ||
var newDoc = (0, graphql_1.visit)(doc, (_a = {}, | ||
_a[graphql_1.Kind.INTERFACE_TYPE_DEFINITION] = visitNode, | ||
@@ -41,2 +59,3 @@ _a[graphql_1.Kind.INTERFACE_TYPE_EXTENSION] = visitNode, | ||
_a[graphql_1.Kind.OBJECT_TYPE_EXTENSION] = visitNode, | ||
_a[graphql_1.Kind.FIELD_DEFINITION] = visitSubscriptionField, | ||
_a)); | ||
@@ -46,38 +65,13 @@ if (errors.length > 0) { | ||
} | ||
return (0, Result_1.ok)(doc); | ||
return (0, Result_1.ok)(newDoc); | ||
} | ||
exports.validateAsyncIterable = validateAsyncIterable; | ||
function validateField(t) { | ||
var e_1, _a; | ||
var _b; | ||
if (t.fields == null) | ||
return; | ||
// Note: We assume the default name is used here. When custom operation types are supported | ||
// we'll need to update this. | ||
var isSubscription = t.name.value === "Subscription" && | ||
(t.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || | ||
t.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION); | ||
try { | ||
for (var _c = __values(t.fields), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var field = _d.value; | ||
var asyncDirective = (_b = field.directives) === null || _b === void 0 ? void 0 : _b.find(function (directive) { return directive.name.value === metadataDirectives_1.ASYNC_ITERABLE_TYPE_DIRECTIVE; }); | ||
if (isSubscription && asyncDirective == null) { | ||
return (0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(field.type), E.subscriptionFieldNotAsyncIterable()); | ||
} | ||
if (!isSubscription && asyncDirective != null) { | ||
if (asyncDirective.loc == null) { | ||
throw new Error("Expected asyncDirective to have a location."); | ||
} | ||
return (0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(asyncDirective), // Directive location is the AsyncIterable type. | ||
E.nonSubscriptionFieldAsyncIterable()); | ||
} | ||
} | ||
function innerType(type) { | ||
if (type.kind === graphql_1.Kind.NON_NULL_TYPE) { | ||
return innerType(type.type); | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
return type; | ||
} | ||
function isNullable(t) { | ||
return t.kind !== graphql_1.Kind.NON_NULL_TYPE; | ||
} |
@@ -18,2 +18,4 @@ "use strict"; | ||
var Result_1 = require("../utils/Result"); | ||
var helpers_1 = require("../utils/helpers"); | ||
var E = require("../Errors"); | ||
/** | ||
@@ -25,4 +27,3 @@ * Ensure that every type which implements an interface or is a member of a | ||
var e_1, _a, e_2, _b; | ||
var _c, _d; | ||
var typenameDiagnostics = []; | ||
var errors = []; | ||
var abstractTypes = Object.values(schema.getTypeMap()).filter(graphql_1.isAbstractType); | ||
@@ -37,9 +38,15 @@ try { | ||
var implementor = typeImplementors_1_1.value; | ||
if (!hasTypename.has(implementor.name)) { | ||
var loc = (_d = (_c = implementor.astNode) === null || _c === void 0 ? void 0 : _c.name) === null || _d === void 0 ? void 0 : _d.loc; | ||
if (loc == null) { | ||
throw new Error("Grats expected the parsed type `".concat(implementor.name, "` to have location information. This is a bug in Grats. Please report it.")); | ||
} | ||
typenameDiagnostics.push((0, DiagnosticError_1.gqlErr)(loc, "Missing __typename on `".concat(implementor.name, "`. The type `").concat(type.name, "` is used in a union or interface, so it must have a `__typename` field."))); | ||
var ast = (0, helpers_1.nullThrows)(implementor.astNode); | ||
// Synthesized type cannot guarantee that they have the correct __typename field, so we | ||
// prevent their use in interfaces and unions. | ||
if (ast.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION && ast.wasSynthesized) { | ||
var message = type instanceof graphql_1.GraphQLInterfaceType | ||
? E.genericTypeImplementsInterface() | ||
: E.genericTypeUsedAsUnionMember(); | ||
errors.push((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(ast.name), message)); | ||
} | ||
else if (!hasTypename.has(implementor.name)) { | ||
var err_1 = (0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(ast.name), E.concreteTypeMissingTypename(implementor.name)); | ||
errors.push(err_1); | ||
} | ||
} | ||
@@ -63,4 +70,4 @@ } | ||
} | ||
if (typenameDiagnostics.length > 0) { | ||
return (0, Result_1.err)(typenameDiagnostics); | ||
if (errors.length > 0) { | ||
return (0, Result_1.err)(errors); | ||
} | ||
@@ -67,0 +74,0 @@ return (0, Result_1.ok)(schema); |
{ | ||
"name": "grats", | ||
"version": "0.0.0-main-afbec52b", | ||
"version": "0.0.0-main-b0fafefe", | ||
"main": "dist/src/index.js", | ||
@@ -9,3 +9,4 @@ "bin": "dist/src/cli.js", | ||
"files": [ | ||
"dist" | ||
"dist", | ||
"!dist/src/tests" | ||
], | ||
@@ -40,6 +41,26 @@ "dependencies": { | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/captbaritone/grats/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/captbaritone/grats.git" | ||
}, | ||
"author": { | ||
"name": "Jordan Eldredge", | ||
"email": "jordan@jordaneldredge.com", | ||
"url": "https://jordaneldredge.com" | ||
}, | ||
"keywords": [ | ||
"graphql", | ||
"typescript", | ||
"resolvers", | ||
"schema", | ||
"code-first", | ||
"implementation-first" | ||
], | ||
"scripts": { | ||
"test": "ts-node src/tests/test.ts", | ||
"integration-tests": "node src/tests/integration.mjs", | ||
"build": "tsc --build", | ||
"build": "rm -rf dist/ && tsc --build", | ||
"format": "prettier . --write", | ||
@@ -46,0 +67,0 @@ "lint": "eslint . && prettier . --check" |
@@ -60,1 +60,5 @@ # Grats: Implementation-First GraphQL for TypeScript | ||
- [typegraphql-reflection-poc](https://github.com/MichalLytek/typegraphql-reflection-poc) | ||
## License | ||
Grats is [MIT licensed](./LICENSE). |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
322441
6668
0
64
2
76