Comparing version 0.0.0-main-1acf2fbd to 0.0.0-main-1eed5234
{ | ||
"name": "grats", | ||
"version": "0.0.2", | ||
"version": "0.0.25", | ||
"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" | ||
] | ||
} |
#!/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, DefinitionNode } 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"; | ||
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 declare const SPECIFIED_BY_TAG = "specifiedBy"; | ||
export type ExtractionSnapshot = { | ||
readonly definitions: DefinitionNode[]; | ||
readonly unresolvedNames: Map<ts.EntityName, NameNode>; | ||
readonly nameDefinitions: Map<ts.DeclarationStatement, 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>; |
@@ -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,47 @@ 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 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 resolveTypes_1 = require("./transforms/resolveTypes"); | ||
// Export the TypeScript plugin implementation used by | ||
// grats-ts-plugin | ||
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 +63,128 @@ 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); }) | ||
// Filter out any `implements` clauses that are not GraphQL interfaces. | ||
.map(function (definitions) { return (0, filterNonGqlInterfaces_1.filterNonGqlInterfaces)(ctx, definitions); }) | ||
.andThen(function (definitions) { return (0, resolveTypes_1.resolveTypes)(ctx, definitions); }) | ||
// If you define a field on an interface using the functional style, we need to add | ||
// 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 }); }) | ||
// 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 +195,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,11 @@ | ||
import { DocumentNode, NameNode } from "graphql"; | ||
import { InputObjectTypeDefinitionNode, InterfaceTypeDefinitionNode, NameNode, ObjectTypeDefinitionNode, UnionTypeDefinitionNode } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { DiagnosticResult, DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import { DiagnosticResult } from "./utils/DiagnosticError"; | ||
import { ExtractionSnapshot } from "./Extractor"; | ||
export declare const UNRESOLVED_REFERENCE_NAME = "__UNRESOLVED_REFERENCE__"; | ||
export type NameDefinition = { | ||
name: NameNode; | ||
kind: "TYPE" | "INTERFACE" | "UNION" | "SCALAR" | "INPUT_OBJECT" | "ENUM"; | ||
}; | ||
type TsIdentifier = number; | ||
/** | ||
@@ -19,15 +25,20 @@ * Used to track TypeScript references. | ||
checker: ts.TypeChecker; | ||
host: ts.CompilerHost; | ||
_options: ts.ParsedCommandLine; | ||
_symbolToName: Map<ts.Symbol, string>; | ||
_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>; | ||
resolveNamedType(unresolved: NameNode): DiagnosticResult<NameNode>; | ||
validateInterfaceImplementorsHaveTypenameField(): DiagnosticResult<null>; | ||
getDestFilePath(sourceFile: ts.SourceFile): string; | ||
_declarationToName: Map<ts.Declaration, NameDefinition>; | ||
_unresolvedNodes: Map<TsIdentifier, ts.EntityName>; | ||
_idToDeclaration: Map<TsIdentifier, ts.Declaration>; | ||
static fromSnapshot(checker: ts.TypeChecker, snapshot: ExtractionSnapshot): TypeContext; | ||
constructor(checker: ts.TypeChecker); | ||
private _recordTypeName; | ||
private _markUnresolvedType; | ||
findSymbolDeclaration(startSymbol: ts.Symbol): ts.Declaration | null; | ||
private resolveSymbol; | ||
resolveUnresolvedNamedType(unresolved: NameNode): DiagnosticResult<NameNode>; | ||
unresolvedNameIsGraphQL(unresolved: NameNode): boolean; | ||
gqlNameDefinitionForGqlName(nameNode: NameNode): DiagnosticResult<NameDefinition>; | ||
gqlNameForTsName(node: ts.EntityName): DiagnosticResult<string>; | ||
private maybeTsDeclarationForTsName; | ||
tsDeclarationForTsName(node: ts.EntityName): DiagnosticResult<ts.Declaration>; | ||
tsDeclarationForGqlDefinition(definition: ObjectTypeDefinitionNode | UnionTypeDefinitionNode | InputObjectTypeDefinitionNode | InterfaceTypeDefinitionNode): ts.Declaration; | ||
getEntityName(name: NameNode): ts.EntityName | null; | ||
} | ||
export {}; |
@@ -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,85 +62,160 @@ /** | ||
var TypeContext = /** @class */ (function () { | ||
function TypeContext(options, checker, host) { | ||
this._symbolToName = new Map(); | ||
this._unresolvedTypes = new Map(); | ||
this.hasTypename = new Set(); | ||
this._options = options; | ||
function TypeContext(checker) { | ||
this._declarationToName = new Map(); | ||
this._unresolvedNodes = new Map(); | ||
this._idToDeclaration = new Map(); | ||
this.checker = checker; | ||
this.host = host; | ||
} | ||
TypeContext.prototype.recordTypeName = function (node, name) { | ||
var symbol = this.checker.getSymbolAtLocation(node); | ||
if (symbol == null) { | ||
// FIXME: Make this a diagnostic | ||
throw new Error("Could not resolve type reference. You probably have a TypeScript error."); | ||
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); | ||
} | ||
} | ||
if (this._symbolToName.has(symbol)) { | ||
// Ensure we never try to record the same name twice. | ||
throw new Error("Unexpected double recording of 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; } | ||
} | ||
this._symbolToName.set(symbol, name); | ||
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; | ||
}; | ||
TypeContext.prototype.recordHasTypenameField = function (name) { | ||
this.hasTypename.add(name); | ||
// Record that a GraphQL construct of type `kind` with the name `name` is | ||
// declared at `node`. | ||
TypeContext.prototype._recordTypeName = function (node, name, kind) { | ||
this._idToDeclaration.set(name.tsIdentifier, node); | ||
this._declarationToName.set(node, { name: name, kind: kind }); | ||
}; | ||
TypeContext.prototype.markUnresolvedType = function (node, name) { | ||
var symbol = this.checker.getSymbolAtLocation(node); | ||
if (symbol == null) { | ||
// | ||
throw new Error("Could not resolve type reference. You probably have a TypeScript error."); | ||
} | ||
if (symbol.flags & ts.SymbolFlags.Alias) { | ||
// Follow any aliases to get the real type declaration. | ||
// Record that a type references `node` | ||
TypeContext.prototype._markUnresolvedType = function (node, name) { | ||
this._unresolvedNodes.set(name.tsIdentifier, node); | ||
}; | ||
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."); | ||
} | ||
visitedSymbols.add(symbol); | ||
symbol = this.checker.getAliasedSymbol(symbol); | ||
} | ||
this._unresolvedTypes.set(name, symbol); | ||
return 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; | ||
} | ||
}); | ||
if (errors.length > 0) { | ||
return (0, DiagnosticError_1.err)(errors); | ||
TypeContext.prototype.resolveUnresolvedNamedType = function (unresolved) { | ||
if (unresolved.value !== exports.UNRESOLVED_REFERENCE_NAME) { | ||
return (0, Result_1.ok)(unresolved); | ||
} | ||
return (0, DiagnosticError_1.ok)(newDoc); | ||
var typeReference = this.getEntityName(unresolved); | ||
if (typeReference == null) { | ||
throw new Error("Unexpected unresolved reference name."); | ||
} | ||
var declarationResult = this.tsDeclarationForTsName(typeReference); | ||
if (declarationResult.kind === "ERROR") { | ||
return (0, Result_1.err)(declarationResult.err); | ||
} | ||
if (ts.isTypeParameterDeclaration(declarationResult.value)) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(unresolved), "Type parameters are not supported in this context.")); | ||
} | ||
var nameDefinition = this._declarationToName.get(declarationResult.value); | ||
if (nameDefinition == null) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(unresolved), E.unresolvedTypeReference())); | ||
} | ||
return (0, Result_1.ok)(__assign(__assign({}, unresolved), { value: nameDefinition.name.value })); | ||
}; | ||
TypeContext.prototype.resolveNamedType = function (unresolved) { | ||
var symbol = this._unresolvedTypes.get(unresolved); | ||
TypeContext.prototype.unresolvedNameIsGraphQL = function (unresolved) { | ||
var referenceNode = this.getEntityName(unresolved); | ||
if (referenceNode == null) | ||
return false; | ||
var declaration = this.maybeTsDeclarationForTsName(referenceNode); | ||
if (declaration == null) | ||
return false; | ||
return this._declarationToName.has(declaration); | ||
}; | ||
TypeContext.prototype.gqlNameDefinitionForGqlName = function (nameNode) { | ||
var referenceNode = this.getEntityName(nameNode); | ||
if (referenceNode == null) { | ||
throw new Error("Expected to find reference node for name node."); | ||
} | ||
var declaration = this.maybeTsDeclarationForTsName(referenceNode); | ||
if (declaration == null) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(nameNode), E.unresolvedTypeReference())); | ||
} | ||
var definition = this._declarationToName.get(declaration); | ||
if (definition == null) { | ||
throw new Error("Expected to find name definition."); | ||
} | ||
return (0, Result_1.ok)(definition); | ||
}; | ||
// Note! This assumes you have already handled any type parameters. | ||
TypeContext.prototype.gqlNameForTsName = function (node) { | ||
var declarationResult = this.tsDeclarationForTsName(node); | ||
if (declarationResult.kind === "ERROR") { | ||
return (0, Result_1.err)(declarationResult.err); | ||
} | ||
if (ts.isTypeParameterDeclaration(declarationResult.value)) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, "Type parameter not valid", [ | ||
(0, DiagnosticError_1.tsErr)(declarationResult.value, "Defined here"), | ||
])); | ||
} | ||
var nameDefinition = this._declarationToName.get(declarationResult.value); | ||
if (nameDefinition == null) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, E.unresolvedTypeReference())); | ||
} | ||
return (0, Result_1.ok)(nameDefinition.name.value); | ||
}; | ||
TypeContext.prototype.maybeTsDeclarationForTsName = function (node) { | ||
var symbol = this.checker.getSymbolAtLocation(node); | ||
if (symbol == null) { | ||
if (unresolved.value === exports.UNRESOLVED_REFERENCE_NAME) { | ||
// This is a logic error on our side. | ||
throw new Error("Unexpected unresolved reference name."); | ||
} | ||
return (0, DiagnosticError_1.ok)(unresolved); | ||
return null; | ||
} | ||
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) | ||
}); | ||
return this.findSymbolDeclaration(symbol); | ||
}; | ||
TypeContext.prototype.tsDeclarationForTsName = function (node) { | ||
var declaration = this.maybeTsDeclarationForTsName(node); | ||
if (!declaration) { | ||
return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, E.unresolvedTypeReference())); | ||
} | ||
return (0, DiagnosticError_1.ok)(__assign(__assign({}, unresolved), { value: name })); | ||
return (0, Result_1.ok)(declaration); | ||
}; | ||
TypeContext.prototype.validateInterfaceImplementorsHaveTypenameField = function () { | ||
return (0, DiagnosticError_1.ok)(null); | ||
TypeContext.prototype.tsDeclarationForGqlDefinition = function (definition) { | ||
var name = definition.name; | ||
var declaration = this._idToDeclaration.get(name.tsIdentifier); | ||
if (!declaration) { | ||
throw new Error("Could not find declaration for ".concat(name.value)); | ||
} | ||
return declaration; | ||
}; | ||
TypeContext.prototype.getDestFilePath = function (sourceFile) { | ||
return (0, gratsRoot_1.getRelativeOutputPath)(this._options, sourceFile); | ||
TypeContext.prototype.getEntityName = function (name) { | ||
var _a; | ||
var entityName = (_a = this._unresolvedNodes.get(name.tsIdentifier)) !== null && _a !== void 0 ? _a : null; | ||
if (entityName == null && name.value === exports.UNRESOLVED_REFERENCE_NAME) { | ||
throw new Error("Expected unresolved reference to have a node."); | ||
} | ||
return entityName; | ||
}; | ||
@@ -119,0 +222,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; | ||
import { Result } from "./Result"; | ||
type FixableDiagnostic = ts.Diagnostic & { | ||
fix?: ts.CodeFixAction; | ||
}; | ||
type Err<E> = { | ||
kind: "ERROR"; | ||
err: E; | ||
export type FixableDiagnosticWithLocation = ts.DiagnosticWithLocation & { | ||
fix?: ts.CodeFixAction; | ||
}; | ||
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>; | ||
export type DiagnosticResult<T> = Result<T, FixableDiagnosticWithLocation>; | ||
export type DiagnosticsResult<T> = Result<T, FixableDiagnosticWithLocation[]>; | ||
export type DiagnosticsWithoutLocationResult<T> = Result<T, ts.Diagnostic[]>; | ||
export declare class ReportableDiagnostics { | ||
_host: ts.CompilerHost; | ||
_diagnostics: ts.Diagnostic[]; | ||
constructor(host: ts.CompilerHost, diagnostics: ts.Diagnostic[]); | ||
_host: ts.FormatDiagnosticsHost; | ||
_diagnostics: FixableDiagnostic[]; | ||
constructor(host: ts.FormatDiagnosticsHost, diagnostics: FixableDiagnostic[]); | ||
static fromDiagnostics(diagnostics: ts.Diagnostic[]): ReportableDiagnostics; | ||
formatDiagnosticsWithColorAndContext(): string; | ||
formatDiagnosticsWithContext(): string; | ||
} | ||
export declare const FAKE_ERROR_CODE = 349389149282; | ||
export declare const FAKE_ERROR_CODE = 1038; | ||
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[], fix?: ts.CodeFixAction): FixableDiagnosticWithLocation; | ||
/** | ||
* A generic version of the methods on ts.Node that we need | ||
* to create diagnostics. | ||
* | ||
* This interface allows us to create diagnostics from our | ||
* own classes. | ||
*/ | ||
export interface TsLocatableNode { | ||
getStart(): number; | ||
getEnd(): number; | ||
getSourceFile(): ts.SourceFile; | ||
} | ||
export declare function tsErr(node: TsLocatableNode, message: string, relatedInformation?: ts.DiagnosticRelatedInformation[], fix?: ts.CodeFixAction): FixableDiagnosticWithLocation; | ||
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,5 +61,5 @@ 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. | ||
exports.FAKE_ERROR_CODE = 349389149282; | ||
exports.FAKE_ERROR_CODE = 1038; | ||
function stripColor(str) { | ||
@@ -44,2 +73,3 @@ // eslint-disable-next-line no-control-regex | ||
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,63 @@ 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, fix) { | ||
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", | ||
fix: fix, | ||
}; | ||
} | ||
exports.rangeErr = rangeErr; | ||
function tsErr(node, message, relatedInformation, fix) { | ||
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", | ||
fix: fix, | ||
}; | ||
} | ||
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 +199,0 @@ return ts.createSourceFile(source.name, source.body, ts.ScriptTarget.Latest); |
{ | ||
"name": "grats", | ||
"version": "0.0.0-main-1acf2fbd", | ||
"version": "0.0.0-main-1eed5234", | ||
"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 | ||
**The simplest way to build a GraphQL server in TypeScript** | ||
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,35 @@ 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 | ||
Read the [blog post](https://jordaneldredge.com/blog/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 +56,13 @@ | ||
* [@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) | ||
## License | ||
Grats is [MIT licensed](./LICENSE). |
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
343388
3
78
7051
0
66
2
240
13
6
97
13
1
6
+ Addedtypescript@5.7.3(transitive)
- Removed@graphql-tools/utils@^9.2.1
- Removed@graphql-tools/utils@9.2.1(transitive)
- Removed@graphql-typed-document-node/core@3.2.0(transitive)
- Removedtslib@2.8.1(transitive)
- Removedtypescript@4.9.5(transitive)
Updatedtypescript@^5.0.2