New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

grats

Package Overview
Dependencies
Maintainers
1
Versions
240
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

grats - npm Package Compare versions

Comparing version 0.0.0-main-1acf2fbd to 0.0.0-main-1f647d1c

dist/src/codegen.d.ts

45

dist/package.json
{
"name": "grats",
"version": "0.0.2",
"version": "0.0.23",
"main": "dist/src/index.js",

@@ -9,18 +9,21 @@ "bin": "dist/src/cli.js",

"files": [
"dist"
"dist",
"!dist/src/tests"
],
"scripts": {
"test": "ts-node --esm src/tests/test.ts",
"test": "ts-node src/tests/test.ts",
"integration-tests": "node src/tests/integration.mjs",
"build": "tsc --build",
"lint": "eslint src/**/*.ts"
"build": "rm -rf dist/ && tsc --build",
"format": "prettier . --write",
"lint": "eslint . && 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",
"@types/semver": "^7.5.6",
"@typescript-eslint/eslint-plugin": "^5.55.0",

@@ -32,3 +35,5 @@ "@typescript-eslint/parser": "^5.55.0",

"path-browserify": "^1.0.1",
"prettier": "^2.8.7",
"process": "^0.11.10",
"semver": "^7.5.4",
"ts-node": "^10.9.1"

@@ -39,3 +44,27 @@ },

},
"packageManager": "pnpm@8.1.1"
"packageManager": "pnpm@8.12.0",
"engines": {
"node": ">=16 <=21",
"pnpm": "^8"
},
"bugs": {
"url": "https://github.com/captbaritone/grats/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/captbaritone/grats.git"
},
"author": {
"name": "Jordan Eldredge",
"email": "jordan@jordaneldredge.com",
"url": "https://jordaneldredge.com"
},
"keywords": [
"graphql",
"typescript",
"resolvers",
"schema",
"code-first",
"implementation-first"
]
}

3

dist/src/cli.d.ts
#!/usr/bin/env node
export {};
import { Location } from "graphql";
export declare function formatLoc(loc: Location): string;

@@ -39,7 +39,6 @@ #!/usr/bin/env node

};
exports.__esModule = true;
var graphql_1 = require("graphql");
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatLoc = void 0;
var _1 = require("./");
var lib_1 = require("./lib");
var utils_1 = require("@graphql-tools/utils");
var commander_1 = require("commander");

@@ -49,2 +48,6 @@ var fs_1 = require("fs");

var package_json_1 = require("../package.json");
var Locate_1 = require("./Locate");
var printSchema_1 = require("./printSchema");
var ts = require("typescript");
var DiagnosticError_1 = require("./utils/DiagnosticError");
var program = new commander_1.Command();

@@ -55,9 +58,14 @@ 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")
.option("--watch", "Watch for changes and rebuild schema files as needed")
.action(function (_a) {
var output = _a.output, tsconfig = _a.tsconfig;
var tsconfig = _a.tsconfig, watch = _a.watch;
return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_b) {
build(output, tsconfig);
if (watch) {
startWatchMode(tsconfig);
}
else {
runBuild(tsconfig);
}
return [2 /*return*/];

@@ -67,26 +75,92 @@ });

});
program.parse();
function build(output, tsconfig) {
if (tsconfig && !(0, fs_1.existsSync)(tsconfig)) {
console.error("Grats: Could not find tsconfig.json at `".concat(tsconfig, "`."));
program
.command("locate")
.argument("<ENTITY>", "GraphQL entity to locate. E.g. `User` or `User.id`")
.option("--tsconfig <TSCONFIG>", "Path to tsconfig.json. Defaults to auto-detecting based on the current working directory")
.action(function (entity, _a) {
var tsconfig = _a.tsconfig;
var config = getTsConfigOrReportAndExit(tsconfig).config;
var schemaAndDocResult = (0, lib_1.buildSchemaAndDocResult)(config);
if (schemaAndDocResult.kind === "ERROR") {
console.error(schemaAndDocResult.err.formatDiagnosticsWithColorAndContext());
process.exit(1);
}
var parsed = (0, _1.getParsedTsConfig)(tsconfig);
// FIXME: Validate config!
// https://github.com/tsconfig/bases
var schemaResult = (0, lib_1.buildSchemaResult)(parsed);
if (schemaResult.kind === "ERROR") {
console.error(schemaResult.err.formatDiagnosticsWithColorAndContext());
var schema = schemaAndDocResult.value.schema;
var loc = (0, Locate_1.locate)(schema, entity);
if (loc.kind === "ERROR") {
console.error(loc.err);
process.exit(1);
}
var schema = (0, graphql_1.lexicographicSortSchema)(schemaResult.value);
var schemaStr = (0, utils_1.printSchemaWithDirectives)(schema, { 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, "`."));
console.log(formatLoc(loc.value));
});
program.parse();
/**
* Run the compiler in watch mode.
*/
function startWatchMode(tsconfig) {
var _a = getTsConfigOrReportAndExit(tsconfig), config = _a.config, configPath = _a.configPath;
var watchHost = ts.createWatchCompilerHost(configPath, {}, ts.sys, ts.createSemanticDiagnosticsBuilderProgram, function (diagnostic) { return reportDiagnostics([diagnostic]); }, function (diagnostic) { return reportDiagnostics([diagnostic]); });
watchHost.afterProgramCreate = function (program) {
// For now we just rebuild the schema on every change.
var schemaResult = (0, lib_1.extractSchemaAndDoc)(config, program.getProgram());
if (schemaResult.kind === "ERROR") {
reportDiagnostics(schemaResult.err);
return;
}
writeSchemaFilesAndReport(schemaResult.value, config, configPath);
};
ts.createWatchProgram(watchHost);
}
/**
* Run the compiler performing a single build.
*/
function runBuild(tsconfig) {
var _a = getTsConfigOrReportAndExit(tsconfig), config = _a.config, configPath = _a.configPath;
var schemaAndDocResult = (0, lib_1.buildSchemaAndDocResult)(config);
if (schemaAndDocResult.kind === "ERROR") {
console.error(schemaAndDocResult.err.formatDiagnosticsWithColorAndContext());
process.exit(1);
}
else {
console.log(schemaStr);
writeSchemaFilesAndReport(schemaAndDocResult.value, config, configPath);
}
/**
* Serializes the SDL and TypeScript schema to disk and reports to the console.
*/
function writeSchemaFilesAndReport(schemaAndDoc, config, configPath) {
var schema = schemaAndDoc.schema, doc = schemaAndDoc.doc;
var gratsOptions = config.raw.grats;
var dest = (0, path_1.resolve)((0, path_1.dirname)(configPath), gratsOptions.tsSchema);
var code = (0, printSchema_1.printExecutableSchema)(schema, gratsOptions, dest);
(0, fs_1.writeFileSync)(dest, code);
console.error("Grats: Wrote TypeScript schema to `".concat(dest, "`."));
var schemaStr = (0, printSchema_1.printGratsSDL)(doc, 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, "`."));
}
/**
* Utility function to report diagnostics to the console.
*/
function reportDiagnostics(diagnostics) {
var reportable = DiagnosticError_1.ReportableDiagnostics.fromDiagnostics(diagnostics);
console.error(reportable.formatDiagnosticsWithColorAndContext());
}
// Locate and read the tsconfig.json file
function getTsConfigOrReportAndExit(tsconfig) {
var configPath = tsconfig || ts.findConfigFile(process.cwd(), ts.sys.fileExists);
if (configPath == null) {
throw new Error("Grats: Could not find tsconfig.json");
}
var optionsResult = (0, _1.getParsedTsConfig)(configPath);
if (optionsResult.kind === "ERROR") {
console.error(optionsResult.err.formatDiagnosticsWithColorAndContext());
process.exit(1);
}
return { configPath: configPath, config: optionsResult.value };
}
// Format a location for printing to the console. Tools like VS Code and iTerm
// will automatically turn this into a clickable link.
function formatLoc(loc) {
return "".concat(loc.source.name, ":").concat(loc.startToken.line + 1, ":").concat(loc.startToken.column + 1);
}
exports.formatLoc = formatLoc;

@@ -1,7 +0,26 @@

import { DefinitionNode, FieldDefinitionNode, InputValueDefinitionNode, ListTypeNode, NamedTypeNode, Location as GraphQLLocation, NameNode, Token, TypeNode, NonNullTypeNode, StringValueNode, ConstValueNode, ConstDirectiveNode, ConstArgumentNode, EnumValueDefinitionNode, ConstObjectFieldNode, ConstObjectValueNode, ConstListValueNode } from "graphql";
import { NameNode } from "graphql";
import { DiagnosticsResult } from "./utils/DiagnosticError";
import * as ts from "typescript";
import { TypeContext } from "./TypeContext";
import { ConfigOptions } from "./lib";
type ArgDefaults = Map<string, ts.Expression>;
import { NameDefinition } from "./TypeContext";
import { GratsDefinitionNode } from "./GraphQLConstructor";
export declare const LIBRARY_IMPORT_NAME = "grats";
export declare const LIBRARY_NAME = "Grats";
export declare const TYPE_TAG = "gqlType";
export declare const FIELD_TAG = "gqlField";
export declare const SCALAR_TAG = "gqlScalar";
export declare const INTERFACE_TAG = "gqlInterface";
export declare const ENUM_TAG = "gqlEnum";
export declare const UNION_TAG = "gqlUnion";
export declare const INPUT_TAG = "gqlInput";
export declare const IMPLEMENTS_TAG_DEPRECATED = "gqlImplements";
export declare const KILLS_PARENT_ON_EXCEPTION_TAG = "killsParentOnException";
export declare const ALL_TAGS: string[];
export type ExtractionSnapshot = {
readonly definitions: GratsDefinitionNode[];
readonly unresolvedNames: Map<ts.Node, NameNode>;
readonly nameDefinitions: Map<ts.Node, NameDefinition>;
readonly contextReferences: Array<ts.Node>;
readonly typesWithTypename: Set<string>;
readonly interfaceDeclarations: Array<ts.InterfaceDeclaration>;
};
/**

@@ -17,83 +36,2 @@ * Extracts GraphQL definitions from TypeScript source code.

*/
export declare class Extractor {
definitions: DefinitionNode[];
sourceFile: ts.SourceFile;
ctx: TypeContext;
configOptions: ConfigOptions;
errors: ts.Diagnostic[];
constructor(sourceFile: ts.SourceFile, ctx: TypeContext, buildOptions: ConfigOptions);
extract(): DiagnosticsResult<DefinitionNode[]>;
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): null;
reportUnhandled(node: ts.Node, message: string): null;
diagnosticAnnotatedLocation(node: ts.Node): {
start: number;
length: number;
filepath: ts.SourceFile;
};
loc(node: ts.Node): GraphQLLocation;
gqlDummyToken(pos: number): Token;
/** 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): Array<NamedTypeNode> | null;
interfaceInterfaceDeclaration(node: ts.InterfaceDeclaration, tag: ts.JSDocTag): void;
collectFields(node: ts.ClassDeclaration | ts.InterfaceDeclaration | ts.TypeLiteralNode): Array<FieldDefinitionNode>;
collectArgs(argsParam: ts.ParameterDeclaration): ReadonlyArray<InputValueDefinitionNode> | null;
collectArgDefaults(node: ts.ObjectBindingPattern): ArgDefaults;
collectConstValue(node: ts.Expression): ConstValueNode | null;
collectArrayLiteral(node: ts.ArrayLiteralExpression): ConstListValueNode | null;
cellectObjectLiteral(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, tag: ts.JSDocTag): NameNode | null;
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;
methodNameDirective(nameNode: ts.Node, name: string): ConstDirectiveNode;
exportDirective(nameNode: ts.Node, filename: string, functionName: string): ConstDirectiveNode;
/** GraphQL AST node helper methods */
gqlName(node: ts.Node, value: string): NameNode;
gqlNamedType(node: ts.Node, value: string): NamedTypeNode;
gqlNonNullType(node: ts.Node, type: TypeNode): NonNullTypeNode;
gqlNullableType(type: TypeNode): NamedTypeNode | ListTypeNode;
gqlListType(node: ts.Node, type: TypeNode): ListTypeNode;
gqlConstArgument(node: ts.Node, name: NameNode, value: ConstValueNode): ConstArgumentNode;
gqlConstDirective(node: ts.Node, name: NameNode, args: ReadonlyArray<ConstArgumentNode>): ConstDirectiveNode;
gqlString(node: ts.Node, value: string): StringValueNode;
}
export {};
export declare function extract(sourceFile: ts.SourceFile): DiagnosticsResult<ExtractionSnapshot>;

@@ -13,30 +13,55 @@ "use strict";

};
exports.__esModule = true;
exports.Extractor = 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.extract = exports.ALL_TAGS = exports.KILLS_PARENT_ON_EXCEPTION_TAG = exports.IMPLEMENTS_TAG_DEPRECATED = exports.INPUT_TAG = exports.UNION_TAG = exports.ENUM_TAG = exports.INTERFACE_TAG = exports.SCALAR_TAG = exports.FIELD_TAG = exports.TYPE_TAG = exports.LIBRARY_NAME = exports.LIBRARY_IMPORT_NAME = void 0;
var graphql_1 = require("graphql");
var DiagnosticError_1 = require("./utils/DiagnosticError");
var Result_1 = require("./utils/Result");
var ts = require("typescript");
var TypeContext_1 = require("./TypeContext");
var serverDirectives_1 = require("./serverDirectives");
var LIBRARY_IMPORT_NAME = "grats";
var LIBRARY_NAME = "Grats";
var ISSUE_URL = "https://github.com/captbaritone/grats/issues";
var TYPE_TAG = "gqlType";
var FIELD_TAG = "gqlField";
var SCALAR_TAG = "gqlScalar";
var INTERFACE_TAG = "gqlInterface";
var ENUM_TAG = "gqlEnum";
var UNION_TAG = "gqlUnion";
var INPUT_TAG = "gqlInput";
var KILLS_PARENT_ON_EXCEPTION_TAG = "killsParentOnException";
var ALL_TAGS = [
TYPE_TAG,
FIELD_TAG,
SCALAR_TAG,
INTERFACE_TAG,
ENUM_TAG,
UNION_TAG,
INPUT_TAG,
var E = require("./Errors");
var JSDoc_1 = require("./utils/JSDoc");
var GraphQLConstructor_1 = require("./GraphQLConstructor");
var gratsRoot_1 = require("./gratsRoot");
var Errors_1 = require("./Errors");
var comments_1 = require("./comments");
var helpers_1 = require("./utils/helpers");
exports.LIBRARY_IMPORT_NAME = "grats";
exports.LIBRARY_NAME = "Grats";
exports.TYPE_TAG = "gqlType";
exports.FIELD_TAG = "gqlField";
exports.SCALAR_TAG = "gqlScalar";
exports.INTERFACE_TAG = "gqlInterface";
exports.ENUM_TAG = "gqlEnum";
exports.UNION_TAG = "gqlUnion";
exports.INPUT_TAG = "gqlInput";
exports.IMPLEMENTS_TAG_DEPRECATED = "gqlImplements";
exports.KILLS_PARENT_ON_EXCEPTION_TAG = "killsParentOnException";
// All the tags that start with gql
exports.ALL_TAGS = [
exports.TYPE_TAG,
exports.FIELD_TAG,
exports.SCALAR_TAG,
exports.INTERFACE_TAG,
exports.ENUM_TAG,
exports.UNION_TAG,
exports.INPUT_TAG,
];
var DEPRECATED_TAG = "deprecated";
var OPERATION_TYPES = new Set(["Query", "Mutation", "Subscription"]);
/**

@@ -52,10 +77,25 @@ * Extracts GraphQL definitions from TypeScript source code.

*/
function extract(sourceFile) {
var extractor = new Extractor();
return extractor.extract(sourceFile);
}
exports.extract = extract;
var Extractor = /** @class */ (function () {
function Extractor(sourceFile, ctx, buildOptions) {
function Extractor() {
this.definitions = [];
// Snapshot data
this.unresolvedNames = new Map();
this.nameDefinitions = new Map();
this.contextReferences = [];
this.typesWithTypename = new Set();
this.interfaceDeclarations = [];
this.errors = [];
this.sourceFile = sourceFile;
this.ctx = ctx;
this.configOptions = buildOptions;
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.

@@ -65,86 +105,91 @@ // 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;
ts.forEachChild(this.sourceFile, function (node) {
var e_1, _a, e_2, _b;
try {
for (var _c = __values(ts.getJSDocTags(node)), _d = _c.next(); !_d.done; _d = _c.next()) {
var tag = _d.value;
switch (tag.tagName.text) {
case TYPE_TAG:
_this.extractType(node, tag);
break;
case SCALAR_TAG:
_this.extractScalar(node, tag);
break;
case INTERFACE_TAG:
_this.extractInterface(node, tag);
break;
case ENUM_TAG:
_this.extractEnum(node, tag);
break;
case INPUT_TAG:
_this.extractInput(node, tag);
break;
case UNION_TAG:
_this.extractUnion(node, tag);
break;
case FIELD_TAG:
if (ts.isFunctionDeclaration(node)) {
_this.functionDeclarationExtendType(node, tag);
var seenCommentPositions = new Set();
(0, JSDoc_1.traverseJSDocTags)(sourceFile, function (node, tag) {
var e_1, _a;
seenCommentPositions.add(tag.parent.pos);
switch (tag.tagName.text) {
case exports.TYPE_TAG:
_this.extractType(node, tag);
break;
case exports.SCALAR_TAG:
_this.extractScalar(node, tag);
break;
case exports.INTERFACE_TAG:
_this.extractInterface(node, tag);
break;
case exports.ENUM_TAG:
_this.extractEnum(node, tag);
break;
case exports.INPUT_TAG:
_this.extractInput(node, tag);
break;
case exports.UNION_TAG:
_this.extractUnion(node, tag);
break;
case exports.FIELD_TAG:
if (ts.isFunctionDeclaration(node)) {
_this.functionDeclarationExtendType(node, tag);
}
else if (!(ts.isParameter(node) ||
ts.isMethodDeclaration(node) ||
ts.isGetAccessorDeclaration(node) ||
ts.isPropertyDeclaration(node) ||
ts.isMethodSignature(node) ||
ts.isPropertySignature(node))) {
// Right now this happens via deep traversal
// Note: Keep this in sync with `collectFields`
_this.reportUnhandled(node, "field", E.fieldTagOnWrongNode());
}
break;
case exports.KILLS_PARENT_ON_EXCEPTION_TAG: {
var hasFieldTag = ts.getJSDocTags(node).some(function (t) {
return t.tagName.text === exports.FIELD_TAG;
});
if (!hasFieldTag) {
_this.report(tag.tagName, E.killsParentOnExceptionOnWrongNode());
}
// TODO: Report invalid location as well
break;
}
default:
{
var lowerCaseTag = tag.tagName.text.toLowerCase();
if (lowerCaseTag.startsWith("gql")) {
try {
for (var ALL_TAGS_1 = __values(exports.ALL_TAGS), ALL_TAGS_1_1 = ALL_TAGS_1.next(); !ALL_TAGS_1_1.done; ALL_TAGS_1_1 = ALL_TAGS_1.next()) {
var t = ALL_TAGS_1_1.value;
if (t.toLowerCase() === lowerCaseTag) {
_this.report(tag.tagName, E.wrongCasingForGratsTag(tag.tagName.text, t));
break;
}
}
}
else if (!(ts.isMethodDeclaration(node) ||
ts.isPropertyDeclaration(node) ||
ts.isMethodSignature(node) ||
ts.isPropertySignature(node))) {
// Right now this happens via deep traversal
// Note: Keep this in sync with `collectFields`
_this.reportUnhandled(node, "`@".concat(FIELD_TAG, "` can only be used on method/property declarations or signatures."));
}
break;
case KILLS_PARENT_ON_EXCEPTION_TAG:
var hasFieldTag = ts.getJSDocTags(node).some(function (t) {
return t.tagName.text === FIELD_TAG;
});
if (!hasFieldTag) {
_this.report(tag.tagName, "Unexpected `@".concat(KILLS_PARENT_ON_EXCEPTION_TAG, "`. `@").concat(KILLS_PARENT_ON_EXCEPTION_TAG, "` can only be used in field annotation docblocks. Perhaps you are missing a `@").concat(FIELD_TAG, "` tag?"));
}
break;
default:
var lowerCaseTag = tag.tagName.text.toLowerCase();
if (lowerCaseTag.startsWith("gql")) {
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
for (var ALL_TAGS_1 = (e_2 = void 0, __values(ALL_TAGS)), ALL_TAGS_1_1 = ALL_TAGS_1.next(); !ALL_TAGS_1_1.done; ALL_TAGS_1_1 = ALL_TAGS_1.next()) {
var t = ALL_TAGS_1_1.value;
if (t.toLowerCase() === lowerCaseTag) {
_this.report(tag.tagName, "Incorrect casing for Grats tag `@".concat(tag.tagName.text, "`. Use `@").concat(t, "` instead."));
break;
}
}
if (ALL_TAGS_1_1 && !ALL_TAGS_1_1.done && (_a = ALL_TAGS_1.return)) _a.call(ALL_TAGS_1);
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (ALL_TAGS_1_1 && !ALL_TAGS_1_1.done && (_b = ALL_TAGS_1["return"])) _b.call(ALL_TAGS_1);
}
finally { if (e_2) throw e_2.error; }
}
_this.report(tag.tagName, "`@".concat(tag.tagName.text, "` is not a valid Grats tag. Valid tags are: ").concat(ALL_TAGS.map(function (t) { return "`@".concat(t, "`"); }).join(", "), "."));
finally { if (e_1) throw e_1.error; }
}
break;
_this.report(tag.tagName, E.invalidGratsTag(tag.tagName.text));
}
}
}
break;
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
});
var errors = (0, comments_1.detectInvalidComments)(sourceFile, seenCommentPositions);
(0, helpers_1.extend)(this.errors, errors);
if (this.errors.length > 0) {
return (0, DiagnosticError_1.err)(this.errors);
return (0, Result_1.err)(this.errors);
}
return (0, DiagnosticError_1.ok)(this.definitions);
return (0, Result_1.ok)({
definitions: this.definitions,
unresolvedNames: this.unresolvedNames,
nameDefinitions: this.nameDefinitions,
contextReferences: this.contextReferences,
typesWithTypename: this.typesWithTypename,
interfaceDeclarations: this.interfaceDeclarations,
});
};

@@ -162,3 +207,3 @@ Extractor.prototype.extractType = function (node, tag) {

else {
this.report(tag, "`@".concat(TYPE_TAG, "` can only be used on class or interface declarations."));
this.report(tag, E.invalidTypeTagUsage());
}

@@ -171,3 +216,3 @@ };

else {
this.report(tag, "`@".concat(SCALAR_TAG, "` can only be used on type alias declarations."));
this.report(tag, E.invalidScalarTagUsage());
}

@@ -180,3 +225,3 @@ };

else {
this.report(tag, "`@".concat(INTERFACE_TAG, "` can only be used on interface declarations."));
this.report(tag, E.invalidInterfaceTagUsage());
}

@@ -192,3 +237,3 @@ };

else {
this.report(tag, "`@".concat(ENUM_TAG, "` can only be used on enum declarations or TypeScript unions."));
this.report(tag, E.invalidEnumTagUsage());
}

@@ -200,4 +245,7 @@ };

}
else if (ts.isInterfaceDeclaration(node)) {
this.inputInterfaceDeclaration(node, tag);
}
else {
this.report(tag, "`@".concat(INPUT_TAG, "` can only be used on type alias declarations."));
this.report(tag, E.invalidInputTagUsage());
}

@@ -210,17 +258,8 @@ };

else {
this.report(tag, "`@".concat(UNION_TAG, "` can only be used on type alias declarations."));
this.report(tag, E.invalidUnionTagUsage());
}
};
/** Error handling and location juggling */
Extractor.prototype.report = function (node, message) {
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
});
Extractor.prototype.report = function (node, message, relatedInformation) {
this.errors.push((0, DiagnosticError_1.tsErr)(node, message, relatedInformation));
return null;

@@ -230,29 +269,10 @@ };

// Gives the user a path forward if they think we should be able to infer this type.
Extractor.prototype.reportUnhandled = function (node, message) {
var suggestion = "If you think ".concat(LIBRARY_NAME, " should be able to infer this type, please report an issue at ").concat(ISSUE_URL, ".");
Extractor.prototype.reportUnhandled = function (node, positionKind, message, relatedInformation) {
var suggestion = "If you think ".concat(exports.LIBRARY_NAME, " should be able to infer this ").concat(positionKind, ", please report an issue at ").concat(Errors_1.ISSUE_URL, ".");
var completedMessage = "".concat(message, "\n\n").concat(suggestion);
this.report(node, completedMessage);
return null;
return this.report(node, completedMessage, relatedInformation);
};
Extractor.prototype.diagnosticAnnotatedLocation = function (node) {
var start = node.getStart();
var end = node.getEnd();
return { start: start, length: end - start, filepath: this.sourceFile };
};
// TODO: This is potentially quite expensive, and we only need it if we report
// an error at one of these locations. We could consider some trick to return a
// proxy object that would lazily compute the line/column info.
Extractor.prototype.loc = function (node) {
var source = new graphql_1.Source(this.sourceFile.text, this.sourceFile.fileName);
var startToken = this.gqlDummyToken(node.getStart());
var endToken = this.gqlDummyToken(node.getEnd());
return new graphql_1.Location(startToken, endToken, source);
};
Extractor.prototype.gqlDummyToken = function (pos) {
var _a = this.sourceFile.getLineAndCharacterOfPosition(pos), line = _a.line, character = _a.character;
return new graphql_1.Token(graphql_1.TokenKind.SOF, pos, pos, line, character, undefined);
};
/** TypeScript traversals */
Extractor.prototype.unionTypeAliasDeclaration = function (node, tag) {
var e_3, _a;
var e_2, _a;
var name = this.entityName(node, tag);

@@ -262,5 +282,5 @@ if (name == null)

if (!ts.isUnionTypeNode(node.type)) {
return this.report(node, "Expected a TypeScript union. `@".concat(UNION_TAG, "` can only be used on TypeScript unions."));
return this.report(node, E.expectedUnionTypeNode());
}
var description = this.collectDescription(node.name);
var description = this.collectDescription(node);
var types = [];

@@ -271,24 +291,18 @@ try {

if (!ts.isTypeReferenceNode(member)) {
return this.reportUnhandled(member, "Expected `@".concat(UNION_TAG, "` union members to be type references."));
return this.reportUnhandled(member, "union member", E.expectedUnionTypeReference());
}
var namedType = this.gqlNamedType(member.typeName, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
this.ctx.markUnresolvedType(member.typeName, namedType.name);
var namedType = this.gql.namedType(member.typeName, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
this.markUnresolvedType(member.typeName, namedType.name);
types.push(namedType);
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_3) throw e_3.error; }
finally { if (e_2) throw e_2.error; }
}
this.ctx.recordTypeName(node.name, name.value);
this.definitions.push({
kind: graphql_1.Kind.UNION_TYPE_DEFINITION,
loc: this.loc(node),
description: description !== null && description !== void 0 ? description : undefined,
name: name,
types: types
});
this.recordTypeName(node.name, name, "UNION");
this.definitions.push(this.gql.unionTypeDefinition(node, name, types, description));
};

@@ -301,3 +315,3 @@ Extractor.prototype.functionDeclarationExtendType = function (node, tag) {

if (typeParam == null) {
return this.report(funcName, "Expected `@".concat(FIELD_TAG, "` function to have a first argument representing the type to extend."));
return this.report(funcName, E.invalidParentArgForFunctionField());
}

@@ -311,5 +325,5 @@ var typeName = this.typeReferenceFromParam(typeParam);

if (node.type == null) {
return this.report(funcName, "Expected GraphQL field to have an explicit return type.");
return this.report(funcName, E.invalidReturnTypeForFunctionField());
}
var type = this.collectMethodType(node.type);
var type = this.collectType(node.type, { kind: "OUTPUT" });
if (type == null)

@@ -322,12 +336,19 @@ return null;

}
var description = this.collectDescription(funcName);
var context = node.parameters[2];
if (context != null) {
this.validateContextParameter(context);
}
var description = this.collectDescription(node);
if (!ts.isSourceFile(node.parent)) {
return this.report(node, "Expected `@".concat(FIELD_TAG, "` function to be a top-level declaration."));
return this.report(node, E.functionFieldNotTopLevel());
}
// TODO: Does this work in the browser?
var filename = this.ctx.getDestFilePath(node.parent);
var directives = [this.exportDirective(funcName, filename, funcName.text)];
if (funcName.text !== name.value) {
directives.push(this.methodNameDirective(funcName, funcName.text));
}
var tsModulePath = (0, gratsRoot_1.relativePath)(node.getSourceFile().fileName);
var directives = [
this.gql.fieldMetadataDirective(funcName, {
tsModulePath: tsModulePath,
name: funcName.text,
argCount: node.parameters.length,
}),
];
var deprecated = this.collectDeprecated(node);

@@ -337,29 +358,19 @@ if (deprecated != null) {

}
this.definitions.push({
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
loc: this.loc(node),
name: typeName,
fields: [
{
kind: graphql_1.Kind.FIELD_DEFINITION,
loc: this.loc(node),
description: description || undefined,
name: name,
arguments: args || undefined,
type: this.handleErrorBubbling(node, type),
directives: directives.length === 0 ? undefined : directives
},
]
});
var killsParentOnExceptionDirective = this.killsParentOnExceptionDirective(node);
if (killsParentOnExceptionDirective != null) {
directives.push(killsParentOnExceptionDirective);
}
var field = this.gql.fieldDefinition(node, name, type, args, directives, description);
this.definitions.push(this.gql.abstractFieldDefinition(node, typeName, field));
};
Extractor.prototype.typeReferenceFromParam = function (typeParam) {
if (typeParam.type == null) {
return this.report(typeParam, "Expected first argument of a `@".concat(FIELD_TAG, "` function to have an explicit type annotation."));
return this.report(typeParam, E.functionFieldParentTypeMissing());
}
if (!ts.isTypeReferenceNode(typeParam.type)) {
return this.report(typeParam.type, "Expected first argument of a `@".concat(FIELD_TAG, "` function to be typed as a `@").concat(TYPE_TAG, "` type."));
return this.report(typeParam.type, E.functionFieldParentTypeNotValid());
}
var nameNode = typeParam.type.typeName;
var typeName = this.gqlName(nameNode, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
this.ctx.markUnresolvedType(nameNode, typeName);
var typeName = this.gql.name(nameNode, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
this.markUnresolvedType(nameNode, typeName);
return typeName;

@@ -370,3 +381,3 @@ };

if (node.name == null) {
return this.report(node, "Expected a `@".concat(FIELD_TAG, "` function to be a named function."));
return this.report(node, E.functionFieldNotNamed());
}

@@ -381,6 +392,6 @@ var exportKeyword = (_a = node.modifiers) === null || _a === void 0 ? void 0 : _a.some(function (modifier) {

// TODO: We could support this
return this.report(defaultKeyword, "Expected a `@".concat(FIELD_TAG, "` function to be a named export, not a default export."));
return this.report(defaultKeyword, E.functionFieldDefaultExport());
}
if (exportKeyword == null) {
return this.report(node.name, "Expected a `@".concat(FIELD_TAG, "` function to be a named export."));
return this.report(node.name, E.functionFieldNotNamedExport());
}

@@ -393,10 +404,5 @@ return node.name;

return null;
var description = this.collectDescription(node.name);
this.ctx.recordTypeName(node.name, name.value);
this.definitions.push({
kind: graphql_1.Kind.SCALAR_TYPE_DEFINITION,
loc: this.loc(node),
description: description !== null && description !== void 0 ? description : undefined,
name: name
});
var description = this.collectDescription(node);
this.recordTypeName(node.name, name, "SCALAR");
this.definitions.push(this.gql.scalarTypeDefinition(node, name, description));
};

@@ -407,15 +413,39 @@ Extractor.prototype.inputTypeAliasDeclaration = function (node, tag) {

return null;
var description = this.collectDescription(node.name);
this.ctx.recordTypeName(node.name, name.value);
var description = this.collectDescription(node);
this.recordTypeName(node.name, name, "INPUT_OBJECT");
var fields = this.collectInputFields(node);
var deprecatedDirective = this.collectDeprecated(node);
this.definitions.push({
kind: graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION,
loc: this.loc(node),
description: description !== null && description !== void 0 ? description : undefined,
name: name,
fields: fields !== null && fields !== void 0 ? fields : undefined,
directives: deprecatedDirective == null ? undefined : [deprecatedDirective]
});
this.definitions.push(this.gql.inputObjectTypeDefinition(node, name, fields, deprecatedDirective == null ? null : [deprecatedDirective], description));
};
Extractor.prototype.inputInterfaceDeclaration = function (node, tag) {
var e_3, _a;
var name = this.entityName(node, tag);
if (name == null)
return null;
var description = this.collectDescription(node);
this.recordTypeName(node.name, name, "INPUT_OBJECT");
var fields = [];
try {
for (var _b = __values(node.members), _c = _b.next(); !_c.done; _c = _b.next()) {
var member = _c.value;
if (!ts.isPropertySignature(member)) {
this.reportUnhandled(member, "input field", E.inputTypeFieldNotProperty());
continue;
}
var field = this.collectInputField(member);
if (field != null)
fields.push(field);
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_3) throw e_3.error; }
}
this.interfaceDeclarations.push(node);
var deprecatedDirective = this.collectDeprecated(node);
this.definitions.push(this.gql.inputObjectTypeDefinition(node, name, fields, deprecatedDirective == null ? null : [deprecatedDirective], description));
};
Extractor.prototype.collectInputFields = function (node) {

@@ -425,3 +455,3 @@ var e_4, _a;

if (!ts.isTypeLiteralNode(node.type)) {
return this.reportUnhandled(node, "`@".concat(INPUT_TAG, "` can only be used on type literals."));
return this.reportUnhandled(node, "input", E.inputTypeNotLiteral());
}

@@ -432,3 +462,3 @@ try {

if (!ts.isPropertySignature(member)) {
this.reportUnhandled(member, "`@".concat(INPUT_TAG, "` types only support property signature members."));
this.reportUnhandled(member, "input field", E.inputTypeFieldNotProperty());
continue;

@@ -444,3 +474,3 @@ }

try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}

@@ -452,27 +482,19 @@ finally { if (e_4) throw e_4.error; }

Extractor.prototype.collectInputField = function (node) {
var id = this.expectIdentifier(node.name);
var id = this.expectNameIdentifier(node.name);
if (id == null)
return null;
if (node.type == null) {
return this.report(node, "Input field must have a type annotation.");
return this.report(node, E.inputFieldUntyped());
}
var inner = this.collectType(node.type);
var inner = this.collectType(node.type, { kind: "INPUT" });
if (inner == null)
return null;
var type = node.questionToken == null ? inner : this.gqlNullableType(inner);
var description = this.collectDescription(node.name);
var type = node.questionToken == null ? inner : this.gql.nullableType(inner);
var description = this.collectDescription(node);
var deprecatedDirective = this.collectDeprecated(node);
return {
kind: graphql_1.Kind.INPUT_VALUE_DEFINITION,
loc: this.loc(node),
description: description !== null && description !== void 0 ? description : undefined,
name: this.gqlName(id, id.text),
type: type,
defaultValue: undefined,
directives: deprecatedDirective == null ? undefined : [deprecatedDirective]
};
return this.gql.inputValueDefinition(node, this.gql.name(id, id.text), type, deprecatedDirective == null ? null : [deprecatedDirective], null, description);
};
Extractor.prototype.typeClassDeclaration = function (node, tag) {
if (node.name == null) {
return this.report(node, "Unexpected `@".concat(TYPE_TAG, "` annotation on unnamed class declaration."));
return this.report(node, E.typeTagOnUnnamedClass());
}

@@ -482,17 +504,17 @@ 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.value);
this.recordTypeName(node.name, name, "TYPE");
this.checkForTypenameProperty(node, name.value);
this.definitions.push({
kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
loc: this.loc(node),
description: description !== null && description !== void 0 ? description : undefined,
directives: undefined,
name: name,
fields: fields,
interfaces: interfaces !== null && interfaces !== void 0 ? interfaces : undefined
});
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) {

@@ -502,16 +524,9 @@ 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.value);
this.recordTypeName(node.name, name, "TYPE");
this.checkForTypenameProperty(node, name.value);
this.definitions.push({
kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
loc: this.loc(node),
description: description !== null && description !== void 0 ? description : undefined,
directives: undefined,
name: name,
fields: fields,
interfaces: interfaces !== null && interfaces !== void 0 ? interfaces : undefined
});
this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description));
};

@@ -522,21 +537,21 @@ Extractor.prototype.typeTypeAliasDeclaration = function (node, tag) {

return null;
if (!ts.isTypeLiteralNode(node.type)) {
this.reportUnhandled(node.type, "Expected `@".concat(TYPE_TAG, "` type to be a type literal. For example: `type Foo = { bar: string }`"));
return;
var fields = [];
var interfaces = null;
if (ts.isTypeLiteralNode(node.type)) {
this.validateOperationTypes(node.type, name.value);
fields = this.collectFields(node.type);
interfaces = this.collectInterfaces(node);
this.checkForTypenameProperty(node.type, name.value);
}
var description = this.collectDescription(node.name);
var fields = this.collectFields(node.type);
this.ctx.recordTypeName(node.name, name.value);
this.checkForTypenameProperty(node.type, name.value);
this.definitions.push({
kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
loc: this.loc(node),
description: description !== null && description !== void 0 ? description : undefined,
directives: undefined,
name: name,
fields: fields,
// I don't believe there is a reasonable way to specify that a type
// implements an interface.
interfaces: undefined
});
else if (node.type.kind === ts.SyntaxKind.UnknownKeyword) {
// This is fine, we just don't know what it is. This should be the expected
// case for operation types such as `Query`, `Mutation`, and `Subscription`
// where there is not strong convention around.
}
else {
return this.report(node.type, E.typeTagOnAliasOfNonObjectOrUnknown());
}
var description = this.collectDescription(node);
this.recordTypeName(node.name, name, "TYPE");
this.definitions.push(this.gql.objectTypeDefinition(node, name, fields, interfaces, description));
};

@@ -549,3 +564,3 @@ Extractor.prototype.checkForTypenameProperty = function (node, expectedName) {

if (hasTypename) {
this.ctx.recordHasTypenameField(expectedName);
this.typesWithTypename.add(expectedName);
}

@@ -565,5 +580,4 @@ };

}
this.report(member.name,
// TODO: Could show what kind we found, but TS AST does not have node names.
"Expected `__typename` to be a property declaration.");
this.report(member.name, E.typeNameNotDeclaration());
return false;

@@ -579,11 +593,11 @@ };

if (node.initializer == null) {
this.report(node.name, "Expected `__typename` property to have an initializer or a string literal type. For example: `__typename = \"MyType\"` or `__typename: \"MyType\";`.");
this.report(node.name, E.typeNameMissingInitializer());
return false;
}
if (!ts.isStringLiteral(node.initializer)) {
this.report(node.initializer, "Expected `__typename` property initializer to be a string literal. For example: `__typename = \"MyType\"` or `__typename: \"MyType\";`.");
this.report(node.initializer, E.typeNameInitializeNotString());
return false;
}
if (node.initializer.text !== expectedName) {
this.report(node.initializer, "Expected `__typename` property initializer to be `\"".concat(expectedName, "\"`, found `\"").concat(node.initializer.text, "\"`."));
this.report(node.initializer, E.typeNameInitializerWrong(expectedName, node.initializer.text));
return false;

@@ -595,3 +609,3 @@ }

if (node.type == null) {
this.report(node, "Expected `__typename` property signature to specify the typename as a string literal string type. For example `__typename: \"".concat(expectedName, "\";`"));
this.report(node, E.typeNameMissingTypeAnnotation(expectedName));
return false;

@@ -603,7 +617,7 @@ }

if (!ts.isLiteralTypeNode(node) || !ts.isStringLiteral(node.literal)) {
this.report(node, "Expected `__typename` property signature to specify the typename as a string literal string type. For example `__typename: \"".concat(expectedName, "\";`"));
this.report(node, E.typeNameTypeNotStringLiteral(expectedName));
return false;
}
if (node.literal.text !== expectedName) {
this.report(node, "Expected `__typename` property to be `\"".concat(expectedName, "\"`"));
this.report(node, E.typeNameDoesNotMatchExpected(expectedName));
return false;

@@ -614,15 +628,40 @@ }

Extractor.prototype.collectInterfaces = function (node) {
this.reportTagInterfaces(node);
return ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)
? this.collectHeritageInterfaces(node)
: null;
};
Extractor.prototype.reportTagInterfaces = function (node) {
var tag = this.findTag(node, exports.IMPLEMENTS_TAG_DEPRECATED);
if (tag == null)
return null;
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
this.report(tag, E.implementsTagOnClass());
}
if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
this.report(tag, E.implementsTagOnInterface());
}
if (node.kind === ts.SyntaxKind.TypeAliasDeclaration) {
this.report(tag, E.implementsTagOnTypeAlias());
}
};
Extractor.prototype.collectHeritageInterfaces = function (node) {
var _this = this;
if (node.heritageClauses == null)
return null;
var maybeInterfaces = node.heritageClauses.flatMap(function (clause) {
if (clause.token !== ts.SyntaxKind.ImplementsKeyword)
return [];
return clause.types.map(function (type) {
if (!ts.isIdentifier(type.expression)) {
// TODO: Are there valid cases we want to cover here?
return null;
}
var namedType = _this.gqlNamedType(type.expression, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
_this.ctx.markUnresolvedType(type.expression, namedType.name);
var maybeInterfaces = node.heritageClauses
.filter(function (clause) {
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
return clause.token === ts.SyntaxKind.ImplementsKeyword;
}
// Interfaces can only have extends clauses, and those are allowed.
return true;
})
.flatMap(function (clause) {
return clause.types
.map(function (type) { return type.expression; })
.filter(function (expression) { return ts.isIdentifier(expression); })
.map(function (expression) {
var namedType = _this.gql.namedType(expression, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
_this.markUnresolvedType(expression, namedType.name);
return namedType;

@@ -637,2 +676,7 @@ });

};
Extractor.prototype.hasGqlTag = function (node) {
return ts.getJSDocTags(node).some(function (tag) {
return exports.ALL_TAGS.includes(tag.tagName.text);
});
};
Extractor.prototype.interfaceInterfaceDeclaration = function (node, tag) {

@@ -643,16 +687,8 @@ var name = this.entityName(node, tag);

}
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.value);
// While GraphQL supports interfaces that extend other interfaces,
// TypeScript does not. So we can't support that here either.
// In the future we could support classes that act as interfaces through
// inheritance.
this.definitions.push({
kind: graphql_1.Kind.INTERFACE_TYPE_DEFINITION,
loc: this.loc(node),
description: description || undefined,
name: name,
fields: fields
});
this.recordTypeName(node.name, name, "INTERFACE");
this.definitions.push(this.gql.interfaceTypeDefinition(node, name, fields, interfaces, description));
};

@@ -663,5 +699,28 @@ Extractor.prototype.collectFields = function (node) {

ts.forEachChild(node, function (node) {
if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
var e_5, _a;
if (ts.isConstructorDeclaration(node)) {
try {
// Handle parameter properties
// https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
for (var _b = __values(node.parameters), _c = _b.next(); !_c.done; _c = _b.next()) {
var param = _c.value;
var field = _this.constructorParam(param);
if (field != null) {
fields.push(field);
}
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_5) throw e_5.error; }
}
}
if (ts.isMethodDeclaration(node) ||
ts.isMethodSignature(node) ||
ts.isGetAccessorDeclaration(node)) {
var field = _this.methodDeclaration(node);
if (field) {
if (field != null) {
fields.push(field);

@@ -680,14 +739,71 @@ }

};
Extractor.prototype.constructorParam = function (node) {
var tag = this.findTag(node, exports.FIELD_TAG);
if (tag == null)
return null;
if (node.modifiers == null) {
return this.report(node, E.parameterWithoutModifiers());
}
var isParameterProperty = node.modifiers.some(function (modifier) {
return modifier.kind === ts.SyntaxKind.PublicKeyword ||
modifier.kind === ts.SyntaxKind.PrivateKeyword ||
modifier.kind === ts.SyntaxKind.ProtectedKeyword ||
modifier.kind === ts.SyntaxKind.ReadonlyKeyword;
});
if (!isParameterProperty) {
return this.report(node, E.parameterWithoutModifiers());
}
var notPublic = node.modifiers.find(function (modifier) {
return modifier.kind === ts.SyntaxKind.PrivateKeyword ||
modifier.kind === ts.SyntaxKind.ProtectedKeyword;
});
if (notPublic != null) {
return this.report(notPublic, E.parameterPropertyNotPublic());
}
var name = this.entityName(node, tag);
if (name == null)
return null;
if (node.type == null) {
return this.report(node, E.parameterPropertyMissingType());
}
var id = node.name;
if (ts.isArrayBindingPattern(id) || ts.isObjectBindingPattern(id)) {
// TypeScript triggers an error if a binding pattern is used for a
// parameter property, so we don't need to report them.
// https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8BQ1rHgOwgFwCcBXYPeAgCgAciAjEAS2BQDNEBfAShXdXaA
return null;
}
var directives = [
this.gql.fieldMetadataDirective(node.name, {
name: id.text == name.value ? null : id.text,
tsModulePath: null,
argCount: null,
}),
];
var type = this.collectType(node.type, { kind: "OUTPUT" });
if (type == null)
return null;
var deprecated = this.collectDeprecated(node);
if (deprecated != null) {
directives.push(deprecated);
}
var description = this.collectDescription(node);
var killsParentOnExceptionDirective = this.killsParentOnExceptionDirective(node);
if (killsParentOnExceptionDirective != null) {
directives.push(killsParentOnExceptionDirective);
}
return this.gql.fieldDefinition(node, name, type, null, directives, description);
};
Extractor.prototype.collectArgs = function (argsParam) {
var e_5, _a;
var e_6, _a;
var args = [];
var argsType = argsParam.type;
if (argsType == null) {
return this.report(argsParam, "Expected GraphQL field arguments to have a TypeScript type. If there are no arguments, you can use `args: never`.");
return this.report(argsParam, E.argumentParamIsMissingType());
}
if (argsType.kind === ts.SyntaxKind.NeverKeyword) {
if (argsType.kind === ts.SyntaxKind.UnknownKeyword) {
return [];
}
if (!ts.isTypeLiteralNode(argsType)) {
return this.report(argsType, "Expected GraphQL field arguments to be typed using a literal object: `{someField: string}`.");
return this.report(argsType, E.argumentParamIsNotObject());
}

@@ -707,8 +823,8 @@ var defaults = null;

}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_5) throw e_5.error; }
finally { if (e_6) throw e_6.error; }
}

@@ -718,3 +834,3 @@ return args;

Extractor.prototype.collectArgDefaults = function (node) {
var e_6, _a;
var e_7, _a;
var defaults = new Map();

@@ -731,8 +847,8 @@ try {

}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_6) throw e_6.error; }
finally { if (e_7) throw e_7.error; }
}

@@ -743,19 +859,20 @@ return defaults;

if (ts.isStringLiteral(node)) {
return { kind: graphql_1.Kind.STRING, loc: this.loc(node), value: node.text };
return this.gql.string(node, node.text);
}
else if (ts.isNumericLiteral(node)) {
var kind = node.text.includes(".") ? graphql_1.Kind.FLOAT : graphql_1.Kind.INT;
return { kind: kind, loc: this.loc(node), value: node.text };
return node.text.includes(".")
? this.gql.float(node, node.text)
: this.gql.int(node, node.text);
}
else if (this.isNullish(node)) {
return { kind: graphql_1.Kind.NULL, loc: this.loc(node) };
return this.gql.null(node);
}
else if (node.kind === ts.SyntaxKind.TrueKeyword) {
return { kind: graphql_1.Kind.BOOLEAN, loc: this.loc(node), value: true };
return this.gql.boolean(node, true);
}
else if (node.kind === ts.SyntaxKind.FalseKeyword) {
return { kind: graphql_1.Kind.BOOLEAN, loc: this.loc(node), value: false };
return this.gql.boolean(node, false);
}
else if (ts.isObjectLiteralExpression(node)) {
return this.cellectObjectLiteral(node);
return this.collectObjectLiteral(node);
}

@@ -765,7 +882,6 @@ else if (ts.isArrayLiteralExpression(node)) {

}
this.reportUnhandled(node, "Expected GraphQL field argument default values to be a literal.");
return null;
return this.reportUnhandled(node, "constant value", E.defaultValueIsNotLiteral());
};
Extractor.prototype.collectArrayLiteral = function (node) {
var e_7, _a;
var e_8, _a;
var values = [];

@@ -785,8 +901,8 @@ var errors = false;

}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
catch (e_8_1) { e_8 = { error: e_8_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_7) throw e_7.error; }
finally { if (e_8) throw e_8.error; }
}

@@ -796,10 +912,6 @@ if (errors) {

}
return {
kind: graphql_1.Kind.LIST,
loc: this.loc(node),
values: values
};
return this.gql.list(node, values);
};
Extractor.prototype.cellectObjectLiteral = function (node) {
var e_8, _a;
Extractor.prototype.collectObjectLiteral = function (node) {
var e_9, _a;
var fields = [];

@@ -819,8 +931,8 @@ var errors = false;

}
catch (e_8_1) { e_8 = { error: e_8_1 }; }
catch (e_9_1) { e_9 = { error: e_9_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_8) throw e_8.error; }
finally { if (e_9) throw e_9.error; }
}

@@ -830,16 +942,12 @@ if (errors) {

}
return {
kind: graphql_1.Kind.OBJECT,
loc: this.loc(node),
fields: fields
};
return this.gql.object(node, fields);
};
Extractor.prototype.collectObjectField = function (node) {
if (!ts.isPropertyAssignment(node)) {
return this.reportUnhandled(node, "Expected object literal property to be a property assignment.");
return this.reportUnhandled(node, "constant value", E.defaultArgElementIsNotAssignment());
}
if (node.name == null) {
return this.reportUnhandled(node, "Expected object literal property to have a name.");
return this.reportUnhandled(node, "field", E.defaultArgPropertyMissingName());
}
var name = this.expectIdentifier(node.name);
var name = this.expectNameIdentifier(node.name);
if (name == null)

@@ -849,3 +957,3 @@ return null;

if (initialize == null) {
return this.report(node, "Expected object literal property to have an initializer. For example: `{ offset = 10}`.");
return this.report(node, E.defaultArgPropertyMissingInitializer());
}

@@ -855,8 +963,3 @@ var value = this.collectConstValue(initialize);

return null;
return {
kind: graphql_1.Kind.OBJECT_FIELD,
loc: this.loc(node),
name: this.gqlName(node.name, name.text),
value: value
};
return this.gql.constObjectField(node, this.gql.name(node.name, name.text), value);
};

@@ -866,18 +969,20 @@ Extractor.prototype.collectArg = function (node, defaults) {

// TODO: How can I create this error?
return this.report(node, "Expected GraphQL field argument type to be a property signature.");
return this.report(node, E.argIsNotProperty());
}
if (!ts.isIdentifier(node.name)) {
// TODO: How can I create this error?
return this.report(node.name, "Expected GraphQL field argument names to be a literal.");
return this.report(node.name, E.argNameNotLiteral());
}
if (node.type == null) {
return this.report(node.name, "Expected GraphQL field argument to have a type.");
return this.report(node.name, E.argNotTyped());
}
var type = this.collectType(node.type);
var type = this.collectType(node.type, { kind: "INPUT" });
if (type == null)
return null;
if (node.questionToken) {
type = this.gqlNullableType(type);
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());
}
var description = this.collectDescription(node.name);
var defaultValue = null;

@@ -890,12 +995,18 @@ if (defaults != null) {

}
if (node.questionToken && defaultValue == null) {
// 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.
//
// ... unless there is a default value. In that case, the default will be
// used argument is omitted or references an undefined variable.
// TODO: This will catch { a?: string } but not { a?: string | undefined }.
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);
var deprecatedDirective = this.collectDeprecated(node);
return {
kind: graphql_1.Kind.INPUT_VALUE_DEFINITION,
loc: this.loc(node),
description: description || undefined,
name: this.gqlName(node.name, node.name.text),
type: type,
defaultValue: defaultValue || undefined,
directives: deprecatedDirective != null ? [deprecatedDirective] : undefined
};
return this.gql.inputValueDefinition(node, this.gql.name(node.name, node.name.text), type, deprecatedDirective == null ? null : [deprecatedDirective], defaultValue, description);
};

@@ -907,12 +1018,6 @@ Extractor.prototype.enumEnumDeclaration = function (node, tag) {

}
var description = this.collectDescription(node.name);
var description = this.collectDescription(node);
var values = this.collectEnumValues(node);
this.ctx.recordTypeName(node.name, name.value);
this.definitions.push({
kind: graphql_1.Kind.ENUM_TYPE_DEFINITION,
loc: this.loc(node),
description: description || undefined,
name: name,
values: values
});
this.recordTypeName(node.name, name, "ENUM");
this.definitions.push(this.gql.enumTypeDefinition(node, name, values, description));
};

@@ -927,15 +1032,8 @@ Extractor.prototype.enumTypeAliasDeclaration = function (node, tag) {

return;
var description = this.collectDescription(node.name);
this.ctx.recordTypeName(node.name, name.value);
this.definitions.push({
kind: graphql_1.Kind.ENUM_TYPE_DEFINITION,
loc: this.loc(node),
description: description || undefined,
name: name,
values: values
});
var description = this.collectDescription(node);
this.recordTypeName(node.name, name, "ENUM");
this.definitions.push(this.gql.enumTypeDefinition(node, name, values, description));
};
Extractor.prototype.enumTypeAliasVariants = function (node) {
var e_9, _a;
var _b;
var e_10, _a;
// Semantically we only support deriving enums from type aliases that

@@ -949,12 +1047,7 @@ // are unions of string literals. However, in the edge case of a union

return [
{
kind: graphql_1.Kind.ENUM_VALUE_DEFINITION,
name: this.gqlName(node.type.literal, node.type.literal.text),
description: undefined,
loc: this.loc(node)
},
this.gql.enumValueDefinition(node, this.gql.name(node.type.literal, node.type.literal.text), undefined, null),
];
}
if (!ts.isUnionTypeNode(node.type)) {
this.reportUnhandled(node.type, "Expected `@".concat(ENUM_TAG, "` to be a union type, or a string literal in the edge case of a single value enum."));
this.reportUnhandled(node.type, "union", E.enumTagOnInvalidNode());
return null;

@@ -964,31 +1057,7 @@ }

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) {
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({
kind: graphql_1.Kind.ENUM_VALUE_DEFINITION,
name: this.gqlName(declaration.type.literal, declaration.type.literal.text),
directives: deprecatedDirective ? [deprecatedDirective] : [],
description: memberDescription || undefined,
loc: this.loc(node)
});
continue;
}
}
}
}
}
for (var _b = __values(node.type.types), _c = _b.next(); !_c.done; _c = _b.next()) {
var member = _c.value;
if (!ts.isLiteralTypeNode(member) ||
!ts.isStringLiteral(member.literal)) {
this.reportUnhandled(member, "Expected `@".concat(ENUM_TAG, "` enum members to be string literal types. For example: `'foo'`."));
this.reportUnhandled(member, "union member", E.enumVariantNotStringLiteral());
continue;

@@ -998,16 +1067,11 @@ }

// does not allow comments attached to string literal types.
values.push({
kind: graphql_1.Kind.ENUM_VALUE_DEFINITION,
name: this.gqlName(member.literal, member.literal.text),
description: undefined,
loc: this.loc(member)
});
values.push(this.gql.enumValueDefinition(node, this.gql.name(member.literal, member.literal.text), undefined, null));
}
}
catch (e_9_1) { e_9 = { error: e_9_1 }; }
catch (e_10_1) { e_10 = { error: e_10_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_9) throw e_9.error; }
finally { if (e_10) throw e_10.error; }
}

@@ -1017,3 +1081,3 @@ return values;

Extractor.prototype.collectEnumValues = function (node) {
var e_10, _a;
var e_11, _a;
var values = [];

@@ -1025,22 +1089,16 @@ try {

!ts.isStringLiteral(member.initializer)) {
this.reportUnhandled(member, "Expected `@".concat(ENUM_TAG, "` enum members to have string literal initializers. For example: `FOO = 'foo'`."));
this.reportUnhandled(member, "enum value", E.enumVariantMissingInitializer());
continue;
}
var description = this.collectDescription(member.name);
var description = this.collectDescription(member);
var deprecated = this.collectDeprecated(member);
values.push({
kind: graphql_1.Kind.ENUM_VALUE_DEFINITION,
loc: this.loc(member),
description: description || undefined,
name: this.gqlName(member.initializer, member.initializer.text),
directives: deprecated ? [deprecated] : undefined
});
values.push(this.gql.enumValueDefinition(member, this.gql.name(member.initializer, member.initializer.text), deprecated ? [deprecated] : undefined, description));
}
}
catch (e_10_1) { e_10 = { error: e_10_1 }; }
catch (e_11_1) { e_11 = { error: e_11_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_10) throw e_10.error; }
finally { if (e_11) throw e_11.error; }
}

@@ -1054,15 +1112,56 @@ return values;

// FIXME: Use the _value_'s location not the tag's
return this.gqlName(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);
}
}
if (node.name == null) {
return this.report(node, "Expected GraphQL entity to have a name.");
return this.report(node, E.gqlEntityMissingName());
}
var id = this.expectIdentifier(node.name);
var id = this.expectNameIdentifier(node.name);
if (id == null)
return null;
return this.gqlName(id, id.text);
return this.gql.name(id, id.text);
};
// Ensure the type of the ctx param resolves to the declaration
// annotated with `@gqlContext`.
Extractor.prototype.validateContextParameter = function (node) {
if (node.type == null) {
return this.report(node, E.expectedTypeAnnotationOnContext());
}
if (node.type.kind === ts.SyntaxKind.UnknownKeyword) {
// If the user just needs to define the argument to get to a later parameter,
// they can use `ctx: unknown` to safely avoid triggering a Grats error.
return;
}
if (!ts.isTypeReferenceNode(node.type)) {
return this.report(node.type, E.expectedTypeAnnotationOfReferenceOnContext());
}
// Check for ...
if (node.dotDotDotToken != null) {
return this.report(node.dotDotDotToken, E.unexpectedParamSpreadForContextParam());
}
this.contextReferences.push(node.type.typeName);
};
Extractor.prototype.methodDeclaration = function (node) {
var tag = this.findTag(node, FIELD_TAG);
var tag = this.findTag(node, exports.FIELD_TAG);
if (tag == null)

@@ -1074,5 +1173,7 @@ return null;

if (node.type == null) {
return this.report(node.name, "Expected GraphQL field to have a type.");
return this.report(node.name, E.methodMissingType());
}
var type = this.collectMethodType(node.type);
var type = this.collectType(node.type, { kind: "OUTPUT" });
if (type == null)
return null;
// We already reported an error

@@ -1086,10 +1187,17 @@ if (type == null)

}
var description = this.collectDescription(node.name);
var id = this.expectIdentifier(node.name);
var context = node.parameters[1];
if (context != null) {
this.validateContextParameter(context);
}
var description = this.collectDescription(node);
var id = this.expectNameIdentifier(node.name);
if (id == null)
return null;
var directives = [];
if (id.text !== name.value) {
directives = [this.methodNameDirective(node.name, id.text)];
}
var directives = [
this.gql.fieldMetadataDirective(node.name, {
name: id.text === name.value ? null : id.text,
tsModulePath: null,
argCount: isCallable(node) ? node.parameters.length : null,
}),
];
var deprecated = this.collectDeprecated(node);

@@ -1099,54 +1207,19 @@ if (deprecated != null) {

}
return {
kind: graphql_1.Kind.FIELD_DEFINITION,
loc: this.loc(node),
description: description || undefined,
name: name,
arguments: args || undefined,
type: this.handleErrorBubbling(node, type),
directives: directives.length === 0 ? undefined : directives
};
};
Extractor.prototype.collectMethodType = function (node) {
var inner = this.maybeUnwrapePromise(node);
if (inner == null)
return null;
return this.collectType(inner);
};
Extractor.prototype.collectPropertyType = function (node) {
// TODO: Handle function types here.
var inner = this.maybeUnwrapePromise(node);
if (inner == null)
return null;
return this.collectType(inner);
};
Extractor.prototype.maybeUnwrapePromise = function (node) {
if (ts.isTypeReferenceNode(node)) {
var identifier = this.expectIdentifier(node.typeName);
if (identifier == null)
return null;
if (identifier.text === "Promise") {
if (node.typeArguments == null) {
return this.report(node, "Expected type reference to have type arguments.");
}
return node.typeArguments[0];
}
var killsParentOnExceptionDirective = this.killsParentOnExceptionDirective(node);
if (killsParentOnExceptionDirective != null) {
directives.push(killsParentOnExceptionDirective);
}
return node;
return this.gql.fieldDefinition(node, name, type, args, directives, description);
};
Extractor.prototype.collectDescription = function (node) {
var symbol = this.ctx.checker.getSymbolAtLocation(node);
if (symbol == null) {
return this.report(node, "Expected TypeScript to be able to resolve this GraphQL entity to a symbol.");
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 {
kind: graphql_1.Kind.STRING,
loc: this.loc(node),
value: description,
block: true
};
}
return null;

@@ -1163,15 +1236,9 @@ };

// FIXME: Use the _value_'s location not the tag's
reason = this.gqlConstArgument(tag, this.gqlName(tag, "reason"), this.gqlString(tag, reasonComment));
reason = this.gql.constArgument(tag, this.gql.name(tag, "reason"), this.gql.string(tag, reasonComment));
}
}
var args = reason == null ? undefined : [reason];
return {
kind: graphql_1.Kind.DIRECTIVE,
loc: this.loc(tag),
name: this.gqlName(tag, DEPRECATED_TAG),
arguments: args
};
return this.gql.constDirective(tag.tagName, this.gql.name(node, DEPRECATED_TAG), reason == null ? null : [reason]);
};
Extractor.prototype.property = function (node) {
var tag = this.findTag(node, FIELD_TAG);
var tag = this.findTag(node, exports.FIELD_TAG);
if (tag == null)

@@ -1183,13 +1250,13 @@ return null;

if (node.type == null) {
this.report(node.name, "Expected GraphQL field to have a type.");
this.report(node.name, E.propertyFieldMissingType());
return null;
}
var inner = this.collectPropertyType(node.type);
var inner = this.collectType(node.type, { kind: "OUTPUT" });
// We already reported an error
if (inner == null)
return null;
var type = node.questionToken == null ? inner : this.gqlNullableType(inner);
var description = this.collectDescription(node.name);
var type = node.questionToken == null ? inner : this.gql.nullableType(inner);
var description = this.collectDescription(node);
var directives = [];
var id = this.expectIdentifier(node.name);
var id = this.expectNameIdentifier(node.name);
if (id == null)

@@ -1201,19 +1268,19 @@ return null;

}
if (id.text !== name.value) {
directives = [this.methodNameDirective(node.name, id.text)];
directives.push(this.gql.fieldMetadataDirective(node.name, {
name: id.text === name.value ? null : id.text,
tsModulePath: null,
argCount: null,
}));
var killsParentOnExceptionDirective = this.killsParentOnExceptionDirective(node);
if (killsParentOnExceptionDirective != null) {
directives.push(killsParentOnExceptionDirective);
}
return {
kind: graphql_1.Kind.FIELD_DEFINITION,
loc: this.loc(node),
description: description || undefined,
name: name,
arguments: undefined,
type: this.handleErrorBubbling(node, type),
directives: directives.length === 0 ? undefined : directives
};
return this.gql.fieldDefinition(node, name, type, null, directives, description);
};
Extractor.prototype.collectType = function (node) {
// TODO: Support separate modes for input and output types
// For input nodes and field may only be optional if `null` is a valid value.
Extractor.prototype.collectType = function (node, ctx) {
var _this = this;
if (ts.isTypeReferenceNode(node)) {
var type = this.typeReference(node);
var type = this.typeReference(node, ctx);
if (type == null)

@@ -1224,57 +1291,88 @@ return null;

else if (ts.isArrayTypeNode(node)) {
var element = this.collectType(node.elementType);
var element = this.collectType(node.elementType, ctx);
if (element == null)
return null;
return this.gqlNonNullType(node, this.gqlListType(node, element));
return this.gql.nonNullType(node, this.gql.listType(node, element));
}
else if (ts.isUnionTypeNode(node)) {
var types = node.types.filter(function (type) { return !_this.isNullish(type); });
if (types.length !== 1) {
this.report(node, "Expected exactly one non-nullish type.");
return null;
if (types.length === 0) {
return this.report(node, E.expectedOneNonNullishType());
}
var type = this.collectType(types[0]);
var type = this.collectType(types[0], ctx);
if (type == null)
return null;
if (types.length > 1) {
var _a = __read(types), first = _a[0], rest = _a.slice(1);
// FIXME: If each of `rest` matches `first` this should be okay.
var incompatibleVariants = rest.map(function (tsType) {
return (0, DiagnosticError_1.tsRelated)(tsType, "Other non-nullish type");
});
this.report(first, E.expectedOneNonNullishType(), incompatibleVariants);
return null;
}
if (node.types.length > 1) {
return this.gqlNullableType(type);
return this.gql.withLocation(node, this.gql.nullableType(type));
}
return this.gqlNonNullType(node, type);
return this.gql.nonNullType(node, type);
}
else if (ts.isParenthesizedTypeNode(node)) {
return this.collectType(node.type);
return this.collectType(node.type, ctx);
}
else if (node.kind === ts.SyntaxKind.StringKeyword) {
return this.gqlNonNullType(node, this.gqlNamedType(node, "String"));
return this.gql.nonNullType(node, this.gql.namedType(node, "String"));
}
else if (node.kind === ts.SyntaxKind.BooleanKeyword) {
return this.gqlNonNullType(node, this.gqlNamedType(node, "Boolean"));
return this.gql.nonNullType(node, this.gql.namedType(node, "Boolean"));
}
else if (node.kind === ts.SyntaxKind.NumberKeyword) {
return this.report(node, "Unexpected number type. GraphQL supports both Int and Float, making `number` ambiguous. Instead, import the `Int` or `Float` type from `".concat(LIBRARY_IMPORT_NAME, "` and use that. e.g. `import { Int, Float } from \"").concat(LIBRARY_IMPORT_NAME, "\";`."));
return this.report(node, E.ambiguousNumberType());
}
else if (ts.isTypeLiteralNode(node)) {
return this.report(node, "Unexpected type literal. You may want to define a named GraphQL type elsewhere and reference it here.");
return this.report(node, E.unsupportedTypeLiteral());
}
// TODO: Better error message. This is okay if it's a type reference, but everything else is not.
this.reportUnhandled(node, "Unknown GraphQL type.");
this.reportUnhandled(node, "type", E.unknownGraphQLType());
return null;
};
Extractor.prototype.typeReference = function (node) {
var identifier = this.expectIdentifier(node.typeName);
Extractor.prototype.typeReference = function (node, ctx) {
var identifier = this.expectNameIdentifier(node.typeName);
if (identifier == null)
return null;
var typeName = identifier.text;
// Some types are not valid as input types. Validate that here:
if (ctx.kind === "INPUT") {
switch (typeName) {
case "AsyncIterable":
return this.report(node, "`AsyncIterable` is not a valid as an input type.");
case "Promise":
return this.report(node, "`Promise` is not a valid as an input type.");
}
}
switch (typeName) {
case "Array":
case "Iterator":
case "ReadonlyArray": {
case "ReadonlyArray":
case "AsyncIterable": {
if (node.typeArguments == null) {
return this.report(node, "Expected type reference to have type arguments.");
return this.report(node, E.pluralTypeMissingParameter());
}
var element = this.collectType(node.typeArguments[0]);
var element = this.collectType(node.typeArguments[0], ctx);
if (element == null)
return null;
return this.gqlNonNullType(node, this.gqlListType(node, element));
var listType = this.gql.listType(node, element);
if (typeName === "AsyncIterable") {
listType.isAsyncIterable = true;
}
return this.gql.nonNullType(node, listType);
}
case "Promise": {
if (node.typeArguments == null) {
return this.report(node, E.wrapperMissingTypeArg());
}
var element = this.collectType(node.typeArguments[0], ctx);
if (element == null)
return null;
return element;
}
default: {

@@ -1285,5 +1383,5 @@ // We may not have encountered the definition of this type yet. So, we

// A later pass will resolve the type.
var namedType = this.gqlNamedType(node, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
this.ctx.markUnresolvedType(node.typeName, namedType.name);
return this.gqlNonNullType(node, namedType);
var namedType = this.gql.namedType(node, TypeContext_1.UNRESOLVED_REFERENCE_NAME);
this.markUnresolvedType(node.typeName, namedType.name);
return this.gql.nonNullType(node, namedType);
}

@@ -1304,84 +1402,53 @@ }

};
Extractor.prototype.expectIdentifier = function (node) {
Extractor.prototype.expectNameIdentifier = function (node) {
if (ts.isIdentifier(node)) {
return node;
}
return this.report(node, "Expected an identifier.");
return this.report(node, E.expectedNameIdentifier());
};
Extractor.prototype.findTag = function (node, tagName) {
var _a;
return ((_a = ts
var tags = ts
.getJSDocTags(node)
.find(function (tag) { return tag.tagName.escapedText === tagName; })) !== null && _a !== void 0 ? _a : null);
.filter(function (tag) { return tag.tagName.escapedText === tagName; });
if (tags.length === 0) {
return null;
}
if (tags.length > 1) {
var additionalTags = tags.slice(1).map(function (tag) {
return (0, DiagnosticError_1.tsRelated)(tag, "Additional tag");
});
return this.report(tags[0], E.duplicateTag(tagName), additionalTags);
}
return tags[0];
};
// 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.
// https://graphql.org/learn/best-practices/#nullability
Extractor.prototype.handleErrorBubbling = function (parentNode, type) {
Extractor.prototype.killsParentOnExceptionDirective = function (parentNode) {
var tags = ts.getJSDocTags(parentNode);
var killsParentOnExceptions = tags.find(function (tag) { return tag.tagName.text === KILLS_PARENT_ON_EXCEPTION_TAG; });
var killsParentOnExceptions = tags.find(function (tag) { return tag.tagName.text === exports.KILLS_PARENT_ON_EXCEPTION_TAG; });
if (killsParentOnExceptions) {
if (!this.configOptions.nullableByDefault) {
this.report(killsParentOnExceptions.tagName, "Unexpected `@".concat(KILLS_PARENT_ON_EXCEPTION_TAG, "` tag. `@").concat(KILLS_PARENT_ON_EXCEPTION_TAG, "` is only supported when the Grats config `nullableByDefault` is enabled."));
}
if (type.kind !== graphql_1.Kind.NON_NULL_TYPE) {
this.report(killsParentOnExceptions.tagName, "Unexpected `@".concat(KILLS_PARENT_ON_EXCEPTION_TAG, "` tag. `@").concat(KILLS_PARENT_ON_EXCEPTION_TAG, "` is unnessesary on fields that are already nullable."));
}
return type;
return this.gql.killsParentOnExceptionDirective(killsParentOnExceptions.tagName);
}
if (this.configOptions.nullableByDefault) {
return this.gqlNullableType(type);
}
return type;
return null;
};
Extractor.prototype.methodNameDirective = function (nameNode, name) {
return this.gqlConstDirective(nameNode, this.gqlName(nameNode, serverDirectives_1.METHOD_NAME_DIRECTIVE), [
this.gqlConstArgument(nameNode, this.gqlName(nameNode, serverDirectives_1.METHOD_NAME_ARG), this.gqlString(nameNode, name)),
]);
};
Extractor.prototype.exportDirective = function (nameNode, filename, functionName) {
return this.gqlConstDirective(nameNode, this.gqlName(nameNode, serverDirectives_1.EXPORTED_DIRECTIVE), [
this.gqlConstArgument(nameNode, this.gqlName(nameNode, serverDirectives_1.EXPORTED_FILENAME_ARG), this.gqlString(nameNode, filename)),
this.gqlConstArgument(nameNode, this.gqlName(nameNode, serverDirectives_1.EXPORTED_FUNCTION_NAME_ARG), this.gqlString(nameNode, functionName)),
]);
};
/** GraphQL AST node helper methods */
Extractor.prototype.gqlName = function (node, value) {
return { kind: graphql_1.Kind.NAME, loc: this.loc(node), value: value };
};
Extractor.prototype.gqlNamedType = function (node, value) {
return {
kind: graphql_1.Kind.NAMED_TYPE,
loc: this.loc(node),
name: this.gqlName(node, value)
};
};
Extractor.prototype.gqlNonNullType = function (node, type) {
if (type.kind === graphql_1.Kind.NON_NULL_TYPE) {
return type;
}
return { kind: graphql_1.Kind.NON_NULL_TYPE, loc: this.loc(node), type: type };
};
Extractor.prototype.gqlNullableType = function (type) {
var inner = type;
while (inner.kind === graphql_1.Kind.NON_NULL_TYPE) {
inner = inner.type;
}
return inner;
};
Extractor.prototype.gqlListType = function (node, type) {
return { kind: graphql_1.Kind.LIST_TYPE, loc: this.loc(node), type: type };
};
Extractor.prototype.gqlConstArgument = function (node, name, value) {
return { kind: graphql_1.Kind.ARGUMENT, loc: this.loc(node), name: name, value: value };
};
Extractor.prototype.gqlConstDirective = function (node, name, args) {
return { kind: graphql_1.Kind.DIRECTIVE, loc: this.loc(node), name: name, arguments: args };
};
Extractor.prototype.gqlString = function (node, value) {
return { kind: graphql_1.Kind.STRING, loc: this.loc(node), value: value };
};
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*)+$/, "");
}
function isCallable(node) {
return ts.isMethodDeclaration(node) || ts.isMethodSignature(node);
}

@@ -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,9 @@

import { GraphQLSchema } from "graphql";
import * as ts from "typescript";
import { ParsedCommandLineGrats } from "./gratsConfig";
import { ReportableDiagnostics } from "./utils/DiagnosticError";
import { Result } from "./utils/Result";
export { printSDLWithoutMetadata } 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 { extract } from "./Extractor";
export { codegen } from "./codegen";
export declare function getParsedTsConfig(configFile: string): Result<ParsedCommandLineGrats, ReportableDiagnostics>;

@@ -16,39 +16,19 @@ "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.extract = exports.printSDLWithoutMetadata = void 0;
var ts = require("typescript");
var lib_1 = require("./lib");
var gratsConfig_1 = require("./gratsConfig");
var DiagnosticError_1 = require("./utils/DiagnosticError");
var Result_1 = require("./utils/Result");
var printSchema_1 = require("./printSchema");
Object.defineProperty(exports, "printSDLWithoutMetadata", { enumerable: true, get: function () { return printSchema_1.printSDLWithoutMetadata; } });
__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;
// Used by the experimental TypeScript plugin
var Extractor_1 = require("./Extractor");
Object.defineProperty(exports, "extract", { enumerable: true, get: function () { return Extractor_1.extract; } });
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 +40,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, Result_1.err)(DiagnosticError_1.ReportableDiagnostics.fromDiagnostics(parsed.errors));
}
return (0, Result_1.ok)((0, gratsConfig_1.validateGratsOptions)(parsed));
}
exports.getParsedTsConfig = getParsedTsConfig;

@@ -1,10 +0,16 @@

import { GraphQLSchema } from "graphql";
import { Result, ReportableDiagnostics } from "./utils/DiagnosticError";
import { DocumentNode, GraphQLSchema } from "graphql";
import { DiagnosticsWithoutLocationResult, ReportableDiagnostics } from "./utils/DiagnosticError";
import { Result } from "./utils/Result";
import * as ts from "typescript";
export { applyServerDirectives } from "./serverDirectives";
export type ConfigOptions = {
nullableByDefault?: boolean;
reportTypeScriptTypeErrors?: boolean;
import { ParsedCommandLineGrats } from "./gratsConfig";
export { initTsPlugin } from "./tsPlugin/initTsPlugin";
export type SchemaAndDoc = {
schema: GraphQLSchema;
doc: DocumentNode;
};
export declare function buildSchemaResult(options: ts.ParsedCommandLine): Result<GraphQLSchema, ReportableDiagnostics>;
export declare function buildSchemaResultWithHost(options: ts.ParsedCommandLine, compilerHost: ts.CompilerHost): Result<GraphQLSchema, ReportableDiagnostics>;
export declare function buildSchemaAndDocResult(options: ParsedCommandLineGrats): Result<SchemaAndDoc, ReportableDiagnostics>;
export declare function buildSchemaAndDocResultWithHost(options: ParsedCommandLineGrats, compilerHost: ts.CompilerHost): Result<SchemaAndDoc, ReportableDiagnostics>;
/**
* The core transformation pipeline of Grats.
*/
export declare function extractSchemaAndDoc(options: ParsedCommandLineGrats, program: ts.Program): DiagnosticsWithoutLocationResult<SchemaAndDoc>;
"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) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __values = (this && this.__values) || function(o) {

@@ -35,16 +13,45 @@ 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.extractSchemaAndDoc = exports.buildSchemaAndDocResultWithHost = exports.buildSchemaAndDocResult = exports.initTsPlugin = void 0;
var graphql_1 = require("graphql");
var DiagnosticError_1 = require("./utils/DiagnosticError");
var Result_1 = require("./utils/Result");
var Result_2 = require("./utils/Result");
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 serverDirectives_2 = require("./serverDirectives");
__createBinding(exports, serverDirectives_2, "applyServerDirectives");
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 addInterfaceFields_1 = require("./transforms/addInterfaceFields");
var filterNonGqlInterfaces_1 = require("./transforms/filterNonGqlInterfaces");
var resolveTypes_1 = require("./transforms/resolveTypes");
var validateAsyncIterable_1 = require("./validations/validateAsyncIterable");
var applyDefaultNullability_1 = require("./transforms/applyDefaultNullability");
var mergeExtensions_1 = require("./transforms/mergeExtensions");
var sortSchemaAst_1 = require("./transforms/sortSchemaAst");
var validateSemanticNullability_1 = require("./validations/validateSemanticNullability");
var initTsPlugin_1 = require("./tsPlugin/initTsPlugin");
Object.defineProperty(exports, "initTsPlugin", { enumerable: true, get: function () { return initTsPlugin_1.initTsPlugin; } });
// Construct a schema, using GraphQL schema language
// Exported for tests that want to intercept diagnostic errors.
function buildSchemaResult(options) {
function buildSchemaAndDocResult(options) {
// https://stackoverflow.com/a/66604532/1263117

@@ -54,136 +61,129 @@ var compilerHost = ts.createCompilerHost(options.options,

true);
return buildSchemaResultWithHost(options, compilerHost);
return buildSchemaAndDocResultWithHost(options, compilerHost);
}
exports.buildSchemaResult = buildSchemaResult;
function buildSchemaResultWithHost(options, compilerHost) {
var gratsOptions = parseGratsOptions(options);
var schemaResult = extractSchema(options, gratsOptions, 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));
exports.buildSchemaAndDocResult = buildSchemaAndDocResult;
function buildSchemaAndDocResultWithHost(options, compilerHost) {
var program = ts.createProgram(options.fileNames, options.options, compilerHost);
return new Result_1.ResultPipe(extractSchemaAndDoc(options, program))
.mapErr(function (e) { return new DiagnosticError_1.ReportableDiagnostics(compilerHost, e); })
.result();
}
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;
exports.buildSchemaAndDocResultWithHost = buildSchemaAndDocResultWithHost;
/**
* The core transformation pipeline of Grats.
*/
function extractSchemaAndDoc(options, program) {
return new Result_1.ResultPipe((0, snapshotsFromProgram_1.extractSnapshotsFromProgram)(program, options))
.map(function (snapshots) { return combineSnapshots(snapshots); })
.andThen(function (snapshot) {
var typesWithTypename = snapshot.typesWithTypename;
var config = options.raw.grats;
var checker = program.getTypeChecker();
var ctx = TypeContext_1.TypeContext.fromSnapshot(checker, snapshot);
// Collect validation errors
var validationResult = (0, Result_1.concatResults)((0, validateMergedInterfaces_1.validateMergedInterfaces)(checker, snapshot.interfaceDeclarations), (0, validateContextReferences_1.validateContextReferences)(ctx, snapshot.contextReferences));
var docResult = new Result_1.ResultPipe(validationResult)
// Add the metadata directive definitions to definitions
// found in the snapshot.
.map(function () { return (0, metadataDirectives_1.addMetadataDirectives)(snapshot.definitions); })
// 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.
.andThen(function (definitions) { return (0, addInterfaceFields_1.addInterfaceFields)(ctx, definitions); })
// Convert the definitions into a DocumentNode
.map(function (definitions) { return ({ kind: graphql_1.Kind.DOCUMENT, definitions: definitions }); })
// Filter out any `implements` clauses that are not GraphQL interfaces.
.map(function (doc) { return (0, filterNonGqlInterfaces_1.filterNonGqlInterfaces)(ctx, doc); })
// Resolve TypeScript type references to the GraphQL types they represent (or error).
.andThen(function (doc) { return (0, resolveTypes_1.resolveTypes)(ctx, doc); })
// Ensure all subscription fields return an AsyncIterable.
.andThen(function (doc) { return (0, validateAsyncIterable_1.validateAsyncIterable)(doc); })
// Apply default nullability to fields and arguments, and detect any misuse of
// `@killsParentOnException`.
.andThen(function (doc) { return (0, applyDefaultNullability_1.applyDefaultNullability)(doc, config); })
// Merge any `extend` definitions into their base definitions.
.map(function (doc) { return (0, mergeExtensions_1.mergeExtensions)(doc); })
// Sort the definitions in the document to ensure a stable output.
.map(function (doc) { return (0, sortSchemaAst_1.sortSchemaAst)(doc); })
.result();
if (docResult.kind === "ERROR") {
return docResult;
}
var doc = docResult.value;
// Build and validate the schema with regards to the GraphQL spec.
return (new Result_1.ResultPipe(buildSchemaFromDoc(doc))
// Ensure that every type which implements an interface or is a member of a
// union has a __typename field.
.andThen(function (schema) { return (0, validateTypenames_1.validateTypenames)(schema, typesWithTypename); })
.andThen(function (schema) { return (0, validateSemanticNullability_1.validateSemanticNullability)(schema, config); })
// Combine the schema and document into a single result.
.map(function (schema) { return ({ schema: schema, doc: doc }); })
.result());
})
.result();
}
exports.extractSchemaAndDoc = extractSchemaAndDoc;
// Given a SDL AST, build and validate a GraphQLSchema.
function buildSchemaFromDoc(doc) {
// 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);
if (validationErrors.length > 0) {
return (0, Result_2.err)(validationErrors.map(DiagnosticError_1.graphQlErrorToDiagnostic));
}
else if (typeof gratsOptions.nullableByDefault !== "boolean") {
throw new Error("Grats: The Grats config option `nullableByDefault` 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; });
if (diagnostics.length > 0) {
return (0, Result_2.err)(diagnostics.map(DiagnosticError_1.graphQlErrorToDiagnostic));
}
if (gratsOptions.reportTypeScriptTypeErrors === undefined) {
gratsOptions.reportTypeScriptTypeErrors = false;
}
else if (typeof gratsOptions.reportTypeScriptTypeErrors !== "boolean") {
throw new Error("Grats: The Grats config option `reportTypeScriptTypeErrors` must be a boolean if provided");
}
// FIXME: Check for unknown options
return gratsOptions;
return (0, Result_2.ok)(schema);
}
function extractSchema(options, gratsOptions, host) {
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);
// Given a list of snapshots, merge them into a single snapshot.
function combineSnapshots(snapshots) {
var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f, e_7, _g;
var result = {
definitions: [],
nameDefinitions: new Map(),
unresolvedNames: new Map(),
contextReferences: [],
typesWithTypename: new Set(),
interfaceDeclarations: [],
};
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) {
return (0, DiagnosticError_1.err)(typeErrors);
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 _h = (e_2 = void 0, __values(snapshot.definitions)), _j = _h.next(); !_j.done; _j = _h.next()) {
var definition = _j.value;
result.definitions.push(definition);
}
}
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.
return (0, DiagnosticError_1.err)([syntaxErrors[0]]);
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_j && !_j.done && (_b = _h.return)) _b.call(_h);
}
finally { if (e_2) throw e_2.error; }
}
var extractor = new Extractor_1.Extractor(sourceFile, ctx, gratsOptions);
var extractedResult = extractor.extract();
if (extractedResult.kind === "ERROR")
return extractedResult;
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);
for (var _k = (e_3 = 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_2_1) { e_2 = { error: e_2_1 }; }
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e["return"])) _b.call(_e);
if (_l && !_l.done && (_c = _k.return)) _c.call(_k);
}
finally { if (e_2) throw e_2.error; }
finally { if (e_3) throw e_3.error; }
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
var docResult = ctx.resolveTypes({ kind: graphql_1.Kind.DOCUMENT, definitions: definitions });
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 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);
}
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);
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);
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 _o = (e_4 = 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);
}

@@ -194,16 +194,55 @@ }

try {
if (typeImplementors_1_1 && !typeImplementors_1_1.done && (_b = typeImplementors_1["return"])) _b.call(typeImplementors_1);
if (_p && !_p.done && (_d = _o.return)) _d.call(_o);
}
finally { if (e_4) throw e_4.error; }
}
try {
for (var _r = (e_5 = void 0, __values(snapshot.contextReferences)), _s = _r.next(); !_s.done; _s = _r.next()) {
var contextReference = _s.value;
result.contextReferences.push(contextReference);
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_s && !_s.done && (_e = _r.return)) _e.call(_r);
}
finally { if (e_5) throw e_5.error; }
}
try {
for (var _t = (e_6 = void 0, __values(snapshot.typesWithTypename)), _u = _t.next(); !_u.done; _u = _t.next()) {
var typeName = _u.value;
result.typesWithTypename.add(typeName);
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (_u && !_u.done && (_f = _t.return)) _f.call(_t);
}
finally { if (e_6) throw e_6.error; }
}
try {
for (var _v = (e_7 = void 0, __values(snapshot.interfaceDeclarations)), _w = _v.next(); !_w.done; _w = _v.next()) {
var interfaceDeclaration = _w.value;
result.interfaceDeclarations.push(interfaceDeclaration);
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (_w && !_w.done && (_g = _v.return)) _g.call(_v);
}
finally { if (e_7) throw e_7.error; }
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
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; }
finally { if (e_1) throw e_1.error; }
}
return typenameDiagnostics;
return result;
}

@@ -1,5 +0,10 @@

import { DocumentNode, NameNode } from "graphql";
import { NameNode } from "graphql";
import * as ts from "typescript";
import { DiagnosticResult, DiagnosticsResult } from "./utils/DiagnosticError";
import { ExtractionSnapshot } from "./Extractor";
export declare const UNRESOLVED_REFERENCE_NAME = "__UNRESOLVED_REFERENCE__";
export type NameDefinition = {
name: NameNode;
kind: "TYPE" | "INTERFACE" | "UNION" | "SCALAR" | "INPUT_OBJECT" | "ENUM";
};
/**

@@ -19,15 +24,13 @@ * Used to track TypeScript references.

checker: ts.TypeChecker;
host: ts.CompilerHost;
_options: ts.ParsedCommandLine;
_symbolToName: Map<ts.Symbol, string>;
_symbolToName: Map<ts.Symbol, NameDefinition>;
_unresolvedTypes: Map<NameNode, ts.Symbol>;
hasTypename: Set<string>;
constructor(options: ts.ParsedCommandLine, checker: ts.TypeChecker, host: ts.CompilerHost);
recordTypeName(node: ts.Node, name: string): void;
recordHasTypenameField(name: string): void;
markUnresolvedType(node: ts.Node, name: NameNode): void;
resolveTypes(doc: DocumentNode): DiagnosticsResult<DocumentNode>;
static fromSnapshot(checker: ts.TypeChecker, snapshot: ExtractionSnapshot): TypeContext;
constructor(checker: ts.TypeChecker);
_recordTypeName(node: ts.Node, name: NameNode, kind: NameDefinition["kind"]): void;
_markUnresolvedType(node: ts.Node, name: NameNode): void;
findSymbolDeclaration(startSymbol: ts.Symbol): ts.Declaration | null;
resolveSymbol(startSymbol: ts.Symbol): ts.Symbol;
resolveNamedType(unresolved: NameNode): DiagnosticResult<NameNode>;
validateInterfaceImplementorsHaveTypenameField(): DiagnosticResult<null>;
getDestFilePath(sourceFile: ts.SourceFile): string;
unresolvedNameIsGraphQL(unresolved: NameNode): boolean;
getNameDefinition(nameNode: NameNode): DiagnosticsResult<NameDefinition>;
}

@@ -13,8 +13,36 @@ "use strict";

};
exports.__esModule = true;
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.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 Result_1 = require("./utils/Result");
var E = require("./Errors");
var helpers_1 = require("./utils/helpers");
exports.UNRESOLVED_REFERENCE_NAME = "__UNRESOLVED_REFERENCE__";

@@ -34,11 +62,41 @@ /**

var TypeContext = /** @class */ (function () {
function TypeContext(options, checker, host) {
function TypeContext(checker) {
this._symbolToName = new Map();
this._unresolvedTypes = new Map();
this.hasTypename = new Set();
this._options = options;
this.checker = checker;
this.host = host;
}
TypeContext.prototype.recordTypeName = function (node, name) {
TypeContext.fromSnapshot = function (checker, snapshot) {
var e_1, _a, e_2, _b;
var self = new TypeContext(checker);
try {
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];
self._markUnresolvedType(node, typeName);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
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];
self._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; }
}
return self;
};
// Record that a GraphQL construct of type `kind` with the name `name` is
// declared at `node`.
TypeContext.prototype._recordTypeName = function (node, name, kind) {
var symbol = this.checker.getSymbolAtLocation(node);

@@ -53,8 +111,6 @@ if (symbol == null) {

}
this._symbolToName.set(symbol, name);
this._symbolToName.set(symbol, { name: name, kind: kind });
};
TypeContext.prototype.recordHasTypenameField = function (name) {
this.hasTypename.add(name);
};
TypeContext.prototype.markUnresolvedType = function (node, name) {
// Record that a type reference `node`
TypeContext.prototype._markUnresolvedType = function (node, name) {
var symbol = this.checker.getSymbolAtLocation(node);

@@ -65,25 +121,23 @@ if (symbol == null) {

}
if (symbol.flags & ts.SymbolFlags.Alias) {
// Follow any aliases to get the real type declaration.
symbol = this.checker.getAliasedSymbol(symbol);
}
this._unresolvedTypes.set(name, symbol);
this._unresolvedTypes.set(name, this.resolveSymbol(symbol));
};
TypeContext.prototype.resolveTypes = function (doc) {
var _this = this;
var errors = [];
var newDoc = (0, graphql_1.visit)(doc, {
Name: function (t) {
var namedTypeResult = _this.resolveNamedType(t);
if (namedTypeResult.kind === "ERROR") {
errors.push(namedTypeResult.err);
return t;
}
return namedTypeResult.value;
TypeContext.prototype.findSymbolDeclaration = function (startSymbol) {
var _a;
var symbol = this.resolveSymbol(startSymbol);
var declaration = (_a = symbol.declarations) === null || _a === void 0 ? void 0 : _a[0];
return declaration !== null && declaration !== void 0 ? declaration : null;
};
// Follow symbol aliases until we find the original symbol. Accounts for
// cyclical aliases.
TypeContext.prototype.resolveSymbol = function (startSymbol) {
var symbol = startSymbol;
var visitedSymbols = new Set();
while (ts.SymbolFlags.Alias & symbol.flags) {
if (visitedSymbols.has(symbol)) {
throw new Error("Cyclical alias detected. Breaking resolution.");
}
});
if (errors.length > 0) {
return (0, DiagnosticError_1.err)(errors);
visitedSymbols.add(symbol);
symbol = this.checker.getAliasedSymbol(symbol);
}
return (0, DiagnosticError_1.ok)(newDoc);
return symbol;
};

@@ -97,25 +151,31 @@ TypeContext.prototype.resolveNamedType = function (unresolved) {

}
return (0, DiagnosticError_1.ok)(unresolved);
return (0, Result_1.ok)(unresolved);
}
var name = this._symbolToName.get(symbol);
if (name == null) {
if (unresolved.loc == null) {
throw new Error("Expected namedType to have a location.");
}
return (0, DiagnosticError_1.err)({
messageText: "This type is not a valid GraphQL type. Did you mean to annotate it's definition with a `/** @gql */` tag such as `/** @gqlType */` or `/** @gqlInput **/`?",
start: unresolved.loc.start,
length: unresolved.loc.end - unresolved.loc.start,
category: ts.DiagnosticCategory.Error,
code: DiagnosticError_1.FAKE_ERROR_CODE,
file: ts.createSourceFile(unresolved.loc.source.name, unresolved.loc.source.body, ts.ScriptTarget.Latest)
});
var nameDefinition = this._symbolToName.get(symbol);
if (nameDefinition == null) {
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(unresolved), E.unresolvedTypeReference()));
}
return (0, DiagnosticError_1.ok)(__assign(__assign({}, unresolved), { value: name }));
return (0, Result_1.ok)(__assign(__assign({}, unresolved), { value: nameDefinition.name.value }));
};
TypeContext.prototype.validateInterfaceImplementorsHaveTypenameField = function () {
return (0, DiagnosticError_1.ok)(null);
TypeContext.prototype.unresolvedNameIsGraphQL = function (unresolved) {
var symbol = this._unresolvedTypes.get(unresolved);
return symbol != null && this._symbolToName.has(symbol);
};
TypeContext.prototype.getDestFilePath = function (sourceFile) {
return (0, gratsRoot_1.getRelativeOutputPath)(this._options, sourceFile);
// TODO: Merge this with resolveNamedType
TypeContext.prototype.getNameDefinition = function (nameNode) {
var typeNameResult = this.resolveNamedType(nameNode);
if (typeNameResult.kind === "ERROR") {
return (0, Result_1.err)([typeNameResult.err]);
}
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, Result_1.ok)(nameDefinition);
};

@@ -122,0 +182,0 @@ return TypeContext;

"use strict";
exports.__esModule = true;
Object.defineProperty(exports, "__esModule", { value: true });
import { GraphQLError, Location, Source } from "graphql";
import * as ts from "typescript";
type Ok<T> = {
kind: "OK";
value: T;
};
type Err<E> = {
kind: "ERROR";
err: E;
};
export type Result<T, E> = Ok<T> | Err<E>;
export type DiagnosticResult<T> = Result<T, ts.Diagnostic>;
export type DiagnosticsResult<T> = Result<T, ts.Diagnostic[]>;
export declare function ok<T>(value: T): Ok<T>;
export declare function err<E>(err: E): Err<E>;
import { Result } from "./Result";
export type DiagnosticResult<T> = Result<T, ts.DiagnosticWithLocation>;
export type DiagnosticsResult<T> = Result<T, ts.DiagnosticWithLocation[]>;
export type DiagnosticsWithoutLocationResult<T> = Result<T, ts.Diagnostic[]>;
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;

@@ -25,4 +17,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.DiagnosticWithLocation;
export declare function gqlRelated(loc: Location, message: string): ts.DiagnosticRelatedInformation;
export declare function rangeErr(file: ts.SourceFile, commentRange: ts.CommentRange, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.DiagnosticWithLocation;
export declare function tsErr(node: ts.Node, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[]): ts.DiagnosticWithLocation;
export declare function tsRelated(node: ts.Node, message: string): ts.DiagnosticRelatedInformation;
export declare function graphqlSourceToSourceFile(source: Source): ts.SourceFile;
export {};
"use strict";
exports.__esModule = true;
exports.graphqlSourceToSourceFile = exports.diagnosticAtGraphQLLocation = exports.graphQlErrorToDiagnostic = exports.FAKE_ERROR_CODE = exports.ReportableDiagnostics = exports.err = exports.ok = 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;
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.graphqlSourceToSourceFile = exports.tsRelated = exports.tsErr = exports.rangeErr = exports.gqlRelated = exports.gqlErr = exports.graphQlErrorToDiagnostic = exports.FAKE_ERROR_CODE = exports.ReportableDiagnostics = void 0;
var ts = require("typescript");
function ok(value) {
return { kind: "OK", value: value };
}
exports.ok = ok;
function err(err) {
return { kind: "ERROR", err: err };
}
exports.err = err;
var ReportableDiagnostics = /** @class */ (function () {

@@ -18,2 +37,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 () {

@@ -32,3 +61,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.

@@ -44,2 +73,3 @@ exports.FAKE_ERROR_CODE = 349389149282;

function graphQlErrorToDiagnostic(error) {
var e_1, _a;
var position = error.positions[0];

@@ -49,2 +79,34 @@ if (position == null) {

}
// Start with baseline location information
var start = position;
var length = 1;
var relatedInformation;
// Nodes have actual ranges (not just a single position), so we we have one
// (or more!) use that instead.
if (error.nodes != null && error.nodes.length > 0) {
var _b = __read(error.nodes), node = _b[0], rest = _b.slice(1);
if (node.loc != null) {
start = node.loc.start;
length = node.loc.end - node.loc.start;
if (rest.length > 0) {
relatedInformation = [];
try {
for (var rest_1 = __values(rest), rest_1_1 = rest_1.next(); !rest_1_1.done; rest_1_1 = rest_1.next()) {
var relatedNode = rest_1_1.value;
if (relatedNode.loc == null) {
continue;
}
relatedInformation.push(gqlRelated(relatedNode.loc, "Related location"));
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (rest_1_1 && !rest_1_1.done && (_a = rest_1.return)) _a.call(rest_1);
}
finally { if (e_1) throw e_1.error; }
}
}
}
}
var sourceFile;

@@ -59,9 +121,10 @@ if (error.source != null) {

category: ts.DiagnosticCategory.Error,
start: position,
// FIXME: Improve ranges
length: 1
start: start,
length: length,
relatedInformation: relatedInformation,
source: "Grats",
};
}
exports.graphQlErrorToDiagnostic = graphQlErrorToDiagnostic;
function diagnosticAtGraphQLLocation(message, loc) {
function gqlErr(loc, message, relatedInformation) {
return {

@@ -73,6 +136,61 @@ messageText: message,

start: loc.start,
length: loc.end - loc.start
length: loc.end - loc.start,
relatedInformation: relatedInformation,
source: "Grats",
};
}
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 rangeErr(file, commentRange, message, relatedInformation) {
var start = commentRange.pos;
var length = commentRange.end - commentRange.pos;
return {
messageText: message,
file: file,
code: exports.FAKE_ERROR_CODE,
category: ts.DiagnosticCategory.Error,
start: start,
length: length,
relatedInformation: relatedInformation,
source: "Grats",
};
}
exports.rangeErr = rangeErr;
function tsErr(node, message, relatedInformation) {
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,
source: "Grats",
};
}
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) {

@@ -79,0 +197,0 @@ return ts.createSourceFile(source.name, source.body, ts.ScriptTarget.Latest);

{
"name": "grats",
"version": "0.0.0-main-1acf2fbd",
"version": "0.0.0-main-1f647d1c",
"main": "dist/src/index.js",

@@ -9,12 +9,14 @@ "bin": "dist/src/cli.js",

"files": [
"dist"
"dist",
"!dist/src/tests"
],
"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",
"@types/semver": "^7.5.6",
"@typescript-eslint/eslint-plugin": "^5.55.0",

@@ -26,3 +28,5 @@ "@typescript-eslint/parser": "^5.55.0",

"path-browserify": "^1.0.1",
"prettier": "^2.8.7",
"process": "^0.11.10",
"semver": "^7.5.4",
"ts-node": "^10.9.1"

@@ -33,9 +37,34 @@ },

},
"packageManager": "pnpm@8.1.1",
"packageManager": "pnpm@8.12.0",
"engines": {
"node": ">=16 <=21",
"pnpm": "^8"
},
"bugs": {
"url": "https://github.com/captbaritone/grats/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/captbaritone/grats.git"
},
"author": {
"name": "Jordan Eldredge",
"email": "jordan@jordaneldredge.com",
"url": "https://jordaneldredge.com"
},
"keywords": [
"graphql",
"typescript",
"resolvers",
"schema",
"code-first",
"implementation-first"
],
"scripts": {
"test": "ts-node --esm src/tests/test.ts",
"test": "ts-node src/tests/test.ts",
"integration-tests": "node src/tests/integration.mjs",
"build": "tsc --build",
"lint": "eslint src/**/*.ts"
"build": "rm -rf dist/ && tsc --build",
"format": "prettier . --write",
"lint": "eslint . && 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,5 +5,7 @@

**What if building a GraphQL server were as easy as writing the resolvers?**
_Beta Software: Grats is largely stable and being used in production in multiple places. If you encounter any issues, don't hesitate to let us know._
When you write your GraphQL server in TypeScript, your fields and resovlers
**What if building a GraphQL server were as simple as just writing functions?**
When you write your GraphQL server in TypeScript, your fields and resolvers
are _already_ annotated with type information. _Grats leverages your existing

@@ -18,7 +16,33 @@ type annotations to automatically extract an executable GraphQL schema from your

By making your TypeScript implementation the source of truth, you never have to
worry about valiating that your implementiaton matches your schema. Your
worry about validating that your implementation matches your schema. Your
implementation _is_ your schema!
## Read the docs: https://capt.dev/grats
## 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 contains your GraphQL schema definition:
```graphql
type User {
name: String
greet(greeting: String!): String
}
```
That's just the beginning! To learn more, **Read the docs: https://grats.capt.dev/**
## Contributing

@@ -30,9 +54,9 @@

* [@mofeiZ](https://github.com/mofeiZ) and [@alunyov](https://github/alunyov) for their Relay hack-week project exploring a similar idea.
* [@josephsavona](https://github.com/josephsavona) for input on the design of [Relay Resolvers](https://relay.dev/docs/guides/relay-resolvers/) which inspired this project.
* [@bradzacher](https://github.com/bradzacher) for tips on how to handle TypeScript ASTs.
* Everyone who worked on Meta's Hack GraphQL server, the developer experince of which inspired this project.
* A number of other projects which seem to have explored similar ideas in the past:
* [ts2gql](https://github.com/convoyinc/ts2gql)
* [ts2graphql](https://github.com/cevek/ts2graphql)
* [typegraphql-reflection-poc](https://github.com/MichalLytek/typegraphql-reflection-poc)
- [@mofeiZ](https://github.com/mofeiZ) and [@alunyov](https://github/alunyov) for their Relay hack-week project exploring a similar idea.
- [@josephsavona](https://github.com/josephsavona) for input on the design of [Relay Resolvers](https://relay.dev/docs/guides/relay-resolvers/) which inspired this project.
- [@bradzacher](https://github.com/bradzacher) for tips on how to handle TypeScript ASTs.
- Everyone who worked on Meta's Hack GraphQL server, the developer experience of which inspired this project.
- A number of other projects which seem to have explored similar ideas in the past:
- [ts2gql](https://github.com/convoyinc/ts2gql)
- [ts2graphql](https://github.com/cevek/ts2graphql)
- [typegraphql-reflection-poc](https://github.com/MichalLytek/typegraphql-reflection-poc)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc