Comparing version 0.0.0-main-6626f659 to 0.0.0-main-663d2ac4
{ | ||
"name": "grats", | ||
"version": "0.0.5", | ||
"version": "0.0.10", | ||
"main": "dist/src/index.js", | ||
@@ -15,11 +15,12 @@ "bin": "dist/src/cli.js", | ||
"build": "tsc --build", | ||
"lint": "eslint src/**/*.ts" | ||
"format": "prettier . --write", | ||
"lint": "eslint src/**/*.ts && prettier . --check" | ||
}, | ||
"dependencies": { | ||
"@graphql-tools/utils": "^9.2.1", | ||
"commander": "^10.0.0", | ||
"graphql": "^16.6.0", | ||
"typescript": "^4.9.5" | ||
"typescript": "^5.0.2" | ||
}, | ||
"devDependencies": { | ||
"@graphql-tools/utils": "^9.2.1", | ||
"@types/node": "^18.14.6", | ||
@@ -32,2 +33,3 @@ "@typescript-eslint/eslint-plugin": "^5.55.0", | ||
"path-browserify": "^1.0.1", | ||
"prettier": "^2.8.7", | ||
"process": "^0.11.10", | ||
@@ -39,3 +41,3 @@ "ts-node": "^10.9.1" | ||
}, | ||
"packageManager": "pnpm@8.1.1", | ||
"packageManager": "pnpm@8.12.0", | ||
"engines": { | ||
@@ -42,0 +44,0 @@ "node": ">=16 <=21", |
@@ -39,3 +39,3 @@ #!/usr/bin/env node | ||
}; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.formatLoc = void 0; | ||
@@ -45,3 +45,2 @@ var graphql_1 = require("graphql"); | ||
var lib_1 = require("./lib"); | ||
var utils_1 = require("@graphql-tools/utils"); | ||
var commander_1 = require("commander"); | ||
@@ -52,2 +51,4 @@ var fs_1 = require("fs"); | ||
var Locate_1 = require("./Locate"); | ||
var printSchema_1 = require("./printSchema"); | ||
var ts = require("typescript"); | ||
var program = new commander_1.Command(); | ||
@@ -58,9 +59,8 @@ program | ||
.version(package_json_1.version) | ||
.option("-o, --output <SCHEMA_FILE>", "Where to write the schema file. Defaults to stdout") | ||
.option("--tsconfig <TSCONFIG>", "Path to tsconfig.json. Defaults to auto-detecting based on the current working directory") | ||
.action(function (_a) { | ||
var output = _a.output, tsconfig = _a.tsconfig; | ||
var tsconfig = _a.tsconfig; | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_b) { | ||
build(output, tsconfig); | ||
build(tsconfig); | ||
return [2 /*return*/]; | ||
@@ -76,3 +76,4 @@ }); | ||
var tsconfig = _a.tsconfig; | ||
var schema = buildSchema(tsconfig); | ||
var config = getTsConfig(tsconfig).config; | ||
var schema = buildSchema(config); | ||
var loc = (0, Locate_1.locate)(schema, entity); | ||
@@ -86,26 +87,31 @@ if (loc.kind === "ERROR") { | ||
program.parse(); | ||
function build(output, tsconfig) { | ||
var schema = buildSchema(tsconfig); | ||
function build(tsconfig) { | ||
var _a = getTsConfig(tsconfig), config = _a.config, configPath = _a.configPath; | ||
var schema = buildSchema(config); | ||
var sortedSchema = (0, graphql_1.lexicographicSortSchema)(schema); | ||
var schemaStr = (0, utils_1.printSchemaWithDirectives)(sortedSchema, { | ||
assumeValid: true | ||
}); | ||
if (output) { | ||
var absOutput = (0, path_1.resolve)(process.cwd(), output); | ||
(0, fs_1.writeFileSync)(absOutput, schemaStr); | ||
console.error("Grats: Wrote schema to `".concat(absOutput, "`.")); | ||
var gratsOptions = config.raw.grats; | ||
var dest = (0, path_1.resolve)((0, path_1.dirname)(configPath), gratsOptions.tsSchema); | ||
var code = (0, printSchema_1.printExecutableSchema)(sortedSchema, gratsOptions, dest); | ||
(0, fs_1.writeFileSync)(dest, code); | ||
console.error("Grats: Wrote TypeScript schema to `".concat(dest, "`.")); | ||
var schemaStr = (0, printSchema_1.printGratsSDL)(sortedSchema, gratsOptions); | ||
var absOutput = (0, path_1.resolve)((0, path_1.dirname)(configPath), gratsOptions.graphqlSchema); | ||
(0, fs_1.writeFileSync)(absOutput, schemaStr); | ||
console.error("Grats: Wrote schema to `".concat(absOutput, "`.")); | ||
} | ||
// Locate and read the tsconfig.json file | ||
function getTsConfig(tsconfig) { | ||
var configPath = tsconfig || ts.findConfigFile(process.cwd(), ts.sys.fileExists); | ||
if (configPath == null) { | ||
throw new Error("Grats: Could not find tsconfig.json"); | ||
} | ||
else { | ||
console.log(schemaStr); | ||
var optionsResult = (0, _1.getParsedTsConfig)(configPath); | ||
if (optionsResult.kind === "ERROR") { | ||
console.error(optionsResult.err.formatDiagnosticsWithColorAndContext()); | ||
process.exit(1); | ||
} | ||
return { configPath: configPath, config: optionsResult.value }; | ||
} | ||
function buildSchema(tsconfig) { | ||
if (tsconfig && !(0, fs_1.existsSync)(tsconfig)) { | ||
console.error("Grats: Could not find tsconfig.json at `".concat(tsconfig, "`.")); | ||
process.exit(1); | ||
} | ||
var parsed = (0, _1.getParsedTsConfig)(tsconfig); | ||
// FIXME: Validate config! | ||
// https://github.com/tsconfig/bases | ||
var schemaResult = (0, lib_1.buildSchemaResult)(parsed); | ||
function buildSchema(options) { | ||
var schemaResult = (0, lib_1.buildSchemaResult)(options); | ||
if (schemaResult.kind === "ERROR") { | ||
@@ -112,0 +118,0 @@ console.error(schemaResult.err.formatDiagnosticsWithColorAndContext()); |
@@ -69,3 +69,3 @@ /** | ||
export declare function nonNullTypeCannotBeOptional(): string; | ||
export declare function mergedInterfaces(interfaceName: string): string; | ||
export declare function mergedInterfaces(): string; | ||
export declare function implementsTagMissingValue(): string; | ||
@@ -88,1 +88,7 @@ export declare function implementsTagOnClass(): string; | ||
export declare function multipleContextTypes(): string; | ||
export declare function graphQLNameHasLeadingNewlines(name: string, tagName: string): string; | ||
export declare function graphQLTagNameHasWhitespace(tagName: string): string; | ||
export declare function subscriptionFieldNotAsyncIterable(): string; | ||
export declare function nonSubscriptionFieldAsyncIterable(): string; | ||
export declare function operationTypeNotUnknown(): string; | ||
export declare function expectedNullableArgumentToBeOptional(): string; |
"use strict"; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defaultArgPropertyMissingName = exports.defaultArgElementIsNotAssignment = exports.defaultValueIsNotLiteral = exports.ambiguousNumberType = exports.expectedOneNonNullishType = exports.propertyFieldMissingType = exports.cannotResolveSymbolForDescription = exports.promiseMissingTypeArg = 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 = void 0; | ||
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.implementsTagMissingValue = exports.mergedInterfaces = exports.nonNullTypeCannotBeOptional = exports.killsParentOnExceptionOnNullable = exports.killsParentOnExceptionWithWrongConfig = exports.expectedIdentifier = exports.pluralTypeMissingParameter = exports.unknownGraphQLType = exports.unsupportedTypeLiteral = exports.defaultArgPropertyMissingInitializer = 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.implementsTagMissingValue = exports.mergedInterfaces = exports.nonNullTypeCannotBeOptional = exports.killsParentOnExceptionOnNullable = exports.killsParentOnExceptionWithWrongConfig = exports.expectedIdentifier = exports.pluralTypeMissingParameter = exports.unknownGraphQLType = exports.unsupportedTypeLiteral = exports.defaultArgPropertyMissingInitializer = void 0; | ||
var Extractor_1 = require("./Extractor"); | ||
// TODO: Move these to short URLS that are easier to keep from breaking. | ||
var DOC_URLS = { | ||
mergedInterfaces: "https://grats.capt.dev/docs/dockblock-tags/interfaces/#merged-interfaces", | ||
parameterProperties: "https://grats.capt.dev/docs/dockblock-tags/fields#class-based-fields", | ||
typeImplementsInterface: "TODO" | ||
mergedInterfaces: "https://grats.capt.dev/docs/docblock-tags/interfaces/#merged-interfaces", | ||
parameterProperties: "https://grats.capt.dev/docs/docblock-tags/fields#class-based-fields", | ||
}; | ||
@@ -120,3 +119,3 @@ /** | ||
function typeTagOnAliasOfNonObjectOrUnknown() { | ||
return "Expected `@".concat(Extractor_1.TYPE_TAG, "` type to be a type literal or `unknown`. For example: `type Foo = { bar: string }` or `type Query = unknown`."); | ||
return "Expected `@".concat(Extractor_1.TYPE_TAG, "` type to be an object type literal (`{ }`) or `unknown`. For example: `type Foo = { bar: string }` or `type Query = unknown`."); | ||
} | ||
@@ -249,12 +248,12 @@ exports.typeTagOnAliasOfNonObjectOrUnknown = typeTagOnAliasOfNonObjectOrUnknown; | ||
function killsParentOnExceptionOnNullable() { | ||
return "Unexpected `@".concat(Extractor_1.KILLS_PARENT_ON_EXCEPTION_TAG, "` tag. `@").concat(Extractor_1.KILLS_PARENT_ON_EXCEPTION_TAG, "` is unnessesary on fields that are already nullable."); | ||
return "Unexpected `@".concat(Extractor_1.KILLS_PARENT_ON_EXCEPTION_TAG, "` tag. `@").concat(Extractor_1.KILLS_PARENT_ON_EXCEPTION_TAG, "` is unnecessary on fields that are already nullable."); | ||
} | ||
exports.killsParentOnExceptionOnNullable = killsParentOnExceptionOnNullable; | ||
function nonNullTypeCannotBeOptional() { | ||
return "Unexpected optional argument that does not also accept `null`. Optional arguments in GraphQL may get passed an explict `null` value. This means optional arguments must be typed to also accept `null`."; | ||
return "Unexpected optional argument that does not also accept `null`. Optional arguments in GraphQL may get passed an explicit `null` value. This means optional arguments must be typed to also accept `null`."; | ||
} | ||
exports.nonNullTypeCannotBeOptional = nonNullTypeCannotBeOptional; | ||
function mergedInterfaces(interfaceName) { | ||
function mergedInterfaces() { | ||
return [ | ||
"Unexpected merged interface `".concat(interfaceName, "`."), | ||
"Unexpected merged interface.", | ||
"If an interface is declared multiple times in a scope, TypeScript merges them.", | ||
@@ -280,3 +279,3 @@ "To avoid ambiguity Grats does not support using merged interfaces as GraphQL interfaces.", | ||
function implementsTagOnTypeAlias() { | ||
return "`@".concat(Extractor_1.IMPLEMENTS_TAG_DEPRECATED, "` has been deprecated. Types which implement GraphQL interfaces should be defined using TypeScript class or interface declarations. Learn more: ").concat(DOC_URLS.typeImplementsInterface, "."); | ||
return "`@".concat(Extractor_1.IMPLEMENTS_TAG_DEPRECATED, "` has been deprecated. Types which implement GraphQL interfaces should be defined using TypeScript class or interface declarations."); | ||
} | ||
@@ -344,1 +343,25 @@ exports.implementsTagOnTypeAlias = implementsTagOnTypeAlias; | ||
exports.multipleContextTypes = multipleContextTypes; | ||
function graphQLNameHasLeadingNewlines(name, tagName) { | ||
return "Expected the GraphQL name `".concat(name, "` to be on the same line as it's `@").concat(tagName, "` tag."); | ||
} | ||
exports.graphQLNameHasLeadingNewlines = graphQLNameHasLeadingNewlines; | ||
function graphQLTagNameHasWhitespace(tagName) { | ||
return "Expected text following a `@".concat(tagName, "` tag to be a GraphQL name. If you intended this text to be a description, place it at the top of the docblock before any `@tags`."); | ||
} | ||
exports.graphQLTagNameHasWhitespace = graphQLTagNameHasWhitespace; | ||
function subscriptionFieldNotAsyncIterable() { | ||
return "Expected fields on `Subscription` to return an AsyncIterable."; | ||
} | ||
exports.subscriptionFieldNotAsyncIterable = subscriptionFieldNotAsyncIterable; | ||
function nonSubscriptionFieldAsyncIterable() { | ||
return "Unexpected AsyncIterable. Only fields on `Subscription` should return an AsyncIterable."; | ||
} | ||
exports.nonSubscriptionFieldAsyncIterable = nonSubscriptionFieldAsyncIterable; | ||
function operationTypeNotUnknown() { | ||
return "Operation types `Query`, `Mutation`, and `Subscription` must be defined as type aliases of `unknown`. E.g. `type Query = unknown`."; | ||
} | ||
exports.operationTypeNotUnknown = operationTypeNotUnknown; | ||
function expectedNullableArgumentToBeOptional() { | ||
return "Expected nullable argument to be optional. graphql-js may not define properties where an undefined argument is passed. To guard against this add a `?` to the end of the argument name to make it optional."; | ||
} | ||
exports.expectedNullableArgumentToBeOptional = expectedNullableArgumentToBeOptional; |
@@ -1,7 +0,7 @@ | ||
import { FieldDefinitionNode, InputValueDefinitionNode, NamedTypeNode, NameNode, TypeNode, StringValueNode, ConstValueNode, ConstDirectiveNode, EnumValueDefinitionNode, ConstObjectFieldNode, ConstObjectValueNode, ConstListValueNode } from "graphql"; | ||
import { NameNode } from "graphql"; | ||
import { DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import * as ts from "typescript"; | ||
import { GratsDefinitionNode, TypeContext } from "./TypeContext"; | ||
import { NameDefinition } from "./TypeContext"; | ||
import { ConfigOptions } from "./lib"; | ||
import { GraphQLConstructor } from "./GraphQLConstructor"; | ||
import { GratsDefinitionNode } from "./GraphQLConstructor"; | ||
export declare const LIBRARY_IMPORT_NAME = "grats"; | ||
@@ -20,3 +20,10 @@ export declare const LIBRARY_NAME = "Grats"; | ||
export declare const ALL_TAGS: string[]; | ||
type ArgDefaults = Map<string, ts.Expression>; | ||
export type ExtractionSnapshot = { | ||
readonly definitions: GratsDefinitionNode[]; | ||
readonly unresolvedNames: Map<ts.Node, NameNode>; | ||
readonly nameDefinitions: Map<ts.Node, NameDefinition>; | ||
readonly contextReferences: Array<ts.Node>; | ||
readonly typesWithTypenameField: Set<string>; | ||
readonly interfaceDeclarations: Array<ts.InterfaceDeclaration>; | ||
}; | ||
/** | ||
@@ -32,80 +39,2 @@ * Extracts GraphQL definitions from TypeScript source code. | ||
*/ | ||
export declare class Extractor { | ||
definitions: GratsDefinitionNode[]; | ||
sourceFile: ts.SourceFile; | ||
ctx: TypeContext; | ||
configOptions: ConfigOptions; | ||
errors: ts.Diagnostic[]; | ||
gql: GraphQLConstructor; | ||
constructor(sourceFile: ts.SourceFile, ctx: TypeContext, buildOptions: ConfigOptions); | ||
extract(): DiagnosticsResult<GratsDefinitionNode[]>; | ||
extractType(node: ts.Node, tag: ts.JSDocTag): void; | ||
extractScalar(node: ts.Node, tag: ts.JSDocTag): void; | ||
extractInterface(node: ts.Node, tag: ts.JSDocTag): void; | ||
extractEnum(node: ts.Node, tag: ts.JSDocTag): void; | ||
extractInput(node: ts.Node, tag: ts.JSDocTag): void; | ||
extractUnion(node: ts.Node, tag: ts.JSDocTag): void; | ||
/** Error handling and location juggling */ | ||
report(node: ts.Node, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): null; | ||
reportUnhandled(node: ts.Node, positionKind: "type" | "field" | "field type" | "input" | "input field" | "union member" | "constant value" | "union" | "enum value", message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): null; | ||
related(node: ts.Node, message: string): ts.DiagnosticRelatedInformation; | ||
diagnosticAnnotatedLocation(node: ts.Node): { | ||
start: number; | ||
length: number; | ||
filepath: ts.SourceFile; | ||
}; | ||
/** TypeScript traversals */ | ||
unionTypeAliasDeclaration(node: ts.TypeAliasDeclaration, tag: ts.JSDocTag): null | undefined; | ||
functionDeclarationExtendType(node: ts.FunctionDeclaration, tag: ts.JSDocTag): null | undefined; | ||
typeReferenceFromParam(typeParam: ts.ParameterDeclaration): NameNode | null; | ||
namedFunctionExportName(node: ts.FunctionDeclaration): ts.Identifier | null; | ||
scalarTypeAliasDeclaration(node: ts.TypeAliasDeclaration, tag: ts.JSDocTag): null | undefined; | ||
inputTypeAliasDeclaration(node: ts.TypeAliasDeclaration, tag: ts.JSDocTag): null | undefined; | ||
collectInputFields(node: ts.TypeAliasDeclaration): Array<InputValueDefinitionNode> | null; | ||
collectInputField(node: ts.PropertySignature): InputValueDefinitionNode | null; | ||
typeClassDeclaration(node: ts.ClassDeclaration, tag: ts.JSDocTag): null | undefined; | ||
typeInterfaceDeclaration(node: ts.InterfaceDeclaration, tag: ts.JSDocTag): null | undefined; | ||
typeTypeAliasDeclaration(node: ts.TypeAliasDeclaration, tag: ts.JSDocTag): null | undefined; | ||
checkForTypenameProperty(node: ts.ClassDeclaration | ts.InterfaceDeclaration | ts.TypeLiteralNode, expectedName: string): void; | ||
isValidTypeNameProperty(member: ts.ClassElement | ts.TypeElement, expectedName: string): boolean; | ||
isValidTypenamePropertyDeclaration(node: ts.PropertyDeclaration, expectedName: string): boolean; | ||
isValidTypenamePropertySignature(node: ts.PropertySignature, expectedName: string): boolean; | ||
isValidTypenamePropertyType(node: ts.TypeNode, expectedName: string): boolean; | ||
collectInterfaces(node: ts.ClassDeclaration | ts.InterfaceDeclaration | ts.TypeAliasDeclaration): Array<NamedTypeNode> | null; | ||
reportTagInterfaces(node: ts.TypeAliasDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration): null | undefined; | ||
collectHeritageInterfaces(node: ts.ClassDeclaration | ts.InterfaceDeclaration): Array<NamedTypeNode> | null; | ||
symbolHasGqlTag(node: ts.Node): boolean; | ||
hasGqlTag(node: ts.Node): boolean; | ||
interfaceInterfaceDeclaration(node: ts.InterfaceDeclaration, tag: ts.JSDocTag): null | undefined; | ||
collectFields(node: ts.ClassDeclaration | ts.InterfaceDeclaration | ts.TypeLiteralNode): Array<FieldDefinitionNode>; | ||
constructorParam(node: ts.ParameterDeclaration): FieldDefinitionNode | null; | ||
collectArgs(argsParam: ts.ParameterDeclaration): ReadonlyArray<InputValueDefinitionNode> | null; | ||
collectArgDefaults(node: ts.ObjectBindingPattern): ArgDefaults; | ||
collectConstValue(node: ts.Expression): ConstValueNode | null; | ||
collectArrayLiteral(node: ts.ArrayLiteralExpression): ConstListValueNode | null; | ||
collectObjectLiteral(node: ts.ObjectLiteralExpression): ConstObjectValueNode | null; | ||
collectObjectField(node: ts.ObjectLiteralElementLike): ConstObjectFieldNode | null; | ||
collectArg(node: ts.TypeElement, defaults?: Map<string, ts.Expression> | null): InputValueDefinitionNode | null; | ||
enumEnumDeclaration(node: ts.EnumDeclaration, tag: ts.JSDocTag): void; | ||
enumTypeAliasDeclaration(node: ts.TypeAliasDeclaration, tag: ts.JSDocTag): void; | ||
enumTypeAliasVariants(node: ts.TypeAliasDeclaration): EnumValueDefinitionNode[] | null; | ||
collectEnumValues(node: ts.EnumDeclaration): ReadonlyArray<EnumValueDefinitionNode>; | ||
entityName(node: ts.ClassDeclaration | ts.MethodDeclaration | ts.MethodSignature | ts.PropertyDeclaration | ts.InterfaceDeclaration | ts.PropertySignature | ts.EnumDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ParameterDeclaration, tag: ts.JSDocTag): NameNode | null; | ||
validateContextParameter(node: ts.ParameterDeclaration): null | undefined; | ||
methodDeclaration(node: ts.MethodDeclaration | ts.MethodSignature): FieldDefinitionNode | null; | ||
collectMethodType(node: ts.TypeNode): TypeNode | null; | ||
collectPropertyType(node: ts.TypeNode): TypeNode | null; | ||
maybeUnwrapePromise(node: ts.TypeNode): ts.TypeNode | null; | ||
collectDescription(node: ts.Node): StringValueNode | null; | ||
collectDeprecated(node: ts.Node): ConstDirectiveNode | null; | ||
property(node: ts.PropertyDeclaration | ts.PropertySignature): FieldDefinitionNode | null; | ||
collectType(node: ts.TypeNode): TypeNode | null; | ||
typeReference(node: ts.TypeReferenceNode): TypeNode | null; | ||
isNullish(node: ts.Node): boolean; | ||
expectIdentifier(node: ts.Node): ts.Identifier | null; | ||
findTag(node: ts.Node, tagName: string): ts.JSDocTag | null; | ||
handleErrorBubbling(parentNode: ts.Node, type: TypeNode): TypeNode; | ||
exportDirective(nameNode: ts.Node, filename: string, functionName: string): ConstDirectiveNode; | ||
fieldNameDirective(nameNode: ts.Node, name: string): ConstDirectiveNode; | ||
} | ||
export {}; | ||
export declare function extract(options: ConfigOptions, sourceFile: ts.SourceFile): DiagnosticsResult<ExtractionSnapshot>; |
@@ -29,4 +29,4 @@ "use strict"; | ||
}; | ||
exports.__esModule = true; | ||
exports.Extractor = 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.ISSUE_URL = exports.LIBRARY_NAME = exports.LIBRARY_IMPORT_NAME = void 0; | ||
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.ISSUE_URL = exports.LIBRARY_NAME = exports.LIBRARY_IMPORT_NAME = void 0; | ||
var graphql_1 = require("graphql"); | ||
@@ -39,3 +39,3 @@ var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var GraphQLConstructor_1 = require("./GraphQLConstructor"); | ||
var serverDirectives_1 = require("./serverDirectives"); | ||
var gratsRoot_1 = require("./gratsRoot"); | ||
exports.LIBRARY_IMPORT_NAME = "grats"; | ||
@@ -64,2 +64,3 @@ exports.LIBRARY_NAME = "Grats"; | ||
var DEPRECATED_TAG = "deprecated"; | ||
var OPERATION_TYPES = new Set(["Query", "Mutation", "Subscription"]); | ||
/** | ||
@@ -75,11 +76,26 @@ * Extracts GraphQL definitions from TypeScript source code. | ||
*/ | ||
function extract(options, sourceFile) { | ||
var extractor = new Extractor(options); | ||
return extractor.extract(sourceFile); | ||
} | ||
exports.extract = extract; | ||
var Extractor = /** @class */ (function () { | ||
function Extractor(sourceFile, ctx, buildOptions) { | ||
function Extractor(buildOptions) { | ||
this.definitions = []; | ||
// Snapshot data | ||
this.unresolvedNames = new Map(); | ||
this.nameDefinitions = new Map(); | ||
this.contextReferences = []; | ||
this.typesWithTypenameField = new Set(); | ||
this.interfaceDeclarations = []; | ||
this.errors = []; | ||
this.sourceFile = sourceFile; | ||
this.ctx = ctx; | ||
this.configOptions = buildOptions; | ||
this.gql = new GraphQLConstructor_1.GraphQLConstructor(sourceFile); | ||
this.gql = new GraphQLConstructor_1.GraphQLConstructor(); | ||
} | ||
Extractor.prototype.markUnresolvedType = function (node, name) { | ||
this.unresolvedNames.set(node, name); | ||
}; | ||
Extractor.prototype.recordTypeName = function (node, name, kind) { | ||
this.nameDefinitions.set(node, { name: name, kind: kind }); | ||
}; | ||
// Traverse all nodes, checking each one for its JSDoc tags. | ||
@@ -89,5 +105,5 @@ // If we find a tag we recognize, we extract the relevant information, | ||
// supported. | ||
Extractor.prototype.extract = function () { | ||
Extractor.prototype.extract = function (sourceFile) { | ||
var _this = this; | ||
(0, JSDoc_1.traverseJSDocTags)(this.sourceFile, function (node, tag) { | ||
(0, JSDoc_1.traverseJSDocTags)(sourceFile, function (node, tag) { | ||
var e_1, _a; | ||
@@ -152,3 +168,3 @@ switch (tag.tagName.text) { | ||
try { | ||
if (ALL_TAGS_1_1 && !ALL_TAGS_1_1.done && (_a = ALL_TAGS_1["return"])) _a.call(ALL_TAGS_1); | ||
if (ALL_TAGS_1_1 && !ALL_TAGS_1_1.done && (_a = ALL_TAGS_1.return)) _a.call(ALL_TAGS_1); | ||
} | ||
@@ -166,3 +182,10 @@ finally { if (e_1) throw e_1.error; } | ||
} | ||
return (0, DiagnosticError_1.ok)(this.definitions); | ||
return (0, DiagnosticError_1.ok)({ | ||
definitions: this.definitions, | ||
unresolvedNames: this.unresolvedNames, | ||
nameDefinitions: this.nameDefinitions, | ||
contextReferences: this.contextReferences, | ||
typesWithTypenameField: this.typesWithTypenameField, | ||
interfaceDeclarations: this.interfaceDeclarations, | ||
}); | ||
}; | ||
@@ -228,13 +251,3 @@ Extractor.prototype.extractType = function (node, tag) { | ||
Extractor.prototype.report = function (node, message, relatedInformation) { | ||
var start = node.getStart(); | ||
var length = node.getEnd() - start; | ||
this.errors.push({ | ||
messageText: message, | ||
file: this.sourceFile, | ||
code: DiagnosticError_1.FAKE_ERROR_CODE, | ||
category: ts.DiagnosticCategory.Error, | ||
start: start, | ||
length: length, | ||
relatedInformation: relatedInformation | ||
}); | ||
this.errors.push((0, DiagnosticError_1.tsErr)(node, message, relatedInformation)); | ||
return null; | ||
@@ -249,17 +262,2 @@ }; | ||
}; | ||
Extractor.prototype.related = function (node, message) { | ||
return { | ||
category: ts.DiagnosticCategory.Message, | ||
code: 0, | ||
file: node.getSourceFile(), | ||
start: node.getStart(), | ||
length: node.getWidth(), | ||
messageText: message | ||
}; | ||
}; | ||
Extractor.prototype.diagnosticAnnotatedLocation = function (node) { | ||
var start = node.getStart(); | ||
var end = node.getEnd(); | ||
return { start: start, length: end - start, filepath: this.sourceFile }; | ||
}; | ||
/** TypeScript traversals */ | ||
@@ -274,3 +272,3 @@ Extractor.prototype.unionTypeAliasDeclaration = function (node, tag) { | ||
} | ||
var description = this.collectDescription(node.name); | ||
var description = this.collectDescription(node); | ||
var types = []; | ||
@@ -284,3 +282,3 @@ try { | ||
var namedType = this.gql.namedType(member.typeName, TypeContext_1.UNRESOLVED_REFERENCE_NAME); | ||
this.ctx.markUnresolvedType(member.typeName, namedType.name); | ||
this.markUnresolvedType(member.typeName, namedType.name); | ||
types.push(namedType); | ||
@@ -292,7 +290,7 @@ } | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
this.ctx.recordTypeName(node.name, name, "UNION"); | ||
this.recordTypeName(node.name, name, "UNION"); | ||
this.definitions.push(this.gql.unionTypeDefinition(node, name, types, description)); | ||
@@ -317,5 +315,6 @@ }; | ||
} | ||
var type = this.collectMethodType(node.type); | ||
if (type == null) | ||
var returnType = this.collectReturnType(node.type); | ||
if (returnType == null) | ||
return null; | ||
var type = returnType.type, isStream = returnType.isStream; | ||
var args = null; | ||
@@ -330,3 +329,3 @@ var argsParam = node.parameters[1]; | ||
} | ||
var description = this.collectDescription(funcName); | ||
var description = this.collectDescription(node); | ||
if (!ts.isSourceFile(node.parent)) { | ||
@@ -336,8 +335,12 @@ return this.report(node, E.functionFieldNotTopLevel()); | ||
// TODO: Does this work in the browser? | ||
var filename = this.ctx.getDestFilePath(node.parent); | ||
var tsModulePath = (0, gratsRoot_1.relativePath)(node.getSourceFile().fileName); | ||
var directives = [ | ||
this.exportDirective(funcName, filename, funcName.text), | ||
this.gql.exportedDirective(funcName, { | ||
tsModulePath: tsModulePath, | ||
exportedFunctionName: funcName.text, | ||
argCount: node.parameters.length, | ||
}), | ||
]; | ||
if (funcName.text !== name.value) { | ||
directives.push(this.fieldNameDirective(funcName, funcName.text)); | ||
if (isStream) { | ||
directives.push(this.gql.asyncIterableDirective(node.type)); | ||
} | ||
@@ -360,3 +363,3 @@ var deprecated = this.collectDeprecated(node); | ||
var typeName = this.gql.name(nameNode, TypeContext_1.UNRESOLVED_REFERENCE_NAME); | ||
this.ctx.markUnresolvedType(nameNode, typeName); | ||
this.markUnresolvedType(nameNode, typeName); | ||
return typeName; | ||
@@ -388,4 +391,4 @@ }; | ||
return null; | ||
var description = this.collectDescription(node.name); | ||
this.ctx.recordTypeName(node.name, name, "SCALAR"); | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "SCALAR"); | ||
this.definitions.push(this.gql.scalarTypeDefinition(node, name, description)); | ||
@@ -397,4 +400,4 @@ }; | ||
return null; | ||
var description = this.collectDescription(node.name); | ||
this.ctx.recordTypeName(node.name, name, "INPUT_OBJECT"); | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "INPUT_OBJECT"); | ||
var fields = this.collectInputFields(node); | ||
@@ -425,3 +428,3 @@ var deprecatedDirective = this.collectDeprecated(node); | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -443,3 +446,3 @@ finally { if (e_3) throw e_3.error; } | ||
var type = node.questionToken == null ? inner : this.gql.nullableType(inner); | ||
var description = this.collectDescription(node.name); | ||
var description = this.collectDescription(node); | ||
var deprecatedDirective = this.collectDeprecated(node); | ||
@@ -455,9 +458,17 @@ return this.gql.inputValueDefinition(node, this.gql.name(id, id.text), type, deprecatedDirective == null ? null : [deprecatedDirective], null, description); | ||
return null; | ||
var description = this.collectDescription(node.name); | ||
this.validateOperationTypes(node.name, name.value); | ||
var description = this.collectDescription(node); | ||
var fields = this.collectFields(node); | ||
var interfaces = this.collectInterfaces(node); | ||
this.ctx.recordTypeName(node.name, name, "TYPE"); | ||
this.recordTypeName(node.name, name, "TYPE"); | ||
this.checkForTypenameProperty(node, name.value); | ||
this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description)); | ||
}; | ||
Extractor.prototype.validateOperationTypes = function (node, name) { | ||
// TODO: If we start supporting defining operation types using | ||
// non-standard names, we will need to update this logic. | ||
if (OPERATION_TYPES.has(name)) { | ||
this.report(node, E.operationTypeNotUnknown()); | ||
} | ||
}; | ||
Extractor.prototype.typeInterfaceDeclaration = function (node, tag) { | ||
@@ -467,6 +478,7 @@ var name = this.entityName(node, tag); | ||
return null; | ||
var description = this.collectDescription(node.name); | ||
this.validateOperationTypes(node.name, name.value); | ||
var description = this.collectDescription(node); | ||
var fields = this.collectFields(node); | ||
var interfaces = this.collectInterfaces(node); | ||
this.ctx.recordTypeName(node.name, name, "INTERFACE"); | ||
this.recordTypeName(node.name, name, "INTERFACE"); | ||
this.checkForTypenameProperty(node, name.value); | ||
@@ -482,2 +494,3 @@ this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description)); | ||
if (ts.isTypeLiteralNode(node.type)) { | ||
this.validateOperationTypes(node.type, name.value); | ||
fields = this.collectFields(node.type); | ||
@@ -495,4 +508,4 @@ interfaces = this.collectInterfaces(node); | ||
} | ||
var description = this.collectDescription(node.name); | ||
this.ctx.recordTypeName(node.name, name, "TYPE"); | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "TYPE"); | ||
this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description)); | ||
@@ -506,3 +519,3 @@ }; | ||
if (hasTypename) { | ||
this.ctx.recordHasTypenameField(expectedName); | ||
this.typesWithTypenameField.add(expectedName); | ||
} | ||
@@ -601,6 +614,5 @@ }; | ||
.filter(function (expression) { return ts.isIdentifier(expression); }) | ||
.filter(function (expression) { return _this.symbolHasGqlTag(expression); }) | ||
.map(function (expression) { | ||
var namedType = _this.gql.namedType(expression, TypeContext_1.UNRESOLVED_REFERENCE_NAME); | ||
_this.ctx.markUnresolvedType(expression, namedType.name); | ||
_this.markUnresolvedType(expression, namedType.name); | ||
return namedType; | ||
@@ -615,11 +627,2 @@ }); | ||
}; | ||
Extractor.prototype.symbolHasGqlTag = function (node) { | ||
var symbol = this.ctx.checker.getSymbolAtLocation(node); | ||
if (symbol == null) | ||
return false; | ||
var declaration = this.ctx.findSymbolDeclaration(symbol); | ||
if (declaration == null) | ||
return false; | ||
return this.hasGqlTag(declaration); | ||
}; | ||
Extractor.prototype.hasGqlTag = function (node) { | ||
@@ -631,3 +634,2 @@ return ts.getJSDocTags(node).some(function (tag) { | ||
Extractor.prototype.interfaceInterfaceDeclaration = function (node, tag) { | ||
var _this = this; | ||
var name = this.entityName(node, tag); | ||
@@ -637,23 +639,7 @@ if (name == null || name.value == null) { | ||
} | ||
// Prevent using merged interfaces as GraphQL interfaces. | ||
// https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces | ||
var symbol = this.ctx.checker.getSymbolAtLocation(node.name); | ||
if (symbol != null && | ||
symbol.declarations != null && | ||
symbol.declarations.length > 1) { | ||
var otherLocations = symbol.declarations | ||
.filter(function (d) { return d !== node && ts.isInterfaceDeclaration(d); }) | ||
.map(function (d) { | ||
var _a; | ||
var locNode = (_a = ts.getNameOfDeclaration(d)) !== null && _a !== void 0 ? _a : d; | ||
return _this.related(locNode, "Other declaration"); | ||
}); | ||
if (otherLocations.length > 0) { | ||
return this.report(node.name, E.mergedInterfaces(name.value), otherLocations); | ||
} | ||
} | ||
var description = this.collectDescription(node.name); | ||
this.interfaceDeclarations.push(node); | ||
var description = this.collectDescription(node); | ||
var interfaces = this.collectInterfaces(node); | ||
var fields = this.collectFields(node); | ||
this.ctx.recordTypeName(node.name, name, "INTERFACE"); | ||
this.recordTypeName(node.name, name, "INTERFACE"); | ||
this.definitions.push(this.gql.interfaceTypeDefinition(node, name, fields, interfaces, description)); | ||
@@ -681,3 +667,3 @@ }; | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -741,3 +727,5 @@ finally { if (e_4) throw e_4.error; } | ||
if (id.text !== name.value) { | ||
directives = [this.fieldNameDirective(node.name, id.text)]; | ||
directives = [ | ||
this.gql.propertyNameDirective(node.name, { name: id.text }), | ||
]; | ||
} | ||
@@ -751,3 +739,3 @@ var type = this.collectType(node.type); | ||
} | ||
var description = this.collectDescription(node.name); | ||
var description = this.collectDescription(node); | ||
return this.gql.fieldDefinition(node, name, this.handleErrorBubbling(node, type), null, directives, description); | ||
@@ -784,3 +772,3 @@ }; | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -807,3 +795,3 @@ finally { if (e_5) throw e_5.error; } | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -824,3 +812,3 @@ finally { if (e_6) throw e_6.error; } | ||
else if (this.isNullish(node)) { | ||
return this.gql["null"](node); | ||
return this.gql.null(node); | ||
} | ||
@@ -860,3 +848,3 @@ else if (node.kind === ts.SyntaxKind.TrueKeyword) { | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -889,3 +877,3 @@ finally { if (e_7) throw e_7.error; } | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -933,12 +921,20 @@ finally { if (e_8) throw e_8.error; } | ||
return null; | ||
if (type.kind !== graphql_1.Kind.NON_NULL_TYPE && !node.questionToken) { | ||
// If a field is passed an argument value, and that argument is not defined in the request, | ||
// `graphql-js` will not define the argument property. Therefore we must ensure the argument | ||
// is not just nullable, but optional. | ||
return this.report(node.name, E.expectedNullableArgumentToBeOptional()); | ||
} | ||
if (node.questionToken) { | ||
/* | ||
// TODO: Don't allow args that are optional but don't accept null | ||
if (type.kind === Kind.NON_NULL_TYPE) { | ||
return this.report(node.questionToken, E.nonNullTypeCannotBeOptional()); | ||
// Question mark means we can handle the argument being undefined in the | ||
// object literal, but if we are going to type the GraphQL arg as | ||
// optional, the code must also be able to handle an explicit null. | ||
// | ||
// TODO: This will catch { a?: string } but not { a?: string | undefined }. | ||
if (type.kind === graphql_1.Kind.NON_NULL_TYPE) { | ||
return this.report(node.questionToken, E.nonNullTypeCannotBeOptional()); | ||
} | ||
*/ | ||
type = this.gql.nullableType(type); | ||
} | ||
var description = this.collectDescription(node.name); | ||
var description = this.collectDescription(node); | ||
var defaultValue = null; | ||
@@ -959,5 +955,5 @@ if (defaults != null) { | ||
} | ||
var description = this.collectDescription(node.name); | ||
var description = this.collectDescription(node); | ||
var values = this.collectEnumValues(node); | ||
this.ctx.recordTypeName(node.name, name, "ENUM"); | ||
this.recordTypeName(node.name, name, "ENUM"); | ||
this.definitions.push(this.gql.enumTypeDefinition(node, name, values, description)); | ||
@@ -973,4 +969,4 @@ }; | ||
return; | ||
var description = this.collectDescription(node.name); | ||
this.ctx.recordTypeName(node.name, name, "ENUM"); | ||
var description = this.collectDescription(node); | ||
this.recordTypeName(node.name, name, "ENUM"); | ||
this.definitions.push(this.gql.enumTypeDefinition(node, name, values, description)); | ||
@@ -980,3 +976,2 @@ }; | ||
var e_9, _a; | ||
var _b; | ||
// Semantically we only support deriving enums from type aliases that | ||
@@ -999,23 +994,4 @@ // are unions of string literals. However, in the edge case of a union | ||
try { | ||
for (var _c = __values(node.type.types), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var member = _d.value; | ||
// TODO: Complete this feature | ||
if (ts.isTypeReferenceNode(member)) { | ||
if (member.typeName.kind === ts.SyntaxKind.Identifier) { | ||
var symbol = this.ctx.checker.getSymbolAtLocation(member.typeName); | ||
if (((_b = symbol === null || symbol === void 0 ? void 0 : symbol.declarations) === null || _b === void 0 ? void 0 : _b.length) === 1) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
var declaration = symbol.declarations[0]; | ||
if (ts.isTypeAliasDeclaration(declaration)) { | ||
if (ts.isLiteralTypeNode(declaration.type) && | ||
ts.isStringLiteral(declaration.type.literal)) { | ||
var deprecatedDirective = this.collectDeprecated(declaration); | ||
var memberDescription = this.collectDescription(declaration.name); | ||
values.push(this.gql.enumValueDefinition(node, this.gql.name(declaration.type.literal, declaration.type.literal.text), deprecatedDirective ? [deprecatedDirective] : [], memberDescription)); | ||
continue; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
for (var _b = __values(node.type.types), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var member = _c.value; | ||
if (!ts.isLiteralTypeNode(member) || | ||
@@ -1034,3 +1010,3 @@ !ts.isStringLiteral(member.literal)) { | ||
try { | ||
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -1052,3 +1028,3 @@ finally { if (e_9) throw e_9.error; } | ||
} | ||
var description = this.collectDescription(member.name); | ||
var description = this.collectDescription(member); | ||
var deprecated = this.collectDeprecated(member); | ||
@@ -1061,3 +1037,3 @@ values.push(this.gql.enumValueDefinition(member, this.gql.name(member.initializer, member.initializer.text), deprecated ? [deprecated] : undefined, description)); | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -1073,3 +1049,24 @@ finally { if (e_10) throw e_10.error; } | ||
// FIXME: Use the _value_'s location not the tag's | ||
return this.gql.name(tag, commentName); | ||
var locNode = tag; | ||
// Test for leading newlines using the raw text | ||
var hasLeadingNewlines = /\n/.test(trimTrailingCommentLines(tag.getText())); | ||
var hasInternalWhitespace = /\s/.test(commentName); | ||
var validationMessage = graphQLNameValidationMessage(commentName); | ||
if (hasLeadingNewlines && validationMessage == null) { | ||
// TODO: Offer quick fix. | ||
return this.report(locNode, E.graphQLNameHasLeadingNewlines(commentName, tag.tagName.text)); | ||
} | ||
if (hasLeadingNewlines || hasInternalWhitespace) { | ||
return this.report(locNode, E.graphQLTagNameHasWhitespace(tag.tagName.text)); | ||
} | ||
// No whitespace, but still invalid. We will assume they meant this to | ||
// be a GraphQL name but didn't provide a valid identifier. | ||
// | ||
// NOTE: We can't let GraphQL validation handle this, because it throws rather | ||
// than returning a validation message. Presumably because it expects token | ||
// validation to be done during lexing/parsing. | ||
if (validationMessage !== null) { | ||
return this.report(locNode, validationMessage); | ||
} | ||
return this.gql.name(locNode, commentName); | ||
} | ||
@@ -1103,22 +1100,3 @@ } | ||
} | ||
var symbol = this.ctx.checker.getSymbolAtLocation(node.type.typeName); | ||
if (symbol == null) { | ||
return this.report(node.type.typeName, E.expectedTypeAnnotationOnContextToBeResolvable()); | ||
} | ||
var declaration = this.ctx.findSymbolDeclaration(symbol); | ||
if (declaration == null) { | ||
return this.report(node.type.typeName, E.expectedTypeAnnotationOnContextToHaveDeclaration()); | ||
} | ||
if (this.ctx.gqlContext == null) { | ||
// This is the first typed context value we've seen... | ||
this.ctx.gqlContext = { | ||
declaration: declaration, | ||
firstReference: node.type.typeName | ||
}; | ||
} | ||
else if (this.ctx.gqlContext.declaration !== declaration) { | ||
return this.report(node.type.typeName, E.multipleContextTypes(), [ | ||
this.related(this.ctx.gqlContext.firstReference, "A different type reference was used here"), | ||
]); | ||
} | ||
this.contextReferences.push(node.type.typeName); | ||
}; | ||
@@ -1135,3 +1113,6 @@ Extractor.prototype.methodDeclaration = function (node) { | ||
} | ||
var type = this.collectMethodType(node.type); | ||
var returnType = this.collectReturnType(node.type); | ||
if (returnType == null) | ||
return null; | ||
var type = returnType.type, isStream = returnType.isStream; | ||
// We already reported an error | ||
@@ -1149,3 +1130,3 @@ if (type == null) | ||
} | ||
var description = this.collectDescription(node.name); | ||
var description = this.collectDescription(node); | ||
var id = this.expectIdentifier(node.name); | ||
@@ -1156,4 +1137,9 @@ if (id == null) | ||
if (id.text !== name.value) { | ||
directives = [this.fieldNameDirective(node.name, id.text)]; | ||
directives = [ | ||
this.gql.propertyNameDirective(node.name, { name: id.text }), | ||
]; | ||
} | ||
if (isStream) { | ||
directives.push(this.gql.asyncIterableDirective(node.type)); | ||
} | ||
var deprecated = this.collectDeprecated(node); | ||
@@ -1165,11 +1151,29 @@ if (deprecated != null) { | ||
}; | ||
Extractor.prototype.collectMethodType = function (node) { | ||
var inner = this.maybeUnwrapePromise(node); | ||
Extractor.prototype.collectReturnType = function (node) { | ||
if (ts.isTypeReferenceNode(node)) { | ||
var identifier = this.expectIdentifier(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.promiseMissingTypeArg()); | ||
} | ||
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; | ||
return this.collectType(inner); | ||
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.maybeUnwrapePromise(node); | ||
var inner = this.maybeUnwrapPromise(node); | ||
if (inner == null) | ||
@@ -1179,3 +1183,3 @@ return null; | ||
}; | ||
Extractor.prototype.maybeUnwrapePromise = function (node) { | ||
Extractor.prototype.maybeUnwrapPromise = function (node) { | ||
if (ts.isTypeReferenceNode(node)) { | ||
@@ -1186,3 +1190,3 @@ var identifier = this.expectIdentifier(node.typeName); | ||
if (identifier.text === "Promise") { | ||
if (node.typeArguments == null) { | ||
if (node.typeArguments == null || node.typeArguments.length === 0) { | ||
return this.report(node, E.promiseMissingTypeArg()); | ||
@@ -1196,11 +1200,12 @@ } | ||
Extractor.prototype.collectDescription = function (node) { | ||
var symbol = this.ctx.checker.getSymbolAtLocation(node); | ||
if (symbol == null) { | ||
return this.report(node, E.cannotResolveSymbolForDescription()); | ||
var docs = | ||
// @ts-ignore Exposed as stable in https://github.com/microsoft/TypeScript/pull/53627 | ||
ts.getJSDocCommentsAndTags(node); | ||
var comment = docs | ||
.filter(function (doc) { return doc.kind === ts.SyntaxKind.JSDoc; }) | ||
.map(function (doc) { return doc.comment; }) | ||
.join(""); | ||
if (comment) { | ||
return this.gql.string(node, comment.trim(), true); | ||
} | ||
var doc = symbol.getDocumentationComment(this.ctx.checker); | ||
var description = ts.displayPartsToString(doc); | ||
if (description) { | ||
return this.gql.string(node, description, true); | ||
} | ||
return null; | ||
@@ -1238,3 +1243,3 @@ }; | ||
var type = node.questionToken == null ? inner : this.gql.nullableType(inner); | ||
var description = this.collectDescription(node.name); | ||
var description = this.collectDescription(node); | ||
var directives = []; | ||
@@ -1249,3 +1254,5 @@ var id = this.expectIdentifier(node.name); | ||
if (id.text !== name.value) { | ||
directives = [this.fieldNameDirective(node.name, id.text)]; | ||
directives = [ | ||
this.gql.propertyNameDirective(node.name, { name: id.text }), | ||
]; | ||
} | ||
@@ -1282,3 +1289,3 @@ return this.gql.fieldDefinition(node, name, this.handleErrorBubbling(node, type), null, directives, description); | ||
var incompatibleVariants = rest.map(function (tsType) { | ||
return _this.related(tsType, "Other non-nullish type"); | ||
return (0, DiagnosticError_1.tsRelated)(tsType, "Other non-nullish type"); | ||
}); | ||
@@ -1335,3 +1342,3 @@ this.report(first, E.expectedOneNonNullishType(), incompatibleVariants); | ||
var namedType = this.gql.namedType(node, TypeContext_1.UNRESOLVED_REFERENCE_NAME); | ||
this.ctx.markUnresolvedType(node.typeName, namedType.name); | ||
this.markUnresolvedType(node.typeName, namedType.name); | ||
return this.gql.nonNullType(node, namedType); | ||
@@ -1360,3 +1367,2 @@ } | ||
Extractor.prototype.findTag = function (node, tagName) { | ||
var _this = this; | ||
var tags = ts | ||
@@ -1370,3 +1376,3 @@ .getJSDocTags(node) | ||
var additionalTags = tags.slice(1).map(function (tag) { | ||
return _this.related(tag, "Additional tag"); | ||
return (0, DiagnosticError_1.tsRelated)(tag, "Additional tag"); | ||
}); | ||
@@ -1381,3 +1387,3 @@ var message = tagName === exports.IMPLEMENTS_TAG_DEPRECATED | ||
// It is a GraphQL best practice to model all fields as nullable. This allows | ||
// the server to handle field level exections by simply returning null for | ||
// the server to handle field level executions by simply returning null for | ||
// that field. | ||
@@ -1402,16 +1408,17 @@ // https://graphql.org/learn/best-practices/#nullability | ||
}; | ||
/* Grats directives */ | ||
Extractor.prototype.exportDirective = function (nameNode, filename, functionName) { | ||
return this.gql.constDirective(nameNode, this.gql.name(nameNode, serverDirectives_1.EXPORTED_DIRECTIVE), [ | ||
this.gql.constArgument(nameNode, this.gql.name(nameNode, serverDirectives_1.EXPORTED_FILENAME_ARG), this.gql.string(nameNode, filename)), | ||
this.gql.constArgument(nameNode, this.gql.name(nameNode, serverDirectives_1.EXPORTED_FUNCTION_NAME_ARG), this.gql.string(nameNode, functionName)), | ||
]); | ||
}; | ||
Extractor.prototype.fieldNameDirective = function (nameNode, name) { | ||
return this.gql.constDirective(nameNode, this.gql.name(nameNode, serverDirectives_1.METHOD_NAME_DIRECTIVE), [ | ||
this.gql.constArgument(nameNode, this.gql.name(nameNode, serverDirectives_1.METHOD_NAME_ARG), this.gql.string(nameNode, name)), | ||
]); | ||
}; | ||
return Extractor; | ||
}()); | ||
exports.Extractor = Extractor; | ||
function graphQLNameValidationMessage(name) { | ||
try { | ||
(0, graphql_1.assertName)(name); | ||
return null; | ||
} | ||
catch (e) { | ||
return e.message; | ||
} | ||
} | ||
// Trims any number of whitespace-only lines including any lines that simply | ||
// contain a `*` surrounded by whitespace. | ||
function trimTrailingCommentLines(text) { | ||
return text.replace(/(\s*\n\s*\*?\s*)+$/, ""); | ||
} |
@@ -1,7 +0,15 @@ | ||
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 } from "graphql"; | ||
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 } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { AbstractFieldDefinitionNode } from "./TypeContext"; | ||
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; | ||
}; | ||
export declare class GraphQLConstructor { | ||
sourceFile: ts.SourceFile; | ||
constructor(sourceFile: ts.SourceFile); | ||
exportedDirective(node: ts.Node, exported: ExportedMetadata): ConstDirectiveNode; | ||
propertyNameDirective(node: ts.Node, propertyName: PropertyNameMetadata): ConstDirectiveNode; | ||
asyncIterableDirective(node: ts.Node): ConstDirectiveNode; | ||
unionTypeDefinition(node: ts.Node, name: NameNode, types: NamedTypeNode[], description: StringValueNode | null): UnionTypeDefinitionNode; | ||
@@ -34,3 +42,3 @@ objectTypeDefinition(node: ts.Node, name: NameNode, fields: FieldDefinitionNode[], interfaces: NamedTypeNode[] | null, description: StringValueNode | null): ObjectTypeDefinitionNode; | ||
_loc(node: ts.Node): GraphQLLocation; | ||
_dummyToken(pos: number): Token; | ||
_dummyToken(sourceFile: ts.SourceFile, pos: number): Token; | ||
} |
"use strict"; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.GraphQLConstructor = void 0; | ||
var graphql_1 = require("graphql"); | ||
var metadataDirectives_1 = require("./metadataDirectives"); | ||
var GraphQLConstructor = /** @class */ (function () { | ||
function GraphQLConstructor(sourceFile) { | ||
this.sourceFile = sourceFile; | ||
function GraphQLConstructor() { | ||
} | ||
/* Metadata Directives */ | ||
GraphQLConstructor.prototype.exportedDirective = function (node, exported) { | ||
return (0, metadataDirectives_1.makeExportedDirective)(this._loc(node), exported); | ||
}; | ||
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)); | ||
}; | ||
/* Top Level Types */ | ||
@@ -16,3 +26,3 @@ GraphQLConstructor.prototype.unionTypeDefinition = function (node, name, types, description) { | ||
name: name, | ||
types: types | ||
types: types, | ||
}; | ||
@@ -28,3 +38,3 @@ }; | ||
fields: fields, | ||
interfaces: interfaces !== null && interfaces !== void 0 ? interfaces : undefined | ||
interfaces: interfaces !== null && interfaces !== void 0 ? interfaces : undefined, | ||
}; | ||
@@ -40,3 +50,3 @@ }; | ||
fields: fields, | ||
interfaces: interfaces !== null && interfaces !== void 0 ? interfaces : undefined | ||
interfaces: interfaces !== null && interfaces !== void 0 ? interfaces : undefined, | ||
}; | ||
@@ -50,3 +60,3 @@ }; | ||
name: name, | ||
values: values | ||
values: values, | ||
}; | ||
@@ -60,3 +70,3 @@ }; | ||
onType: onType, | ||
field: field | ||
field: field, | ||
}; | ||
@@ -73,3 +83,3 @@ }; | ||
arguments: args !== null && args !== void 0 ? args : undefined, | ||
directives: this._optionalList(directives) | ||
directives: this._optionalList(directives), | ||
}; | ||
@@ -88,3 +98,3 @@ }; | ||
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : undefined, | ||
directives: this._optionalList(directives) | ||
directives: this._optionalList(directives), | ||
}; | ||
@@ -98,3 +108,3 @@ }; | ||
name: name, | ||
directives: directives | ||
directives: directives, | ||
}; | ||
@@ -108,3 +118,3 @@ }; | ||
name: name, | ||
directives: undefined | ||
directives: undefined, | ||
}; | ||
@@ -119,3 +129,3 @@ }; | ||
fields: fields !== null && fields !== void 0 ? fields : undefined, | ||
directives: this._optionalList(directives) | ||
directives: this._optionalList(directives), | ||
}; | ||
@@ -131,3 +141,3 @@ }; | ||
loc: this._loc(node), | ||
name: this.name(node, value) | ||
name: this.name(node, value), | ||
}; | ||
@@ -166,3 +176,3 @@ }; | ||
name: name, | ||
arguments: this._optionalList(args) | ||
arguments: this._optionalList(args), | ||
}; | ||
@@ -179,3 +189,3 @@ }; | ||
}; | ||
GraphQLConstructor.prototype["null"] = function (node) { | ||
GraphQLConstructor.prototype.null = function (node) { | ||
return { kind: graphql_1.Kind.NULL, loc: this._loc(node) }; | ||
@@ -196,9 +206,10 @@ }; | ||
GraphQLConstructor.prototype._loc = function (node) { | ||
var source = new graphql_1.Source(this.sourceFile.text, this.sourceFile.fileName); | ||
var startToken = this._dummyToken(node.getStart()); | ||
var endToken = this._dummyToken(node.getEnd()); | ||
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 (pos) { | ||
var _a = this.sourceFile.getLineAndCharacterOfPosition(pos), line = _a.line, character = _a.character; | ||
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); | ||
@@ -205,0 +216,0 @@ }; |
@@ -1,3 +0,2 @@ | ||
import * as ts from "typescript"; | ||
export declare function getRelativeOutputPath(options: ts.ParsedCommandLine, sourceFile: ts.SourceFile): string; | ||
export declare function relativePath(absolute: string): string; | ||
export declare function resolveRelativePath(relativePath: string): string; |
"use strict"; | ||
exports.__esModule = true; | ||
exports.resolveRelativePath = exports.getRelativeOutputPath = void 0; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.resolveRelativePath = exports.relativePath = void 0; | ||
var path_1 = require("path"); | ||
var ts = require("typescript"); | ||
// Grats parses TypeScript files and finds resolvers. If the field resolver is a | ||
@@ -13,16 +12,7 @@ // named export, Grats needs to be able to import that file during execution. | ||
// step and the runtime can agree on. This path is that thing. | ||
var gratsRoot = __dirname; | ||
function getRelativeOutputPath(options, sourceFile) { | ||
var fileNames = ts.getOutputFileNames(options, sourceFile.fileName, true); | ||
// ts.getOutputFileNames returns a list of files that includes both the .d.ts | ||
// and .js files. | ||
var jsFileNames = fileNames.filter(function (fileName) { return fileName.endsWith(".js"); }); | ||
if (jsFileNames.length !== 1) { | ||
throw new Error("Grats: Expected ts.getOutputFileNames to return exactly one `.js` file. " + | ||
"Found ".concat(jsFileNames.length, "}. This is a bug in Grats. I'd appreciate it if ") + | ||
"you could open an issue."); | ||
} | ||
return (0, path_1.relative)(gratsRoot, fileNames[0]); | ||
var gratsRoot = (0, path_1.join)(__dirname, "../.."); | ||
function relativePath(absolute) { | ||
return (0, path_1.relative)(gratsRoot, absolute); | ||
} | ||
exports.getRelativeOutputPath = getRelativeOutputPath; | ||
exports.relativePath = relativePath; | ||
function resolveRelativePath(relativePath) { | ||
@@ -29,0 +19,0 @@ return (0, path_1.resolve)(gratsRoot, relativePath); |
@@ -1,10 +0,7 @@ | ||
import { GraphQLSchema } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { ParsedCommandLineGrats } from "./lib"; | ||
import { ReportableDiagnostics, Result } from "./utils/DiagnosticError"; | ||
export { printSDLWithoutDirectives } from "./printSchema"; | ||
export * from "./Types"; | ||
export * from "./lib"; | ||
type RuntimeOptions = { | ||
emitSchemaFile?: string; | ||
}; | ||
export declare function extractGratsSchemaAtRuntime(runtimeOptions: RuntimeOptions): GraphQLSchema; | ||
export declare function buildSchemaFromSDL(sdl: string): GraphQLSchema; | ||
export declare function getParsedTsConfig(configPath?: string): ts.ParsedCommandLine; | ||
export { codegen } from "./codegen"; | ||
export declare function getParsedTsConfig(configFile: string): Result<ParsedCommandLineGrats, ReportableDiagnostics>; |
@@ -16,39 +16,15 @@ "use strict"; | ||
}; | ||
exports.__esModule = true; | ||
exports.getParsedTsConfig = exports.buildSchemaFromSDL = exports.extractGratsSchemaAtRuntime = void 0; | ||
var graphql_1 = require("graphql"); | ||
var utils_1 = require("@graphql-tools/utils"); | ||
var fs = require("fs"); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getParsedTsConfig = exports.codegen = exports.printSDLWithoutDirectives = void 0; | ||
var ts = require("typescript"); | ||
var lib_1 = require("./lib"); | ||
var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var printSchema_1 = require("./printSchema"); | ||
Object.defineProperty(exports, "printSDLWithoutDirectives", { enumerable: true, get: function () { return printSchema_1.printSDLWithoutDirectives; } }); | ||
__exportStar(require("./Types"), exports); | ||
__exportStar(require("./lib"), exports); | ||
// Build an executable schema from a set of files. Note that if extraction | ||
// fails, this function will exit the process and print a helpful error | ||
// message. | ||
function extractGratsSchemaAtRuntime(runtimeOptions) { | ||
var parsedTsConfig = getParsedTsConfig(); | ||
var schemaResult = (0, lib_1.buildSchemaResult)(parsedTsConfig); | ||
if (schemaResult.kind === "ERROR") { | ||
console.error(schemaResult.err.formatDiagnosticsWithColorAndContext()); | ||
process.exit(1); | ||
} | ||
var runtimeSchema = schemaResult.value; | ||
if (runtimeOptions.emitSchemaFile) { | ||
runtimeSchema = (0, graphql_1.lexicographicSortSchema)(runtimeSchema); | ||
var sdl = (0, utils_1.printSchemaWithDirectives)(runtimeSchema, { assumeValid: true }); | ||
var filePath = runtimeOptions.emitSchemaFile; | ||
fs.writeFileSync(filePath, sdl); | ||
} | ||
return runtimeSchema; | ||
} | ||
exports.extractGratsSchemaAtRuntime = extractGratsSchemaAtRuntime; | ||
function buildSchemaFromSDL(sdl) { | ||
var schema = (0, graphql_1.buildSchema)(sdl); | ||
return (0, lib_1.applyServerDirectives)(schema); | ||
} | ||
exports.buildSchemaFromSDL = buildSchemaFromSDL; | ||
var codegen_1 = require("./codegen"); | ||
Object.defineProperty(exports, "codegen", { enumerable: true, get: function () { return codegen_1.codegen; } }); | ||
// #FIXME: Report diagnostics instead of throwing! | ||
function getParsedTsConfig(configPath) { | ||
var configFile = configPath || ts.findConfigFile(process.cwd(), ts.sys.fileExists); | ||
function getParsedTsConfig(configFile) { | ||
if (!configFile) { | ||
@@ -60,7 +36,10 @@ throw new Error("Grats: Could not find tsconfig.json"); | ||
var parsed = ts.getParsedCommandLineOfConfigFile(configFile, undefined, configFileHost); | ||
if (!parsed || parsed.errors.length > 0) { | ||
throw new Error("Grats: Could not parse tsconfig.json"); | ||
if (!parsed) { | ||
throw new Error("Grats: Could not locate tsconfig.json"); | ||
} | ||
return parsed; | ||
if (parsed.errors.length > 0) { | ||
return (0, DiagnosticError_1.err)(DiagnosticError_1.ReportableDiagnostics.fromDiagnostics(parsed.errors)); | ||
} | ||
return (0, DiagnosticError_1.ok)((0, lib_1.validateGratsOptions)(parsed)); | ||
} | ||
exports.getParsedTsConfig = getParsedTsConfig; |
@@ -1,3 +0,3 @@ | ||
import { GratsDefinitionNode, TypeContext } from "./TypeContext"; | ||
import { DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import { GratsDefinitionNode } from "./GraphQLConstructor"; | ||
import { TypeContext } from "./TypeContext"; | ||
import { DefaultMap } from "./utils/helpers"; | ||
@@ -12,2 +12,2 @@ export type InterfaceImplementor = { | ||
*/ | ||
export declare function computeInterfaceMap(typeContext: TypeContext, docs: GratsDefinitionNode[]): DiagnosticsResult<InterfaceMap>; | ||
export declare function computeInterfaceMap(typeContext: TypeContext, docs: GratsDefinitionNode[]): InterfaceMap; |
@@ -13,5 +13,4 @@ "use strict"; | ||
}; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.computeInterfaceMap = void 0; | ||
var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var helpers_1 = require("./utils/helpers"); | ||
@@ -30,3 +29,2 @@ var graphql_1 = require("graphql"); | ||
}; | ||
var errors = []; | ||
try { | ||
@@ -48,3 +46,3 @@ for (var docs_1 = __values(docs), docs_1_1 = docs_1.next(); !docs_1_1.done; docs_1_1 = docs_1.next()) { | ||
kind: "INTERFACE", | ||
name: doc.name.value | ||
name: doc.name.value, | ||
}); | ||
@@ -56,3 +54,3 @@ } | ||
try { | ||
if (_g && !_g.done && (_b = _f["return"])) _b.call(_f); | ||
if (_g && !_g.done && (_b = _f.return)) _b.call(_f); | ||
} | ||
@@ -78,3 +76,3 @@ finally { if (e_2) throw e_2.error; } | ||
try { | ||
if (_j && !_j.done && (_c = _h["return"])) _c.call(_h); | ||
if (_j && !_j.done && (_c = _h.return)) _c.call(_h); | ||
} | ||
@@ -90,11 +88,8 @@ finally { if (e_3) throw e_3.error; } | ||
try { | ||
if (docs_1_1 && !docs_1_1.done && (_a = docs_1["return"])) _a.call(docs_1); | ||
if (docs_1_1 && !docs_1_1.done && (_a = docs_1.return)) _a.call(docs_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
if (errors.length > 0) { | ||
return (0, DiagnosticError_1.err)(errors); | ||
} | ||
return (0, DiagnosticError_1.ok)(graph); | ||
return graph; | ||
} | ||
exports.computeInterfaceMap = computeInterfaceMap; |
@@ -1,10 +0,13 @@ | ||
import { GraphQLSchema } from "graphql"; | ||
import { Result, ReportableDiagnostics } from "./utils/DiagnosticError"; | ||
import { DocumentNode, GraphQLSchema } from "graphql"; | ||
import { DiagnosticsResult, Result, ReportableDiagnostics } from "./utils/DiagnosticError"; | ||
import * as ts from "typescript"; | ||
export { applyServerDirectives } from "./serverDirectives"; | ||
export type ConfigOptions = { | ||
nullableByDefault?: boolean; | ||
reportTypeScriptTypeErrors?: boolean; | ||
}; | ||
export declare function buildSchemaResult(options: ts.ParsedCommandLine): Result<GraphQLSchema, ReportableDiagnostics>; | ||
export declare function buildSchemaResultWithHost(options: ts.ParsedCommandLine, compilerHost: ts.CompilerHost): Result<GraphQLSchema, ReportableDiagnostics>; | ||
import { ExtractionSnapshot } from "./Extractor"; | ||
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>; | ||
/** | ||
* Given a merged snapshot representing the whole program, construct a GraphQL | ||
* schema document with metadata directives attached. | ||
*/ | ||
export declare function docFromSnapshot(program: ts.Program, host: ts.CompilerHost, snapshot: ExtractionSnapshot): DiagnosticsResult<DocumentNode>; |
"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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
@@ -24,2 +13,5 @@ if (k2 === undefined) k2 = 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) { | ||
@@ -36,14 +28,36 @@ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
}; | ||
exports.__esModule = true; | ||
exports.buildSchemaResultWithHost = exports.buildSchemaResult = exports.applyServerDirectives = void 0; | ||
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.docFromSnapshot = exports.buildSchemaResultWithHost = exports.buildSchemaResult = void 0; | ||
var graphql_1 = require("graphql"); | ||
var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var ts = require("typescript"); | ||
var Extractor_1 = require("./Extractor"); | ||
var TypeContext_1 = require("./TypeContext"); | ||
var validate_1 = require("graphql/validation/validate"); | ||
var serverDirectives_1 = require("./serverDirectives"); | ||
var validateTypenames_1 = require("./validations/validateTypenames"); | ||
var snapshotsFromProgram_1 = require("./transforms/snapshotsFromProgram"); | ||
var validateMergedInterfaces_1 = require("./validations/validateMergedInterfaces"); | ||
var validateContextReferences_1 = require("./validations/validateContextReferences"); | ||
var metadataDirectives_1 = require("./metadataDirectives"); | ||
var helpers_1 = require("./utils/helpers"); | ||
var serverDirectives_2 = require("./serverDirectives"); | ||
__createBinding(exports, serverDirectives_2, "applyServerDirectives"); | ||
var addInterfaceFields_1 = require("./transforms/addInterfaceFields"); | ||
var filterNonGqlInterfaces_1 = require("./transforms/filterNonGqlInterfaces"); | ||
var resolveTypes_1 = require("./transforms/resolveTypes"); | ||
var validateAsyncIterable_1 = require("./validations/validateAsyncIterable"); | ||
__exportStar(require("./gratsConfig"), exports); | ||
// Construct a schema, using GraphQL schema language | ||
@@ -60,81 +74,65 @@ // Exported for tests that want to intercept diagnostic errors. | ||
function buildSchemaResultWithHost(options, compilerHost) { | ||
var gratsOptions = parseGratsOptions(options); | ||
var schemaResult = extractSchema(options, gratsOptions, compilerHost); | ||
var schemaResult = extractSchema(options, compilerHost); | ||
if (schemaResult.kind === "ERROR") { | ||
return (0, DiagnosticError_1.err)(new DiagnosticError_1.ReportableDiagnostics(compilerHost, schemaResult.err)); | ||
} | ||
return (0, DiagnosticError_1.ok)((0, serverDirectives_1.applyServerDirectives)(schemaResult.value)); | ||
return (0, DiagnosticError_1.ok)(schemaResult.value); | ||
} | ||
exports.buildSchemaResultWithHost = buildSchemaResultWithHost; | ||
// TODO: Make this return diagnostics | ||
function parseGratsOptions(options) { | ||
var _a, _b; | ||
var gratsOptions = __assign({}, ((_b = (_a = options.raw) === null || _a === void 0 ? void 0 : _a.grats) !== null && _b !== void 0 ? _b : {})); | ||
if (gratsOptions.nullableByDefault === undefined) { | ||
gratsOptions.nullableByDefault = true; | ||
function extractSchema(options, host) { | ||
var program = ts.createProgram(options.fileNames, options.options, host); | ||
var snapshotsResult = (0, snapshotsFromProgram_1.snapshotsFromProgram)(program, options); | ||
if (snapshotsResult.kind === "ERROR") { | ||
return snapshotsResult; | ||
} | ||
else if (typeof gratsOptions.nullableByDefault !== "boolean") { | ||
throw new Error("Grats: The Grats config option `nullableByDefault` must be a boolean if provided."); | ||
var snapshot = reduceSnapshots(snapshotsResult.value); | ||
var docResult = docFromSnapshot(program, host, snapshot); | ||
if (docResult.kind === "ERROR") { | ||
return docResult; | ||
} | ||
if (gratsOptions.reportTypeScriptTypeErrors === undefined) { | ||
gratsOptions.reportTypeScriptTypeErrors = false; | ||
return buildSchemaFromDocumentNode(docResult.value, snapshot.typesWithTypenameField); | ||
} | ||
// Given a SDL AST, build and validate a GraphQLSchema. | ||
function buildSchemaFromDocumentNode(doc, typesWithTypenameField) { | ||
// TODO: Currently this does not detect definitions that shadow builtins | ||
// (`String`, `Int`, etc). However, if we pass a second param (extending an | ||
// existing schema) we do! So, we should find a way to validate that we don't | ||
// shadow builtins. | ||
var validationErrors = (0, validate_1.validateSDL)(doc).map(function (e) { | ||
return (0, DiagnosticError_1.graphQlErrorToDiagnostic)(e); | ||
}); | ||
if (validationErrors.length > 0) { | ||
return (0, DiagnosticError_1.err)(validationErrors); | ||
} | ||
else if (typeof gratsOptions.reportTypeScriptTypeErrors !== "boolean") { | ||
throw new Error("Grats: The Grats config option `reportTypeScriptTypeErrors` must be a boolean if provided"); | ||
var schema = (0, graphql_1.buildASTSchema)(doc, { assumeValidSDL: true }); | ||
var diagnostics = (0, graphql_1.validateSchema)(schema) | ||
// FIXME: Handle case where query is not defined (no location) | ||
.filter(function (e) { return e.source && e.locations && e.positions; }) | ||
.map(function (e) { return (0, DiagnosticError_1.graphQlErrorToDiagnostic)(e); }); | ||
if (diagnostics.length > 0) { | ||
return (0, DiagnosticError_1.err)(diagnostics); | ||
} | ||
// FIXME: Check for unknown options | ||
return gratsOptions; | ||
var typenameDiagnostics = (0, validateTypenames_1.validateTypenames)(schema, typesWithTypenameField); | ||
if (typenameDiagnostics.length > 0) | ||
return (0, DiagnosticError_1.err)(typenameDiagnostics); | ||
return (0, DiagnosticError_1.ok)(schema); | ||
} | ||
function extractSchema(options, gratsOptions, host) { | ||
/** | ||
* Given a merged snapshot representing the whole program, construct a GraphQL | ||
* schema document with metadata directives attached. | ||
*/ | ||
function docFromSnapshot(program, host, snapshot) { | ||
var e_1, _a, e_2, _b; | ||
var program = ts.createProgram(options.fileNames, options.options, host); | ||
var checker = program.getTypeChecker(); | ||
var ctx = new TypeContext_1.TypeContext(options, checker, host); | ||
var definitions = Array.from(serverDirectives_1.DIRECTIVES_AST.definitions); | ||
var errors = []; | ||
var ctx = new TypeContext_1.TypeContext(checker, host); | ||
// Validate the snapshot | ||
var mergedResult = (0, DiagnosticError_1.combineResults)((0, validateMergedInterfaces_1.validateMergedInterfaces)(checker, snapshot.interfaceDeclarations), (0, validateContextReferences_1.validateContextReferences)(ctx, snapshot.contextReferences)); | ||
if (mergedResult.kind === "ERROR") { | ||
return mergedResult; | ||
} | ||
try { | ||
for (var _c = __values(program.getSourceFiles()), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var sourceFile = _d.value; | ||
// If the file doesn't contain any GraphQL definitions, skip it. | ||
if (!/@gql/i.test(sourceFile.text)) { | ||
continue; | ||
} | ||
if (gratsOptions.reportTypeScriptTypeErrors) { | ||
// If the user asked for us to report TypeScript errors, then we'll report them. | ||
var typeErrors = ts.getPreEmitDiagnostics(program, sourceFile); | ||
if (typeErrors.length > 0) { | ||
(0, helpers_1.extend)(errors, typeErrors); | ||
continue; | ||
} | ||
} | ||
else { | ||
// Otherwise, we will only report syntax errors, since they will prevent us from | ||
// extracting any GraphQL definitions. | ||
var syntaxErrors = program.getSyntacticDiagnostics(sourceFile); | ||
if (syntaxErrors.length > 0) { | ||
// It's not very helpful to report multiple syntax errors, so just report | ||
// the first one. | ||
errors.push(syntaxErrors[0]); | ||
continue; | ||
} | ||
} | ||
var extractor = new Extractor_1.Extractor(sourceFile, ctx, gratsOptions); | ||
var extractedResult = extractor.extract(); | ||
if (extractedResult.kind === "ERROR") { | ||
(0, helpers_1.extend)(errors, extractedResult.err); | ||
continue; | ||
} | ||
try { | ||
for (var _e = (e_2 = void 0, __values(extractedResult.value)), _f = _e.next(); !_f.done; _f = _e.next()) { | ||
var definition = _f.value; | ||
definitions.push(definition); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (_f && !_f.done && (_b = _e["return"])) _b.call(_e); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
// Propagate snapshot data to type context | ||
for (var _c = __values(snapshot.unresolvedNames), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var _e = __read(_d.value, 2), node = _e[0], typeName = _e[1]; | ||
ctx.markUnresolvedType(node, typeName); | ||
} | ||
@@ -145,66 +143,62 @@ } | ||
try { | ||
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c); | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
if (errors.length > 0) { | ||
return (0, DiagnosticError_1.err)(errors); | ||
try { | ||
for (var _f = __values(snapshot.nameDefinitions), _g = _f.next(); !_g.done; _g = _f.next()) { | ||
var _h = __read(_g.value, 2), node = _h[0], definition = _h[1]; | ||
ctx.recordTypeName(node, definition.name, definition.kind); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (_g && !_g.done && (_b = _f.return)) _b.call(_f); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
// Fixup the schema SDL | ||
var definitions = Array.from(metadataDirectives_1.DIRECTIVES_AST.definitions); | ||
(0, helpers_1.extend)(definitions, snapshot.definitions); | ||
// If you define a field on an interface using the functional style, we need to add | ||
// that field to each concrete type as well. This must be done after all types are created, | ||
// but before we validate the schema. | ||
var definitionsResult = ctx.handleAbstractDefinitions(definitions); | ||
var definitionsResult = (0, addInterfaceFields_1.addInterfaceFields)(ctx, definitions); | ||
if (definitionsResult.kind === "ERROR") { | ||
return definitionsResult; | ||
} | ||
var docResult = ctx.resolveTypes({ | ||
var filteredDoc = (0, filterNonGqlInterfaces_1.filterNonGqlInterfaces)(ctx, { | ||
kind: graphql_1.Kind.DOCUMENT, | ||
definitions: definitionsResult.value | ||
definitions: definitionsResult.value, | ||
}); | ||
var docResult = (0, resolveTypes_1.resolveTypes)(ctx, filteredDoc); | ||
if (docResult.kind === "ERROR") | ||
return docResult; | ||
var doc = docResult.value; | ||
// TODO: Currently this does not detect definitions that shadow builtins | ||
// (`String`, `Int`, etc). However, if we pass a second param (extending an | ||
// existing schema) we do! So, we should find a way to validate that we don't | ||
// shadow builtins. | ||
var validationErrors = (0, validate_1.validateSDL)(doc).map(function (e) { | ||
return (0, DiagnosticError_1.graphQlErrorToDiagnostic)(e); | ||
}); | ||
if (validationErrors.length > 0) { | ||
return (0, DiagnosticError_1.err)(validationErrors); | ||
var subscriptionsValidationResult = (0, validateAsyncIterable_1.validateAsyncIterable)(doc); | ||
if (subscriptionsValidationResult.kind === "ERROR") { | ||
return subscriptionsValidationResult; | ||
} | ||
var schema = (0, graphql_1.buildASTSchema)(doc, { assumeValidSDL: true }); | ||
var diagnostics = (0, graphql_1.validateSchema)(schema) | ||
// FIXME: Handle case where query is not defined (no location) | ||
.filter(function (e) { return e.source && e.locations && e.positions; }) | ||
.map(function (e) { return (0, DiagnosticError_1.graphQlErrorToDiagnostic)(e); }); | ||
if (diagnostics.length > 0) { | ||
return (0, DiagnosticError_1.err)(diagnostics); | ||
} | ||
var typenameDiagnostics = validateTypename(schema, ctx); | ||
if (typenameDiagnostics.length > 0) | ||
return (0, DiagnosticError_1.err)(typenameDiagnostics); | ||
return (0, DiagnosticError_1.ok)(schema); | ||
return (0, DiagnosticError_1.ok)(doc); | ||
} | ||
function validateTypename(schema, ctx) { | ||
var e_3, _a, e_4, _b; | ||
var _c, _d; | ||
var typenameDiagnostics = []; | ||
var abstractTypes = Object.values(schema.getTypeMap()).filter(graphql_1.isAbstractType); | ||
exports.docFromSnapshot = docFromSnapshot; | ||
// Given a list of snapshots, merge them into a single snapshot. | ||
function reduceSnapshots(snapshots) { | ||
var e_3, _a, e_4, _b, e_5, _c, e_6, _d, e_7, _e, e_8, _f, e_9, _g; | ||
var result = { | ||
definitions: [], | ||
nameDefinitions: new Map(), | ||
unresolvedNames: new Map(), | ||
contextReferences: [], | ||
typesWithTypenameField: new Set(), | ||
interfaceDeclarations: [], | ||
}; | ||
try { | ||
for (var abstractTypes_1 = __values(abstractTypes), abstractTypes_1_1 = abstractTypes_1.next(); !abstractTypes_1_1.done; abstractTypes_1_1 = abstractTypes_1.next()) { | ||
var type = abstractTypes_1_1.value; | ||
// TODO: If we already implement resolveType, we don't need to check implementors | ||
var typeImplementors = schema.getPossibleTypes(type).filter(graphql_1.isType); | ||
for (var snapshots_1 = __values(snapshots), snapshots_1_1 = snapshots_1.next(); !snapshots_1_1.done; snapshots_1_1 = snapshots_1.next()) { | ||
var snapshot = snapshots_1_1.value; | ||
try { | ||
for (var typeImplementors_1 = (e_4 = void 0, __values(typeImplementors)), typeImplementors_1_1 = typeImplementors_1.next(); !typeImplementors_1_1.done; typeImplementors_1_1 = typeImplementors_1.next()) { | ||
var implementor = typeImplementors_1_1.value; | ||
if (!ctx.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.diagnosticAtGraphQLLocation)("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."), loc)); | ||
} | ||
for (var _h = (e_4 = void 0, __values(snapshot.definitions)), _j = _h.next(); !_j.done; _j = _h.next()) { | ||
var definition = _j.value; | ||
result.definitions.push(definition); | ||
} | ||
@@ -215,6 +209,71 @@ } | ||
try { | ||
if (typeImplementors_1_1 && !typeImplementors_1_1.done && (_b = typeImplementors_1["return"])) _b.call(typeImplementors_1); | ||
if (_j && !_j.done && (_b = _h.return)) _b.call(_h); | ||
} | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
try { | ||
for (var _k = (e_5 = void 0, __values(snapshot.nameDefinitions)), _l = _k.next(); !_l.done; _l = _k.next()) { | ||
var _m = __read(_l.value, 2), node = _m[0], definition = _m[1]; | ||
result.nameDefinitions.set(node, definition); | ||
} | ||
} | ||
catch (e_5_1) { e_5 = { error: e_5_1 }; } | ||
finally { | ||
try { | ||
if (_l && !_l.done && (_c = _k.return)) _c.call(_k); | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
} | ||
try { | ||
for (var _o = (e_6 = void 0, __values(snapshot.unresolvedNames)), _p = _o.next(); !_p.done; _p = _o.next()) { | ||
var _q = __read(_p.value, 2), node = _q[0], typeName = _q[1]; | ||
result.unresolvedNames.set(node, typeName); | ||
} | ||
} | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
finally { | ||
try { | ||
if (_p && !_p.done && (_d = _o.return)) _d.call(_o); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
try { | ||
for (var _r = (e_7 = void 0, __values(snapshot.contextReferences)), _s = _r.next(); !_s.done; _s = _r.next()) { | ||
var contextReference = _s.value; | ||
result.contextReferences.push(contextReference); | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (_s && !_s.done && (_e = _r.return)) _e.call(_r); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
try { | ||
for (var _t = (e_8 = void 0, __values(snapshot.typesWithTypenameField)), _u = _t.next(); !_u.done; _u = _t.next()) { | ||
var typeName = _u.value; | ||
result.typesWithTypenameField.add(typeName); | ||
} | ||
} | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (_u && !_u.done && (_f = _t.return)) _f.call(_t); | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
try { | ||
for (var _v = (e_9 = void 0, __values(snapshot.interfaceDeclarations)), _w = _v.next(); !_w.done; _w = _v.next()) { | ||
var interfaceDeclaration = _w.value; | ||
result.interfaceDeclarations.push(interfaceDeclaration); | ||
} | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
finally { | ||
try { | ||
if (_w && !_w.done && (_g = _v.return)) _g.call(_v); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
} | ||
} | ||
@@ -225,7 +284,7 @@ } | ||
try { | ||
if (abstractTypes_1_1 && !abstractTypes_1_1.done && (_a = abstractTypes_1["return"])) _a.call(abstractTypes_1); | ||
if (snapshots_1_1 && !snapshots_1_1.done && (_a = snapshots_1.return)) _a.call(snapshots_1); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
return typenameDiagnostics; | ||
return result; | ||
} |
"use strict"; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.locate = void 0; | ||
@@ -4,0 +4,0 @@ var graphql_1 = require("graphql"); |
@@ -60,7 +60,6 @@ "use strict"; | ||
}; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var path = require("path"); | ||
var TestRunner_1 = require("./TestRunner"); | ||
var lib_1 = require("../lib"); | ||
var utils_1 = require("@graphql-tools/utils"); | ||
var ts = require("typescript"); | ||
@@ -71,2 +70,7 @@ var graphql_1 = require("graphql"); | ||
var DiagnosticError_1 = require("../utils/DiagnosticError"); | ||
var fs_1 = require("fs"); | ||
var codegen_1 = require("../codegen"); | ||
var utils_1 = require("@graphql-tools/utils"); | ||
var jest_diff_1 = require("jest-diff"); | ||
var printSchema_1 = require("../printSchema"); | ||
var program = new commander_1.Command(); | ||
@@ -81,3 +85,3 @@ program | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var filterRegex, failures, testDirs_1, testDirs_1_1, _b, fixturesDir_1, transformer, runner, e_1_1; | ||
var filterRegex, failures, testDirs_1, testDirs_1_1, _b, fixturesDir_1, transformer, testFilePattern, ignoreFilePattern, runner, e_1_1; | ||
var e_1, _c; | ||
@@ -96,4 +100,4 @@ return __generator(this, function (_d) { | ||
if (!!testDirs_1_1.done) return [3 /*break*/, 5]; | ||
_b = testDirs_1_1.value, fixturesDir_1 = _b.fixturesDir, transformer = _b.transformer; | ||
runner = new TestRunner_1["default"](fixturesDir_1, !!write, filterRegex, transformer); | ||
_b = testDirs_1_1.value, fixturesDir_1 = _b.fixturesDir, transformer = _b.transformer, testFilePattern = _b.testFilePattern, ignoreFilePattern = _b.ignoreFilePattern; | ||
runner = new TestRunner_1.default(fixturesDir_1, !!write, filterRegex, testFilePattern, ignoreFilePattern, transformer); | ||
return [4 /*yield*/, runner.run()]; | ||
@@ -113,3 +117,3 @@ case 3: | ||
try { | ||
if (testDirs_1_1 && !testDirs_1_1.done && (_c = testDirs_1["return"])) _c.call(testDirs_1); | ||
if (testDirs_1_1 && !testDirs_1_1.done && (_c = testDirs_1.return)) _c.call(testDirs_1); | ||
} | ||
@@ -133,6 +137,9 @@ finally { if (e_1) throw e_1.error; } | ||
fixturesDir: fixturesDir, | ||
testFilePattern: /\.ts$/, | ||
ignoreFilePattern: null, | ||
transformer: function (code, fileName) { | ||
var firstLine = code.split("\n")[0]; | ||
var options = { | ||
nullableByDefault: true | ||
nullableByDefault: true, | ||
schemaHeader: null, | ||
}; | ||
@@ -144,11 +151,14 @@ if (firstLine.startsWith("// {")) { | ||
} | ||
var files = ["".concat(fixturesDir, "/").concat(fileName), "src/Types.ts"]; | ||
var parsedOptions = { | ||
var files = [ | ||
"".concat(fixturesDir, "/").concat(fileName), | ||
path.join(__dirname, "../Types.ts"), | ||
]; | ||
var parsedOptions = (0, lib_1.validateGratsOptions)({ | ||
options: {}, | ||
raw: { | ||
grats: options | ||
grats: options, | ||
}, | ||
errors: [], | ||
fileNames: files | ||
}; | ||
fileNames: files, | ||
}); | ||
// https://stackoverflow.com/a/66604532/1263117 | ||
@@ -162,2 +172,4 @@ var compilerHost = ts.createCompilerHost(parsedOptions.options, | ||
} | ||
// We run codegen here just ensure that it doesn't throw. | ||
var executableSchema = (0, codegen_1.codegen)(schemaResult.value, "".concat(fixturesDir, "/").concat(fileName)); | ||
var LOCATION_REGEX = /^\/\/ Locate: (.*)/; | ||
@@ -171,32 +183,29 @@ var locationMatch = code.match(LOCATION_REGEX); | ||
return new DiagnosticError_1.ReportableDiagnostics(compilerHost, [ | ||
(0, DiagnosticError_1.diagnosticAtGraphQLLocation)("Located here", locResult.value), | ||
(0, DiagnosticError_1.gqlErr)(locResult.value, "Located here"), | ||
]).formatDiagnosticsWithContext(); | ||
} | ||
else { | ||
return (0, utils_1.printSchemaWithDirectives)(schemaResult.value, { | ||
assumeValid: true | ||
var sdl = (0, utils_1.printSchemaWithDirectives)(schemaResult.value, { | ||
assumeValid: true, | ||
}); | ||
return "-- SDL --\n".concat(sdl, "\n-- TypeScript --\n").concat(executableSchema); | ||
} | ||
} | ||
}, | ||
}, | ||
{ | ||
fixturesDir: integrationFixturesDir, | ||
testFilePattern: /index.ts$/, | ||
ignoreFilePattern: /schema.ts$/, | ||
transformer: function (code, fileName) { return __awaiter(void 0, void 0, void 0, function () { | ||
var filePath, server, options, files, parsedOptions, schemaResult, schema, data; | ||
var filePath, schemaPath, options, files, parsedOptions, schemaResult, tsSchema, server, schemaModule, actualSchema, schemaDiff, data; | ||
return __generator(this, function (_a) { | ||
var _b; | ||
switch (_a.label) { | ||
case 0: | ||
filePath = "".concat(integrationFixturesDir, "/").concat(fileName); | ||
return [4 /*yield*/, (_b = filePath, Promise.resolve().then(function () { return require(_b); }))]; | ||
case 1: | ||
server = _a.sent(); | ||
if (server.query == null || typeof server.query !== "string") { | ||
throw new Error("Expected `".concat(filePath, "` to export a query text as `query`")); | ||
} | ||
schemaPath = path.join(path.dirname(filePath), "schema.ts"); | ||
options = { | ||
nullableByDefault: true | ||
nullableByDefault: true, | ||
}; | ||
files = [filePath, "src/Types.ts"]; | ||
parsedOptions = { | ||
files = [filePath, path.join(__dirname, "../Types.ts")]; | ||
parsedOptions = (0, lib_1.validateGratsOptions)({ | ||
options: { | ||
@@ -206,10 +215,10 @@ // Required to enable ts-node to locate function exports | ||
outDir: "dist", | ||
configFilePath: "tsconfig.json" | ||
configFilePath: "tsconfig.json", | ||
}, | ||
raw: { | ||
grats: options | ||
grats: options, | ||
}, | ||
errors: [], | ||
fileNames: files | ||
}; | ||
fileNames: files, | ||
}); | ||
schemaResult = (0, lib_1.buildSchemaResult)(parsedOptions); | ||
@@ -219,9 +228,26 @@ if (schemaResult.kind === "ERROR") { | ||
} | ||
schema = schemaResult.value; | ||
tsSchema = (0, codegen_1.codegen)(schemaResult.value, schemaPath); | ||
(0, fs_1.writeFileSync)(schemaPath, tsSchema); | ||
return [4 /*yield*/, Promise.resolve("".concat(filePath)).then(function (s) { return require(s); })]; | ||
case 1: | ||
server = _a.sent(); | ||
if (server.query == null || typeof server.query !== "string") { | ||
throw new Error("Expected `".concat(filePath, "` to export a query text as `query`")); | ||
} | ||
return [4 /*yield*/, Promise.resolve("".concat(schemaPath)).then(function (s) { return require(s); })]; | ||
case 2: | ||
schemaModule = _a.sent(); | ||
actualSchema = schemaModule.getSchema(); | ||
schemaDiff = compareSchemas(actualSchema, schemaResult.value); | ||
if (schemaDiff) { | ||
console.log(schemaDiff); | ||
// TODO: Make this an actual test failure, not an error | ||
throw new Error("The codegen schema does not match the SDL schema."); | ||
} | ||
return [4 /*yield*/, (0, graphql_1.graphql)({ | ||
schema: schema, | ||
schema: actualSchema, | ||
source: server.query, | ||
rootValue: server.Query != null ? new server.Query() : null | ||
variableValues: server.variables, | ||
})]; | ||
case 2: | ||
case 3: | ||
data = _a.sent(); | ||
@@ -231,5 +257,14 @@ return [2 /*return*/, JSON.stringify(data, null, 2)]; | ||
}); | ||
}); } | ||
}); }, | ||
}, | ||
]; | ||
// Returns null if the schemas are equal, otherwise returns a string diff. | ||
function compareSchemas(actual, expected) { | ||
var actualSDL = (0, printSchema_1.printSDLWithoutDirectives)(actual); | ||
var expectedSDL = (0, printSchema_1.printSDLWithoutDirectives)(expected); | ||
if (actualSDL === expectedSDL) { | ||
return null; | ||
} | ||
return (0, jest_diff_1.diff)(expectedSDL, actualSDL); | ||
} | ||
program.parse(); |
@@ -15,3 +15,3 @@ type Transformer = (code: string, filename: string) => Promise<string> | string; | ||
_transformer: Transformer; | ||
constructor(fixturesDir: string, write: boolean, filter: string | null, transformer: Transformer); | ||
constructor(fixturesDir: string, write: boolean, filter: string | null, testFilePattern: RegExp, ignoreFilePattern: RegExp | null, transformer: Transformer); | ||
run(): Promise<boolean>; | ||
@@ -18,0 +18,0 @@ _testFixture(fixture: string): Promise<void>; |
@@ -49,3 +49,3 @@ "use strict"; | ||
}; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var fs = require("fs"); | ||
@@ -60,3 +60,3 @@ var path = require("path"); | ||
var TestRunner = /** @class */ (function () { | ||
function TestRunner(fixturesDir, write, filter, transformer) { | ||
function TestRunner(fixturesDir, write, filter, testFilePattern, ignoreFilePattern, transformer) { | ||
var e_1, _a; | ||
@@ -74,3 +74,3 @@ this._testFixtures = []; | ||
var fileName = _c.value; | ||
if (fileName.endsWith(".ts")) { | ||
if (testFilePattern.test(fileName)) { | ||
this._testFixtures.push(fileName); | ||
@@ -82,3 +82,3 @@ var filePath = path.join(fixturesDir, fileName); | ||
} | ||
else { | ||
else if (!ignoreFilePattern || !ignoreFilePattern.test(fileName)) { | ||
this._otherFiles.add(fileName); | ||
@@ -91,3 +91,3 @@ } | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -125,3 +125,3 @@ finally { if (e_1) throw e_1.error; } | ||
try { | ||
if (_b && !_b.done && (_g = _a["return"])) _g.call(_a); | ||
if (_b && !_b.done && (_g = _a.return)) _g.call(_a); | ||
} | ||
@@ -151,3 +151,3 @@ finally { if (e_2) throw e_2.error; } | ||
try { | ||
if (_d && !_d.done && (_h = _c["return"])) _h.call(_c); | ||
if (_d && !_d.done && (_h = _c.return)) _h.call(_c); | ||
} | ||
@@ -168,3 +168,3 @@ finally { if (e_3) throw e_3.error; } | ||
try { | ||
if (_f && !_f.done && (_j = _e["return"])) _j.call(_e); | ||
if (_f && !_f.done && (_j = _e.return)) _j.call(_e); | ||
} | ||
@@ -191,3 +191,3 @@ finally { if (e_4) throw e_4.error; } | ||
if (this._otherFiles.has(expectedFileName)) { | ||
this._otherFiles["delete"](expectedFileName); | ||
this._otherFiles.delete(expectedFileName); | ||
} | ||
@@ -247,3 +247,3 @@ else { | ||
}()); | ||
exports["default"] = TestRunner; | ||
exports.default = TestRunner; | ||
function readdirSyncRecursive(dir) { | ||
@@ -266,3 +266,3 @@ var e_6, _a, e_7, _b; | ||
try { | ||
if (_f && !_f.done && (_b = _e["return"])) _b.call(_e); | ||
if (_f && !_f.done && (_b = _e.return)) _b.call(_e); | ||
} | ||
@@ -280,3 +280,3 @@ finally { if (e_7) throw e_7.error; } | ||
try { | ||
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c); | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
} | ||
@@ -283,0 +283,0 @@ finally { if (e_6) throw e_6.error; } |
@@ -1,26 +0,10 @@ | ||
import { DefinitionNode, DocumentNode, FieldDefinitionNode, Location, NameNode } from "graphql"; | ||
import { NameNode } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { DiagnosticResult, DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import { InterfaceMap } from "./InterfaceGraph"; | ||
export declare const UNRESOLVED_REFERENCE_NAME = "__UNRESOLVED_REFERENCE__"; | ||
type NameDefinition = { | ||
export type NameDefinition = { | ||
name: NameNode; | ||
kind: "TYPE" | "INTERFACE" | "UNION" | "SCALAR" | "INPUT_OBJECT" | "ENUM"; | ||
}; | ||
export type GratsDefinitionNode = DefinitionNode | AbstractFieldDefinitionNode; | ||
export type AbstractFieldDefinitionNode = { | ||
readonly kind: "AbstractFieldDefinition"; | ||
readonly loc: Location; | ||
readonly onType: NameNode; | ||
readonly field: FieldDefinitionNode; | ||
}; | ||
/** | ||
* Information about the GraphQL context type. We track the first value we see, | ||
* and then validate that any other values we see are the same. | ||
*/ | ||
type GqlContext = { | ||
declaration: ts.Node; | ||
firstReference: ts.Node; | ||
}; | ||
/** | ||
* Used to track TypeScript references. | ||
@@ -40,22 +24,12 @@ * | ||
host: ts.CompilerHost; | ||
_options: ts.ParsedCommandLine; | ||
_symbolToName: Map<ts.Symbol, NameDefinition>; | ||
_unresolvedTypes: Map<NameNode, ts.Symbol>; | ||
gqlContext: GqlContext | null; | ||
hasTypename: Set<string>; | ||
constructor(options: ts.ParsedCommandLine, checker: ts.TypeChecker, host: ts.CompilerHost); | ||
constructor(checker: ts.TypeChecker, host: ts.CompilerHost); | ||
recordTypeName(node: ts.Node, name: NameNode, kind: NameDefinition["kind"]): void; | ||
recordHasTypenameField(name: string): void; | ||
markUnresolvedType(node: ts.Node, name: NameNode): void; | ||
findSymbolDeclaration(startSymbol: ts.Symbol): ts.Declaration | null; | ||
resolveSymbol(startSymbol: ts.Symbol): ts.Symbol; | ||
resolveTypes(doc: DocumentNode): DiagnosticsResult<DocumentNode>; | ||
handleAbstractDefinitions(docs: GratsDefinitionNode[]): DiagnosticsResult<DefinitionNode[]>; | ||
addAbstractFieldDefinition(doc: AbstractFieldDefinitionNode, interfaceGraph: InterfaceMap): DiagnosticsResult<DefinitionNode[]>; | ||
resolveNamedType(unresolved: NameNode): DiagnosticResult<NameNode>; | ||
err(loc: Location, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.Diagnostic; | ||
relatedInformation(loc: Location, message: string): ts.DiagnosticRelatedInformation; | ||
validateInterfaceImplementorsHaveTypenameField(): DiagnosticResult<null>; | ||
getDestFilePath(sourceFile: ts.SourceFile): string; | ||
unresolvedNameIsGraphQL(unresolved: NameNode): boolean; | ||
getNameDefinition(nameNode: NameNode): DiagnosticsResult<NameDefinition>; | ||
} | ||
export {}; |
@@ -13,24 +13,7 @@ "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."); | ||
}; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.TypeContext = exports.UNRESOLVED_REFERENCE_NAME = void 0; | ||
var graphql_1 = require("graphql"); | ||
var ts = require("typescript"); | ||
var DiagnosticError_1 = require("./utils/DiagnosticError"); | ||
var gratsRoot_1 = require("./gratsRoot"); | ||
var serverDirectives_1 = require("./serverDirectives"); | ||
var Extractor_1 = require("./Extractor"); | ||
var E = require("./Errors"); | ||
var InterfaceGraph_1 = require("./InterfaceGraph"); | ||
var helpers_1 = require("./utils/helpers"); | ||
exports.UNRESOLVED_REFERENCE_NAME = "__UNRESOLVED_REFERENCE__"; | ||
@@ -50,13 +33,10 @@ /** | ||
var TypeContext = /** @class */ (function () { | ||
function TypeContext(options, checker, host) { | ||
function TypeContext(checker, host) { | ||
this._symbolToName = new Map(); | ||
this._unresolvedTypes = new Map(); | ||
// The resolver context declaration, if it has been encountered. | ||
// Gets mutated by Extractor. | ||
this.gqlContext = null; | ||
this.hasTypename = new Set(); | ||
this._options = options; | ||
this.checker = checker; | ||
this.host = host; | ||
} | ||
// Record that a GraphQL construct of type `kind` with the name `name` is | ||
// declared at `node`. | ||
TypeContext.prototype.recordTypeName = function (node, name, kind) { | ||
@@ -74,5 +54,3 @@ var symbol = this.checker.getSymbolAtLocation(node); | ||
}; | ||
TypeContext.prototype.recordHasTypenameField = function (name) { | ||
this.hasTypename.add(name); | ||
}; | ||
// Record that a type reference `node` | ||
TypeContext.prototype.markUnresolvedType = function (node, name) { | ||
@@ -106,158 +84,2 @@ var symbol = this.checker.getSymbolAtLocation(node); | ||
}; | ||
TypeContext.prototype.resolveTypes = function (doc) { | ||
var _a; | ||
var _this = this; | ||
var errors = []; | ||
var newDoc = (0, graphql_1.visit)(doc, (_a = {}, | ||
_a[graphql_1.Kind.NAME] = function (t) { | ||
var namedTypeResult = _this.resolveNamedType(t); | ||
if (namedTypeResult.kind === "ERROR") { | ||
errors.push(namedTypeResult.err); | ||
return t; | ||
} | ||
return namedTypeResult.value; | ||
}, | ||
_a)); | ||
if (errors.length > 0) { | ||
return (0, DiagnosticError_1.err)(errors); | ||
} | ||
return (0, DiagnosticError_1.ok)(newDoc); | ||
}; | ||
TypeContext.prototype.handleAbstractDefinitions = function (docs) { | ||
var e_1, _a; | ||
var newDocs = []; | ||
var errors = []; | ||
var interfaceGraphResult = (0, InterfaceGraph_1.computeInterfaceMap)(this, docs); | ||
if (interfaceGraphResult.kind === "ERROR") { | ||
return interfaceGraphResult; | ||
} | ||
var interfaceGraph = interfaceGraphResult.value; | ||
try { | ||
for (var docs_1 = __values(docs), docs_1_1 = docs_1.next(); !docs_1_1.done; docs_1_1 = docs_1.next()) { | ||
var doc = docs_1_1.value; | ||
if (doc.kind === "AbstractFieldDefinition") { | ||
var abstractDocResults = this.addAbstractFieldDefinition(doc, interfaceGraph); | ||
if (abstractDocResults.kind === "ERROR") { | ||
(0, helpers_1.extend)(errors, abstractDocResults.err); | ||
} | ||
else { | ||
(0, helpers_1.extend)(newDocs, abstractDocResults.value); | ||
} | ||
} | ||
else { | ||
newDocs.push(doc); | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (docs_1_1 && !docs_1_1.done && (_a = docs_1["return"])) _a.call(docs_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
if (errors.length > 0) { | ||
return (0, DiagnosticError_1.err)(errors); | ||
} | ||
return (0, DiagnosticError_1.ok)(newDocs); | ||
}; | ||
// A field definition may be on a concrete type, or on an interface. If it's on an interface, | ||
// we need to add it to each concrete type that implements the interface. | ||
TypeContext.prototype.addAbstractFieldDefinition = function (doc, interfaceGraph) { | ||
var e_2, _a; | ||
var _b; | ||
var newDocs = []; | ||
var typeNameResult = this.resolveNamedType(doc.onType); | ||
if (typeNameResult.kind === "ERROR") { | ||
return (0, DiagnosticError_1.err)([typeNameResult.err]); | ||
} | ||
var symbol = this._unresolvedTypes.get(doc.onType); | ||
if (symbol == null) { | ||
// This should have already been handled by resolveNamedType | ||
throw new Error("Expected to find unresolved type."); | ||
} | ||
var nameDefinition = this._symbolToName.get(symbol); | ||
if (nameDefinition == null) { | ||
// This should have already been handled by resolveNamedType | ||
throw new Error("Expected to find name definition."); | ||
} | ||
switch (nameDefinition.kind) { | ||
case "TYPE": | ||
// Extending a type, is just adding a field to it. | ||
newDocs.push({ | ||
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION, | ||
name: doc.onType, | ||
fields: [doc.field], | ||
loc: doc.loc | ||
}); | ||
break; | ||
case "INTERFACE": { | ||
// Extending an interface is a bit more complicated. We need to add the field | ||
// to the interface, and to each type that implements the interface. | ||
// The interface field definition is not executable, so we don't | ||
// 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 !== serverDirectives_1.EXPORTED_DIRECTIVE; | ||
}); | ||
newDocs.push({ | ||
kind: graphql_1.Kind.INTERFACE_TYPE_EXTENSION, | ||
name: doc.onType, | ||
fields: [__assign(__assign({}, doc.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; | ||
var name = { | ||
kind: graphql_1.Kind.NAME, | ||
value: implementor.name, | ||
loc: doc.loc | ||
}; | ||
switch (implementor.kind) { | ||
case "TYPE": | ||
newDocs.push({ | ||
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION, | ||
name: name, | ||
fields: [doc.field], | ||
loc: doc.loc | ||
}); | ||
break; | ||
case "INTERFACE": | ||
newDocs.push({ | ||
kind: graphql_1.Kind.INTERFACE_TYPE_EXTENSION, | ||
name: name, | ||
fields: [__assign(__assign({}, doc.field), { directives: directives })], | ||
loc: doc.loc | ||
}); | ||
break; | ||
} | ||
} | ||
} | ||
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; } | ||
} | ||
break; | ||
} | ||
default: { | ||
// 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, DiagnosticError_1.err)([ | ||
this.err(loc, E.invalidTypePassedToFieldFunction(), [ | ||
this.relatedInformation(relatedLoc, "This is the type that was passed to `@".concat(Extractor_1.FIELD_TAG, "`.")), | ||
]), | ||
]); | ||
} | ||
} | ||
return (0, DiagnosticError_1.ok)(newDocs); | ||
}; | ||
TypeContext.prototype.resolveNamedType = function (unresolved) { | ||
@@ -277,35 +99,30 @@ var symbol = this._unresolvedTypes.get(unresolved); | ||
} | ||
return (0, DiagnosticError_1.err)(this.err(unresolved.loc, E.unresolvedTypeReference())); | ||
return (0, DiagnosticError_1.err)((0, DiagnosticError_1.gqlErr)(unresolved.loc, E.unresolvedTypeReference())); | ||
} | ||
return (0, DiagnosticError_1.ok)(__assign(__assign({}, unresolved), { value: nameDefinition.name.value })); | ||
}; | ||
TypeContext.prototype.err = function (loc, message, relatedInformation) { | ||
return { | ||
messageText: message, | ||
start: loc.start, | ||
length: loc.end - loc.start, | ||
category: ts.DiagnosticCategory.Error, | ||
code: DiagnosticError_1.FAKE_ERROR_CODE, | ||
file: ts.createSourceFile(loc.source.name, loc.source.body, ts.ScriptTarget.Latest), | ||
relatedInformation: relatedInformation | ||
}; | ||
TypeContext.prototype.unresolvedNameIsGraphQL = function (unresolved) { | ||
var symbol = this._unresolvedTypes.get(unresolved); | ||
return symbol != null && this._symbolToName.has(symbol); | ||
}; | ||
TypeContext.prototype.relatedInformation = function (loc, message) { | ||
return { | ||
category: ts.DiagnosticCategory.Message, | ||
code: DiagnosticError_1.FAKE_ERROR_CODE, | ||
messageText: message, | ||
file: ts.createSourceFile(loc.source.name, loc.source.body, ts.ScriptTarget.Latest), | ||
start: loc.start, | ||
length: loc.end - loc.start | ||
}; | ||
// TODO: Merge this with resolveNamedType | ||
TypeContext.prototype.getNameDefinition = function (nameNode) { | ||
var typeNameResult = this.resolveNamedType(nameNode); | ||
if (typeNameResult.kind === "ERROR") { | ||
return (0, DiagnosticError_1.err)([typeNameResult.err]); | ||
} | ||
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 nameDefinition = this._symbolToName.get(symbol); | ||
if (nameDefinition == null) { | ||
// This should have already been handled by resolveNamedType | ||
throw new Error("Expected to find name definition."); | ||
} | ||
return (0, DiagnosticError_1.ok)(nameDefinition); | ||
}; | ||
TypeContext.prototype.validateInterfaceImplementorsHaveTypenameField = function () { | ||
return (0, DiagnosticError_1.ok)(null); | ||
}; | ||
TypeContext.prototype.getDestFilePath = function (sourceFile) { | ||
return (0, gratsRoot_1.getRelativeOutputPath)(this._options, sourceFile); | ||
}; | ||
return TypeContext; | ||
}()); | ||
exports.TypeContext = TypeContext; |
"use strict"; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -16,6 +16,9 @@ import { GraphQLError, Location, Source } from "graphql"; | ||
export declare function err<E>(err: E): Err<E>; | ||
export declare function collectResults<T>(results: DiagnosticsResult<T>[]): DiagnosticsResult<T[]>; | ||
export declare function combineResults<T, U>(result1: DiagnosticsResult<T>, result2: DiagnosticsResult<U>): DiagnosticsResult<[T, U]>; | ||
export declare class ReportableDiagnostics { | ||
_host: ts.CompilerHost; | ||
_host: ts.FormatDiagnosticsHost; | ||
_diagnostics: ts.Diagnostic[]; | ||
constructor(host: ts.CompilerHost, diagnostics: ts.Diagnostic[]); | ||
constructor(host: ts.FormatDiagnosticsHost, diagnostics: ts.Diagnostic[]); | ||
static fromDiagnostics(diagnostics: ts.Diagnostic[]): ReportableDiagnostics; | ||
formatDiagnosticsWithColorAndContext(): string; | ||
@@ -26,4 +29,7 @@ formatDiagnosticsWithContext(): string; | ||
export declare function graphQlErrorToDiagnostic(error: GraphQLError): ts.Diagnostic; | ||
export declare function diagnosticAtGraphQLLocation(message: string, loc: Location): ts.Diagnostic; | ||
export declare function gqlErr(loc: Location, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.Diagnostic; | ||
export declare function gqlRelated(loc: Location, message: string): ts.DiagnosticRelatedInformation; | ||
export declare function tsErr(node: ts.Node, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.Diagnostic; | ||
export declare function tsRelated(node: ts.Node, message: string): ts.DiagnosticRelatedInformation; | ||
export declare function graphqlSourceToSourceFile(source: Source): ts.SourceFile; | ||
export {}; |
"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."); | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
@@ -18,15 +29,13 @@ var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
}; | ||
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 __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]; | ||
} | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
} | ||
return to.concat(ar || Array.prototype.slice.call(from)); | ||
}; | ||
exports.__esModule = true; | ||
exports.graphqlSourceToSourceFile = exports.diagnosticAtGraphQLLocation = exports.graphQlErrorToDiagnostic = exports.FAKE_ERROR_CODE = exports.ReportableDiagnostics = exports.err = exports.ok = void 0; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.graphqlSourceToSourceFile = exports.tsRelated = exports.tsErr = exports.gqlRelated = exports.gqlErr = exports.graphQlErrorToDiagnostic = exports.FAKE_ERROR_CODE = exports.ReportableDiagnostics = exports.combineResults = exports.collectResults = exports.err = exports.ok = void 0; | ||
var ts = require("typescript"); | ||
@@ -41,2 +50,43 @@ function ok(value) { | ||
exports.err = err; | ||
function collectResults(results) { | ||
var e_1, _a; | ||
var errors = []; | ||
var values = []; | ||
try { | ||
for (var results_1 = __values(results), results_1_1 = results_1.next(); !results_1_1.done; results_1_1 = results_1.next()) { | ||
var result = results_1_1.value; | ||
if (result.kind === "ERROR") { | ||
errors.push.apply(errors, __spreadArray([], __read(result.err), false)); | ||
} | ||
else { | ||
values.push(result.value); | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (results_1_1 && !results_1_1.done && (_a = results_1.return)) _a.call(results_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
if (errors.length > 0) { | ||
return err(errors); | ||
} | ||
return ok(values); | ||
} | ||
exports.collectResults = collectResults; | ||
function combineResults(result1, result2) { | ||
if (result1.kind === "ERROR" && result2.kind === "ERROR") { | ||
return err(__spreadArray(__spreadArray([], __read(result1.err), false), __read(result2.err), false)); | ||
} | ||
if (result1.kind === "ERROR") { | ||
return result1; | ||
} | ||
if (result2.kind === "ERROR") { | ||
return result2; | ||
} | ||
return ok([result1.value, result2.value]); | ||
} | ||
exports.combineResults = combineResults; | ||
var ReportableDiagnostics = /** @class */ (function () { | ||
@@ -47,2 +97,12 @@ function ReportableDiagnostics(host, diagnostics) { | ||
} | ||
// If you don't have a host, for example if you error while parsing the | ||
// tsconfig, you can use this method and one will be created for you. | ||
ReportableDiagnostics.fromDiagnostics = function (diagnostics) { | ||
var formatHost = { | ||
getCanonicalFileName: function (path) { return path; }, | ||
getCurrentDirectory: ts.sys.getCurrentDirectory, | ||
getNewLine: function () { return ts.sys.newLine; }, | ||
}; | ||
return new ReportableDiagnostics(formatHost, diagnostics); | ||
}; | ||
ReportableDiagnostics.prototype.formatDiagnosticsWithColorAndContext = function () { | ||
@@ -61,3 +121,3 @@ var formatted = ts.formatDiagnosticsWithColorAndContext(this._diagnostics, this._host); | ||
exports.ReportableDiagnostics = ReportableDiagnostics; | ||
// A madeup error code that we use to fake a TypeScript error code. | ||
// A made-up error code that we use to fake a TypeScript error code. | ||
// We pick a very random number to avoid collisions with real error messages. | ||
@@ -73,3 +133,3 @@ exports.FAKE_ERROR_CODE = 349389149282; | ||
function graphQlErrorToDiagnostic(error) { | ||
var e_1, _a; | ||
var e_2, _a; | ||
var position = error.positions[0]; | ||
@@ -79,3 +139,3 @@ if (position == null) { | ||
} | ||
// Start with baseline location infromation | ||
// Start with baseline location information | ||
var start = position; | ||
@@ -99,18 +159,11 @@ var length = 1; | ||
} | ||
relatedInformation.push({ | ||
category: ts.DiagnosticCategory.Message, | ||
code: exports.FAKE_ERROR_CODE, | ||
messageText: "Related location", | ||
file: graphqlSourceToSourceFile(relatedNode.loc.source), | ||
start: relatedNode.loc.start, | ||
length: relatedNode.loc.end - relatedNode.loc.start | ||
}); | ||
relatedInformation.push(gqlRelated(relatedNode.loc, "Related location")); | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (rest_1_1 && !rest_1_1.done && (_a = rest_1["return"])) _a.call(rest_1); | ||
if (rest_1_1 && !rest_1_1.done && (_a = rest_1.return)) _a.call(rest_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
@@ -131,7 +184,7 @@ } | ||
length: length, | ||
relatedInformation: relatedInformation | ||
relatedInformation: relatedInformation, | ||
}; | ||
} | ||
exports.graphQlErrorToDiagnostic = graphQlErrorToDiagnostic; | ||
function diagnosticAtGraphQLLocation(message, loc) { | ||
function gqlErr(loc, message, relatedInformation) { | ||
return { | ||
@@ -143,6 +196,44 @@ messageText: message, | ||
start: loc.start, | ||
length: loc.end - loc.start | ||
length: loc.end - loc.start, | ||
relatedInformation: relatedInformation, | ||
}; | ||
} | ||
exports.diagnosticAtGraphQLLocation = diagnosticAtGraphQLLocation; | ||
exports.gqlErr = gqlErr; | ||
function gqlRelated(loc, message) { | ||
return { | ||
category: ts.DiagnosticCategory.Message, | ||
code: exports.FAKE_ERROR_CODE, | ||
messageText: message, | ||
file: graphqlSourceToSourceFile(loc.source), | ||
start: loc.start, | ||
length: loc.end - loc.start, | ||
}; | ||
} | ||
exports.gqlRelated = gqlRelated; | ||
function tsErr(node, message, relatedInformation) { | ||
var start = node.getStart(); | ||
var length = node.getEnd() - start; | ||
var sourceFile = node.getSourceFile(); | ||
return { | ||
messageText: message, | ||
file: sourceFile, | ||
code: exports.FAKE_ERROR_CODE, | ||
category: ts.DiagnosticCategory.Error, | ||
start: start, | ||
length: length, | ||
relatedInformation: relatedInformation, | ||
}; | ||
} | ||
exports.tsErr = tsErr; | ||
function tsRelated(node, message) { | ||
return { | ||
category: ts.DiagnosticCategory.Message, | ||
code: 0, | ||
file: node.getSourceFile(), | ||
start: node.getStart(), | ||
length: node.getWidth(), | ||
messageText: message, | ||
}; | ||
} | ||
exports.tsRelated = tsRelated; | ||
function graphqlSourceToSourceFile(source) { | ||
@@ -149,0 +240,0 @@ return ts.createSourceFile(source.name, source.body, ts.ScriptTarget.Latest); |
@@ -13,3 +13,3 @@ "use strict"; | ||
}; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.extend = exports.DefaultMap = void 0; | ||
@@ -43,3 +43,3 @@ var DefaultMap = /** @class */ (function () { | ||
try { | ||
if (b_1_1 && !b_1_1.done && (_a = b_1["return"])) _a.call(b_1); | ||
if (b_1_1 && !b_1_1.done && (_a = b_1.return)) _a.call(b_1); | ||
} | ||
@@ -46,0 +46,0 @@ finally { if (e_1) throw e_1.error; } |
@@ -13,3 +13,3 @@ "use strict"; | ||
}; | ||
exports.__esModule = true; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.traverseJSDocTags = void 0; | ||
@@ -42,3 +42,3 @@ var ts = require("typescript"); | ||
try { | ||
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
@@ -45,0 +45,0 @@ finally { if (e_1) throw e_1.error; } |
{ | ||
"name": "grats", | ||
"version": "0.0.0-main-6626f659", | ||
"version": "0.0.0-main-663d2ac4", | ||
"main": "dist/src/index.js", | ||
@@ -12,8 +12,8 @@ "bin": "dist/src/cli.js", | ||
"dependencies": { | ||
"@graphql-tools/utils": "^9.2.1", | ||
"commander": "^10.0.0", | ||
"graphql": "^16.6.0", | ||
"typescript": "^4.9.5" | ||
"typescript": "^5.0.2" | ||
}, | ||
"devDependencies": { | ||
"@graphql-tools/utils": "^9.2.1", | ||
"@types/node": "^18.14.6", | ||
@@ -26,2 +26,3 @@ "@typescript-eslint/eslint-plugin": "^5.55.0", | ||
"path-browserify": "^1.0.1", | ||
"prettier": "^2.8.7", | ||
"process": "^0.11.10", | ||
@@ -33,3 +34,3 @@ "ts-node": "^10.9.1" | ||
}, | ||
"packageManager": "pnpm@8.1.1", | ||
"packageManager": "pnpm@8.12.0", | ||
"engines": { | ||
@@ -43,4 +44,5 @@ "node": ">=16 <=21", | ||
"build": "tsc --build", | ||
"lint": "eslint src/**/*.ts" | ||
"format": "prettier . --write", | ||
"lint": "eslint src/**/*.ts && prettier . --check" | ||
} | ||
} |
@@ -1,5 +0,1 @@ | ||
# -=[ ALPHA SOFTWARE ]=- | ||
**Grats is still experimental. Feel free to try it out and give feedback, but they api is still in flux** | ||
# Grats: Implementation-First GraphQL for TypeScript | ||
@@ -9,2 +5,4 @@ | ||
_Beta Software: Grats is largely stable and being used in production in multiple places. If you encounter any issues, dont hesitate to let us know._ | ||
**What if building a GraphQL server were as simple as just writing functions?** | ||
@@ -21,4 +19,30 @@ | ||
## Read the docs: https://grats.capt.dev/ | ||
## Example | ||
Here's what it looks like to define a User type with a greeting field using Grats: | ||
```ts | ||
/** @gqlType */ | ||
class User { | ||
/** @gqlField */ | ||
name: string; | ||
/** @gqlField */ | ||
greet(args: { greeting: string }): string { | ||
return `${args.greeting}, ${this.name}`; | ||
} | ||
} | ||
``` | ||
After running `npx grats`, you'll find a `schema.ts` module that exports an executable schema, and a `schema.graphql` file contins your GraphQL schema definition: | ||
```graphql | ||
type User { | ||
name: String | ||
greet(greeting: String!): String | ||
} | ||
``` | ||
That's just the begining! To learn more, **Read the docs: https://grats.capt.dev/** | ||
## Contributing | ||
@@ -25,0 +49,0 @@ |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
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
280393
3
107
6148
60
11
+ Addedtypescript@5.7.3(transitive)
- Removed@graphql-tools/utils@^9.2.1
- Removed@graphql-tools/utils@9.2.1(transitive)
- Removed@graphql-typed-document-node/core@3.2.0(transitive)
- Removedtslib@2.8.1(transitive)
- Removedtypescript@4.9.5(transitive)
Updatedtypescript@^5.0.2