Comparing version 0.0.30 to 0.0.31
{ | ||
"name": "grats", | ||
"version": "0.0.29", | ||
"version": "0.0.30", | ||
"main": "dist/src/index.js", | ||
@@ -5,0 +5,0 @@ "bin": "dist/src/cli.js", |
@@ -14,2 +14,3 @@ import { GraphQLField } from "graphql"; | ||
_helpers: Set<string>; | ||
_derivedContextNames: Map<string, string>; | ||
constructor(ts: TSAstBuilder, _resolvers: Metadata); | ||
@@ -19,4 +20,5 @@ resolveMethod(fieldName: string, methodName: string, parentTypeName: string): ts.MethodDeclaration | null; | ||
resolverParam(arg: ResolverArgument): ts.Expression; | ||
getDerivedContextName(path: string, exportName: string | null): string; | ||
maybeApplySemanticNullRuntimeCheck(field: GraphQLField<unknown, unknown>, method_: ts.MethodDeclaration | null, methodName: string): ts.MethodDeclaration | null; | ||
defaultResolverMethod(methodName: string): ts.MethodDeclaration; | ||
} |
@@ -30,2 +30,3 @@ "use strict"; | ||
this._helpers = new Set(); | ||
this._derivedContextNames = new Map(); | ||
} | ||
@@ -111,2 +112,3 @@ ResolverCodegen.prototype.resolveMethod = function (fieldName, methodName, parentTypeName) { | ||
ResolverCodegen.prototype.resolverParam = function (arg) { | ||
var _this = this; | ||
switch (arg.kind) { | ||
@@ -123,2 +125,7 @@ case "argumentsObject": | ||
return F.createPropertyAccessExpression(F.createIdentifier("args"), F.createIdentifier(arg.name)); | ||
case "derivedContext": { | ||
var localName = this.getDerivedContextName(arg.path, arg.exportName); | ||
this.ts.importUserConstruct(arg.path, arg.exportName, localName); | ||
return F.createCallExpression(F.createIdentifier(localName), undefined, arg.args.map(function (arg) { return _this.resolverParam(arg); })); | ||
} | ||
default: | ||
@@ -129,2 +136,15 @@ // @ts-expect-error | ||
}; | ||
// Derived contexts are not anchored to anything that we know to be | ||
// globally unique, like GraphQL type names, so must ensure this name is | ||
// unique within our module. However, we want to avoid generating a new | ||
// name for the same derived context more than once. | ||
ResolverCodegen.prototype.getDerivedContextName = function (path, exportName) { | ||
var key = "".concat(path, ":").concat(exportName !== null && exportName !== void 0 ? exportName : ""); | ||
var name = this._derivedContextNames.get(key); | ||
if (name == null) { | ||
name = this.ts.getUniqueName(exportName !== null && exportName !== void 0 ? exportName : "deriveContext"); | ||
this._derivedContextNames.set(key, name); | ||
} | ||
return name; | ||
}; | ||
// If a field is smantically non-null, we need to wrap the resolver in a | ||
@@ -182,7 +202,15 @@ // runtime check to ensure that the resolver does not return null. | ||
var used = resolverParams.some(function (param) { | ||
return ((param.kind === "named" && name === "args") || | ||
(param.kind === "argumentsObject" && name === "args") || | ||
(param.kind === "context" && name === "context") || | ||
(param.kind === "information" && name === "info") || | ||
(param.kind === "source" && name === "source")); | ||
switch (name) { | ||
case "source": | ||
return param.kind === "source"; | ||
case "args": | ||
return param.kind === "named" || param.kind === "argumentsObject"; | ||
case "context": | ||
// Recursively check if this arg uses context. | ||
return usesContext(param); | ||
case "info": | ||
return param.kind === "information"; | ||
default: | ||
throw new Error("Unexpected resolver kind ".concat(name)); | ||
} | ||
}) || | ||
@@ -203,2 +231,15 @@ (name === "source" && includeSource); | ||
} | ||
// A param only uses context if it is the root context value, or if it is a | ||
// derived context value that directly or transitively uses the root context | ||
// value. So, we need a recursive function to check if a param uses context. | ||
function usesContext(param) { | ||
switch (param.kind) { | ||
case "context": | ||
return true; | ||
case "derivedContext": | ||
return param.args.some(usesContext); | ||
default: | ||
return false; | ||
} | ||
} | ||
function fieldDirective(field, name) { | ||
@@ -205,0 +246,0 @@ var _a, _b, _c; |
@@ -8,2 +8,3 @@ import * as ts from "typescript"; | ||
private importModuleSpecifierEnding; | ||
_globalNames: Map<string, number>; | ||
_imports: ts.Statement[]; | ||
@@ -32,2 +33,3 @@ imports: Map<string, { | ||
print(): string; | ||
getUniqueName(name: string): string; | ||
} |
@@ -51,2 +51,3 @@ "use strict"; | ||
this.importModuleSpecifierEnding = importModuleSpecifierEnding; | ||
this._globalNames = new Map(); | ||
this._imports = []; | ||
@@ -171,2 +172,15 @@ this.imports = new Map(); | ||
}; | ||
// Given a desired name in the module scope, return a name that is unique. If | ||
// the name is already taken, a suffix will be added to the name to make it | ||
// unique. | ||
// | ||
// NOTE: This is not truly unique, as it only checks the names that have been | ||
// generated through this method. In the future we could add more robust | ||
// scope/name tracking. | ||
TSAstBuilder.prototype.getUniqueName = function (name) { | ||
var _a; | ||
var count = (_a = this._globalNames.get(name)) !== null && _a !== void 0 ? _a : 0; | ||
this._globalNames.set(name, count + 1); | ||
return count === 0 ? name : "".concat(name, "_").concat(count); | ||
}; | ||
return TSAstBuilder; | ||
@@ -173,0 +187,0 @@ }()); |
@@ -138,1 +138,5 @@ export declare const ISSUE_URL = "https://github.com/captbaritone/grats/issues"; | ||
export declare function tsConfigNotFound(cwd: string): string; | ||
export declare function cyclicDerivedContext(): string; | ||
export declare function invalidDerivedContextArgType(): string; | ||
export declare function missingReturnTypeForDerivedResolver(): string; | ||
export declare function derivedResolverInvalidReturnType(): string; |
@@ -130,2 +130,6 @@ "use strict"; | ||
exports.tsConfigNotFound = tsConfigNotFound; | ||
exports.cyclicDerivedContext = cyclicDerivedContext; | ||
exports.invalidDerivedContextArgType = invalidDerivedContextArgType; | ||
exports.missingReturnTypeForDerivedResolver = missingReturnTypeForDerivedResolver; | ||
exports.derivedResolverInvalidReturnType = derivedResolverInvalidReturnType; | ||
var Extractor_1 = require("./Extractor"); | ||
@@ -563,1 +567,13 @@ exports.ISSUE_URL = "https://github.com/captbaritone/grats/issues"; | ||
} | ||
function cyclicDerivedContext() { | ||
return "Cyclic dependency detected in derived context. This derived context value depends upon itself."; | ||
} | ||
function invalidDerivedContextArgType() { | ||
return "Invalid type for derived context function argument. Derived context functions may only accept other `@gqlContext` types as arguments."; | ||
} | ||
function missingReturnTypeForDerivedResolver() { | ||
return 'Expected derived resolver to have an explicit return type. This is needed to allow Grats to "see" which type to treat as a derived context type.'; | ||
} | ||
function derivedResolverInvalidReturnType() { | ||
return "Expected derived resolver function's return type to be a type reference. Grats uses this type reference to determine which type to treat as a derived context type."; | ||
} |
import { NameNode, DefinitionNode } from "graphql"; | ||
import { DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import * as ts from "typescript"; | ||
import { NameDefinition } from "./TypeContext"; | ||
import { DeclarationDefinition, NameDefinition } from "./TypeContext"; | ||
export declare const LIBRARY_IMPORT_NAME = "grats"; | ||
@@ -29,2 +29,3 @@ export declare const LIBRARY_NAME = "Grats"; | ||
readonly nameDefinitions: Map<ts.DeclarationStatement, NameDefinition>; | ||
readonly implicitNameDefinitions: Map<DeclarationDefinition, ts.TypeReferenceNode>; | ||
readonly typesWithTypename: Set<string>; | ||
@@ -31,0 +32,0 @@ readonly interfaceDeclarations: Array<ts.InterfaceDeclaration>; |
@@ -86,3 +86,7 @@ "use strict"; | ||
var checker = program.getTypeChecker(); | ||
var ctx = TypeContext_1.TypeContext.fromSnapshot(checker, snapshot); | ||
var ctxResult = TypeContext_1.TypeContext.fromSnapshot(checker, snapshot); | ||
if (ctxResult.kind === "ERROR") { | ||
return ctxResult; | ||
} | ||
var ctx = ctxResult.value; | ||
// Collect validation errors | ||
@@ -155,6 +159,7 @@ var validationResult = (0, Result_1.concatResults)((0, validateMergedInterfaces_1.validateMergedInterfaces)(checker, snapshot.interfaceDeclarations), (0, validateDuplicateContextOrInfo_1.validateDuplicateContextOrInfo)(ctx)); | ||
function combineSnapshots(snapshots) { | ||
var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f; | ||
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(), | ||
implicitNameDefinitions: new Map(), | ||
unresolvedNames: new Map(), | ||
@@ -168,4 +173,4 @@ typesWithTypename: new Set(), | ||
try { | ||
for (var _g = (e_2 = void 0, __values(snapshot.definitions)), _h = _g.next(); !_h.done; _h = _g.next()) { | ||
var definition = _h.value; | ||
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); | ||
@@ -177,3 +182,3 @@ } | ||
try { | ||
if (_h && !_h.done && (_b = _g.return)) _b.call(_g); | ||
if (_j && !_j.done && (_b = _h.return)) _b.call(_h); | ||
} | ||
@@ -183,4 +188,4 @@ finally { if (e_2) throw e_2.error; } | ||
try { | ||
for (var _j = (e_3 = void 0, __values(snapshot.nameDefinitions)), _k = _j.next(); !_k.done; _k = _j.next()) { | ||
var _l = __read(_k.value, 2), node = _l[0], definition = _l[1]; | ||
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); | ||
@@ -192,3 +197,3 @@ } | ||
try { | ||
if (_k && !_k.done && (_c = _j.return)) _c.call(_j); | ||
if (_l && !_l.done && (_c = _k.return)) _c.call(_k); | ||
} | ||
@@ -198,4 +203,4 @@ finally { if (e_3) throw e_3.error; } | ||
try { | ||
for (var _m = (e_4 = void 0, __values(snapshot.unresolvedNames)), _o = _m.next(); !_o.done; _o = _m.next()) { | ||
var _p = __read(_o.value, 2), node = _p[0], typeName = _p[1]; | ||
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); | ||
@@ -207,3 +212,3 @@ } | ||
try { | ||
if (_o && !_o.done && (_d = _m.return)) _d.call(_m); | ||
if (_p && !_p.done && (_d = _o.return)) _d.call(_o); | ||
} | ||
@@ -213,5 +218,5 @@ finally { if (e_4) throw e_4.error; } | ||
try { | ||
for (var _q = (e_5 = void 0, __values(snapshot.typesWithTypename)), _r = _q.next(); !_r.done; _r = _q.next()) { | ||
var typeName = _r.value; | ||
result.typesWithTypename.add(typeName); | ||
for (var _r = (e_5 = void 0, __values(snapshot.implicitNameDefinitions)), _s = _r.next(); !_s.done; _s = _r.next()) { | ||
var _t = __read(_s.value, 2), node = _t[0], definition = _t[1]; | ||
result.implicitNameDefinitions.set(node, definition); | ||
} | ||
@@ -222,3 +227,3 @@ } | ||
try { | ||
if (_r && !_r.done && (_e = _q.return)) _e.call(_q); | ||
if (_s && !_s.done && (_e = _r.return)) _e.call(_r); | ||
} | ||
@@ -228,5 +233,5 @@ finally { if (e_5) throw e_5.error; } | ||
try { | ||
for (var _s = (e_6 = void 0, __values(snapshot.interfaceDeclarations)), _t = _s.next(); !_t.done; _t = _s.next()) { | ||
var interfaceDeclaration = _t.value; | ||
result.interfaceDeclarations.push(interfaceDeclaration); | ||
for (var _u = (e_6 = void 0, __values(snapshot.typesWithTypename)), _v = _u.next(); !_v.done; _v = _u.next()) { | ||
var typeName = _v.value; | ||
result.typesWithTypename.add(typeName); | ||
} | ||
@@ -237,6 +242,19 @@ } | ||
try { | ||
if (_t && !_t.done && (_f = _s.return)) _f.call(_s); | ||
if (_v && !_v.done && (_f = _u.return)) _f.call(_u); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
try { | ||
for (var _w = (e_7 = void 0, __values(snapshot.interfaceDeclarations)), _x = _w.next(); !_x.done; _x = _w.next()) { | ||
var interfaceDeclaration = _x.value; | ||
result.interfaceDeclarations.push(interfaceDeclaration); | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (_x && !_x.done && (_g = _w.return)) _g.call(_w); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
} | ||
@@ -243,0 +261,0 @@ } |
@@ -68,4 +68,5 @@ /** | ||
}; | ||
export type ContextArgs = ContextArgument | DerivedContextArgument; | ||
/** An argument expected by a resolver function or method */ | ||
export type ResolverArgument = SourceArgument | ArgumentsObjectArgument | ContextArgument | InformationArgument | NamedArgument; | ||
export type ResolverArgument = SourceArgument | ArgumentsObjectArgument | ContextArgument | DerivedContextArgument | InformationArgument | NamedArgument; | ||
/** The source or parent object */ | ||
@@ -83,2 +84,9 @@ export type SourceArgument = { | ||
}; | ||
/** A context value which is expressed as a function of the global context */ | ||
export type DerivedContextArgument = { | ||
kind: "derivedContext"; | ||
path: string; | ||
exportName: string | null; | ||
args: Array<ContextArgs>; | ||
}; | ||
/** The GraphQL info object */ | ||
@@ -85,0 +93,0 @@ export type InformationArgument = { |
@@ -45,2 +45,9 @@ import * as ts from "typescript"; | ||
}; | ||
export type DerivedContextResolverArgument = { | ||
kind: "derivedContext"; | ||
path: string; | ||
exportName: string | null; | ||
args: Array<DerivedContextResolverArgument | ContextResolverArgument>; | ||
node: ts.Node; | ||
}; | ||
export type InformationResolverArgument = { | ||
@@ -61,3 +68,3 @@ kind: "information"; | ||
}; | ||
export type ResolverArgument = SourceResolverArgument | ArgumentsObjectResolverArgument | ContextResolverArgument | InformationResolverArgument | NamedResolverArgument | UnresolvedResolverArgument; | ||
export type ResolverArgument = SourceResolverArgument | ArgumentsObjectResolverArgument | ContextResolverArgument | DerivedContextResolverArgument | InformationResolverArgument | NamedResolverArgument | UnresolvedResolverArgument; | ||
/** | ||
@@ -64,0 +71,0 @@ * At extraction time we don't know if a resolver arg is context, info, or a |
@@ -99,21 +99,33 @@ "use strict"; | ||
} | ||
return args.map(function (arg) { | ||
switch (arg.kind) { | ||
case "argumentsObject": | ||
return { kind: "argumentsObject" }; | ||
case "named": | ||
return { kind: "named", name: arg.name }; | ||
case "source": | ||
return { kind: "source" }; | ||
case "information": | ||
return { kind: "information" }; | ||
case "context": | ||
return { kind: "context" }; | ||
case "unresolved": | ||
throw new Error("Unresolved argument in resolver"); | ||
default: | ||
// @ts-expect-error | ||
throw new Error("Unknown argument kind: ".concat(arg.kind)); | ||
} | ||
}); | ||
return args.map(transformArg); | ||
} | ||
function transformArg(arg) { | ||
switch (arg.kind) { | ||
case "argumentsObject": | ||
return { kind: "argumentsObject" }; | ||
case "named": | ||
return { kind: "named", name: arg.name }; | ||
case "source": | ||
return { kind: "source" }; | ||
case "information": | ||
return { kind: "information" }; | ||
case "context": | ||
return { kind: "context" }; | ||
case "derivedContext": | ||
return { | ||
kind: "derivedContext", | ||
path: arg.path, | ||
exportName: arg.exportName, | ||
args: arg.args.map(function (arg) { | ||
var newArg = transformArg(arg); | ||
(0, helpers_1.invariant)(newArg.kind === "derivedContext" || newArg.kind === "context", "Previous validation passes ensure we only have valid derived context args here"); | ||
return newArg; | ||
}), | ||
}; | ||
case "unresolved": | ||
throw new Error("Unresolved argument in resolver"); | ||
default: | ||
// @ts-expect-error | ||
throw new Error("Unknown argument kind: ".concat(arg.kind)); | ||
} | ||
} |
@@ -13,2 +13,13 @@ "use strict"; | ||
}; | ||
var __values = (this && this.__values) || function(o) { | ||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
if (m) return m.call(o); | ||
if (o && typeof o.length === "number") return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
@@ -39,13 +50,2 @@ var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
}; | ||
var __values = (this && this.__values) || function(o) { | ||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
if (m) return m.call(o); | ||
if (o && typeof o.length === "number") return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -84,4 +84,3 @@ exports.resolveResolverParams = resolveResolverParams; | ||
ResolverParamsResolver.prototype.transformField = function (field) { | ||
var e_1, _a; | ||
var _this = this; | ||
var e_1, _a, e_2, _b; | ||
var resolver = (0, helpers_1.nullThrows)(field.resolver); | ||
@@ -92,5 +91,19 @@ if (resolver.kind === "property" || resolver.arguments == null) { | ||
// Resolve all the params individually | ||
var resolverParams = resolver.arguments.map(function (param) { | ||
return _this.transformParam(param); | ||
}); | ||
var resolverParams = []; | ||
try { | ||
for (var _c = __values(resolver.arguments), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var param = _d.value; | ||
var transformed = this.transformParam(param); | ||
if (transformed == null) | ||
return field; | ||
resolverParams.push(transformed); | ||
} | ||
} | ||
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; } | ||
} | ||
// Now we check to see if the params are a valid combination... | ||
@@ -113,8 +126,8 @@ var args = resolverParams.find(function (param) { return param.kind === "argumentsObject"; }); | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (positionalArgs_1_1 && !positionalArgs_1_1.done && (_a = positionalArgs_1.return)) _a.call(positionalArgs_1); | ||
if (positionalArgs_1_1 && !positionalArgs_1_1.done && (_b = positionalArgs_1.return)) _b.call(positionalArgs_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
@@ -124,3 +137,3 @@ var newResolver = __assign(__assign({}, resolver), { arguments: resolverParams }); | ||
}; | ||
ResolverParamsResolver.prototype.transformParam = function (param) { | ||
ResolverParamsResolver.prototype.transformParam = function (param, seenDerivedContextValues) { | ||
var _a, _b; | ||
@@ -132,2 +145,3 @@ switch (param.kind) { | ||
case "context": | ||
case "derivedContext": | ||
case "source": | ||
@@ -142,5 +156,11 @@ return param; | ||
this.errors.push(resolved.err); | ||
return param; | ||
return null; | ||
} | ||
switch (resolved.value.kind) { | ||
case "DERIVED_CONTEXT": { | ||
var derivedContextArg = this.resolveDerivedContext(param.node, resolved.value, seenDerivedContextValues); | ||
if (derivedContextArg === null) | ||
return null; | ||
return derivedContextArg; | ||
} | ||
case "CONTEXT": | ||
@@ -166,2 +186,50 @@ return { kind: "context", node: param.node }; | ||
}; | ||
ResolverParamsResolver.prototype.resolveDerivedContext = function (node, // Argument | ||
definition, seenDerivedContextValues) { | ||
var e_3, _a; | ||
var path = definition.path, exportName = definition.exportName, args = definition.args; | ||
var key = "".concat(path, ":").concat(exportName); | ||
if (seenDerivedContextValues == null) { | ||
// We're resolving the arg of a resolver. Initiate the map. | ||
seenDerivedContextValues = new Map(); | ||
} | ||
else { | ||
if (seenDerivedContextValues.has(key)) { | ||
this.errors.push(this.cycleError(node, definition, seenDerivedContextValues)); | ||
return null; | ||
} | ||
} | ||
seenDerivedContextValues.set(key, node); | ||
var newArgs = []; | ||
try { | ||
for (var args_1 = __values(args), args_1_1 = args_1.next(); !args_1_1.done; args_1_1 = args_1.next()) { | ||
var arg = args_1_1.value; | ||
var resolvedArg = this.transformParam(arg, seenDerivedContextValues); | ||
if (resolvedArg === null) { | ||
continue; | ||
} | ||
switch (resolvedArg.kind) { | ||
case "context": | ||
newArgs.push(resolvedArg); | ||
break; | ||
case "derivedContext": | ||
// Here we know that the argument `node` maps to a derived context | ||
// `definition` which itself depends another derived resolver `resolvedArg`. | ||
// `definition`. | ||
newArgs.push(resolvedArg); | ||
break; | ||
default: | ||
this.errors.push((0, DiagnosticError_1.tsErr)(resolvedArg.node, E.invalidDerivedContextArgType())); | ||
} | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (args_1_1 && !args_1_1.done && (_a = args_1.return)) _a.call(args_1); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
return { kind: "derivedContext", node: node, path: path, exportName: exportName, args: newArgs }; | ||
}; | ||
ResolverParamsResolver.prototype.resolveToPositionalArg = function (unresolved) { | ||
@@ -179,3 +247,36 @@ if (unresolved.inputDefinition.name.kind === "ERROR") { | ||
}; | ||
/** | ||
* Some slightly complicated logic to construct nice errors in the case of | ||
* cycles where derived resolvers ultimately depend upon themselves. | ||
* | ||
* The `@gqlContext` tag is the main location. If it's a direct cycle, we | ||
* report one related location, of the argument which points back to itself. | ||
* | ||
* If there are multiple nodes in the cycle, we report a related location for | ||
* each node in the cycle, with a message that depends on the position of the | ||
* node in the cycle. | ||
*/ | ||
ResolverParamsResolver.prototype.cycleError = function (node, definition, seenDerivedContextValues) { | ||
// We trim off the first node because that points to a resolver argument. | ||
var nodes = Array.from(seenDerivedContextValues.values()).slice(1); | ||
// The cycle completes with this node, so we include it in the list. | ||
nodes.push(node); | ||
var related = nodes.map(function (def, i) { | ||
if (nodes.length === 1) { | ||
return (0, DiagnosticError_1.tsRelated)(def, "This derived context depends on itself"); | ||
} | ||
var isFirst = i === 0; | ||
var isLast = i === nodes.length - 1; | ||
(0, helpers_1.invariant)(!(isFirst && isLast), "Should not be both first and last"); | ||
if (isFirst) { | ||
return (0, DiagnosticError_1.tsRelated)(def, "This derived context depends on"); | ||
} | ||
else if (!isLast) { | ||
return (0, DiagnosticError_1.tsRelated)(def, "Which in turn depends on"); | ||
} | ||
return (0, DiagnosticError_1.tsRelated)(def, "Which ultimately creates a cycle back to the initial derived context"); | ||
}); | ||
return (0, DiagnosticError_1.gqlErr)(definition.name, E.cyclicDerivedContext(), related); | ||
}; | ||
return ResolverParamsResolver; | ||
}()); |
import { InputObjectTypeDefinitionNode, InterfaceTypeDefinitionNode, NameNode, ObjectTypeDefinitionNode, UnionTypeDefinitionNode } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { DiagnosticResult } from "./utils/DiagnosticError"; | ||
import { DiagnosticResult, DiagnosticsResult } from "./utils/DiagnosticError"; | ||
import { ExtractionSnapshot } from "./Extractor"; | ||
import { ResolverArgument } from "./resolverSignature"; | ||
export declare const UNRESOLVED_REFERENCE_NAME = "__UNRESOLVED_REFERENCE__"; | ||
export type DerivedResolverDefinition = { | ||
name: NameNode; | ||
path: string; | ||
exportName: string | null; | ||
args: ResolverArgument[]; | ||
kind: "DERIVED_CONTEXT"; | ||
}; | ||
export type NameDefinition = { | ||
@@ -10,2 +18,3 @@ name: NameNode; | ||
}; | ||
export type DeclarationDefinition = NameDefinition | DerivedResolverDefinition; | ||
type TsIdentifier = number; | ||
@@ -26,10 +35,10 @@ /** | ||
checker: ts.TypeChecker; | ||
_declarationToName: Map<ts.Declaration, NameDefinition>; | ||
_declarationToDefinition: Map<ts.Declaration, DeclarationDefinition>; | ||
_unresolvedNodes: Map<TsIdentifier, ts.EntityName>; | ||
_idToDeclaration: Map<TsIdentifier, ts.Declaration>; | ||
static fromSnapshot(checker: ts.TypeChecker, snapshot: ExtractionSnapshot): TypeContext; | ||
static fromSnapshot(checker: ts.TypeChecker, snapshot: ExtractionSnapshot): DiagnosticsResult<TypeContext>; | ||
constructor(checker: ts.TypeChecker); | ||
private _recordTypeName; | ||
private _recordDeclaration; | ||
private _markUnresolvedType; | ||
allNameDefinitions(): Iterable<NameDefinition>; | ||
allDefinitions(): Iterable<DeclarationDefinition>; | ||
findSymbolDeclaration(startSymbol: ts.Symbol): ts.Declaration | null; | ||
@@ -39,3 +48,3 @@ private resolveSymbol; | ||
unresolvedNameIsGraphQL(unresolved: NameNode): boolean; | ||
gqlNameDefinitionForGqlName(nameNode: NameNode): DiagnosticResult<NameDefinition>; | ||
gqlNameDefinitionForGqlName(nameNode: NameNode): DiagnosticResult<DeclarationDefinition>; | ||
gqlNameForTsName(node: ts.EntityName): DiagnosticResult<string>; | ||
@@ -42,0 +51,0 @@ private maybeTsDeclarationForTsName; |
@@ -61,3 +61,3 @@ "use strict"; | ||
function TypeContext(checker) { | ||
this._declarationToName = new Map(); | ||
this._declarationToDefinition = new Map(); | ||
this._unresolvedNodes = new Map(); | ||
@@ -68,7 +68,8 @@ this._idToDeclaration = new Map(); | ||
TypeContext.fromSnapshot = function (checker, snapshot) { | ||
var e_1, _a, e_2, _b; | ||
var e_1, _a, e_2, _b, e_3, _c; | ||
var errors = []; | ||
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]; | ||
for (var _d = __values(snapshot.unresolvedNames), _e = _d.next(); !_e.done; _e = _d.next()) { | ||
var _f = __read(_e.value, 2), node = _f[0], typeName = _f[1]; | ||
self._markUnresolvedType(node, typeName); | ||
@@ -80,3 +81,3 @@ } | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
if (_e && !_e.done && (_a = _d.return)) _a.call(_d); | ||
} | ||
@@ -86,5 +87,5 @@ 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); | ||
for (var _g = __values(snapshot.nameDefinitions), _h = _g.next(); !_h.done; _h = _g.next()) { | ||
var _j = __read(_h.value, 2), node = _j[0], definition = _j[1]; | ||
self._recordDeclaration(node, definition); | ||
} | ||
@@ -95,13 +96,42 @@ } | ||
try { | ||
if (_g && !_g.done && (_b = _f.return)) _b.call(_f); | ||
if (_h && !_h.done && (_b = _g.return)) _b.call(_g); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
return self; | ||
try { | ||
for (var _k = __values(snapshot.implicitNameDefinitions), _l = _k.next(); !_l.done; _l = _k.next()) { | ||
var _m = __read(_l.value, 2), definition = _m[0], reference = _m[1]; | ||
var declaration = self.maybeTsDeclarationForTsName(reference.typeName); | ||
if (declaration == null) { | ||
errors.push((0, DiagnosticError_1.tsErr)(reference.typeName, E.unresolvedTypeReference())); | ||
continue; | ||
} | ||
var existing = self._declarationToDefinition.get(declaration); | ||
if (existing != null) { | ||
errors.push((0, DiagnosticError_1.tsErr)(declaration, "Multiple derived contexts defined for given type", [ | ||
(0, DiagnosticError_1.gqlRelated)(definition.name, "One was defined here"), | ||
(0, DiagnosticError_1.gqlRelated)(existing.name, "Another here"), | ||
])); | ||
continue; | ||
} | ||
self._recordDeclaration(declaration, definition); | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (_l && !_l.done && (_c = _k.return)) _c.call(_k); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
if (errors.length > 0) { | ||
return (0, Result_1.err)(errors); | ||
} | ||
return (0, Result_1.ok)(self); | ||
}; | ||
// 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._recordDeclaration = function (node, definition) { | ||
this._idToDeclaration.set(definition.name.tsIdentifier, node); | ||
this._declarationToDefinition.set(node, definition); | ||
}; | ||
@@ -112,4 +142,4 @@ // Record that a type references `node` | ||
}; | ||
TypeContext.prototype.allNameDefinitions = function () { | ||
return this._declarationToName.values(); | ||
TypeContext.prototype.allDefinitions = function () { | ||
return this._declarationToDefinition.values(); | ||
}; | ||
@@ -151,3 +181,3 @@ TypeContext.prototype.findSymbolDeclaration = function (startSymbol) { | ||
} | ||
var nameDefinition = this._declarationToName.get(declarationResult.value); | ||
var nameDefinition = this._declarationToDefinition.get(declarationResult.value); | ||
if (nameDefinition == null) { | ||
@@ -168,3 +198,3 @@ return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)(unresolved, E.unresolvedTypeReference())); | ||
return false; | ||
return this._declarationToName.has(declaration); | ||
return this._declarationToDefinition.has(declaration); | ||
}; | ||
@@ -180,3 +210,3 @@ TypeContext.prototype.gqlNameDefinitionForGqlName = function (nameNode) { | ||
} | ||
var definition = this._declarationToName.get(declaration); | ||
var definition = this._declarationToDefinition.get(declaration); | ||
if (definition == null) { | ||
@@ -198,3 +228,3 @@ return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)(nameNode, E.unresolvedTypeReference())); | ||
} | ||
var nameDefinition = this._declarationToName.get(declarationResult.value); | ||
var nameDefinition = this._declarationToDefinition.get(declarationResult.value); | ||
if (nameDefinition == null) { | ||
@@ -201,0 +231,0 @@ return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, E.unresolvedTypeReference())); |
@@ -24,3 +24,3 @@ "use strict"; | ||
try { | ||
for (var _b = __values(ctx.allNameDefinitions()), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
for (var _b = __values(ctx.allDefinitions()), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var namedDefinition = _c.value; | ||
@@ -27,0 +27,0 @@ switch (namedDefinition.kind) { |
{ | ||
"name": "grats", | ||
"version": "0.0.30", | ||
"version": "0.0.31", | ||
"main": "dist/src/index.js", | ||
@@ -5,0 +5,0 @@ "bin": "dist/src/cli.js", |
Sorry, the diff of this file is too big to display
421989
8810