Comparing version 0.0.27 to 0.0.28
{ | ||
"name": "grats", | ||
"version": "0.0.26", | ||
"version": "0.0.27", | ||
"main": "dist/src/index.js", | ||
@@ -5,0 +5,0 @@ "bin": "dist/src/cli.js", |
@@ -194,4 +194,13 @@ "use strict"; | ||
var resolverParams = (_a = fieldAst.resolverParams) === null || _a === void 0 ? void 0 : _a.map(function (param) { | ||
(0, helpers_1.invariant)(param.kind === "named", "Expected resolver param to have been resolved."); | ||
return param.name; | ||
switch (param.kind) { | ||
case "named": | ||
case "positionalArg": | ||
return param; | ||
case "unresolved": | ||
throw new Error("Expected resolver param to have been resolved."); | ||
default: { | ||
var _exhaustive = param; | ||
throw new Error("Unexpected param kind"); | ||
} | ||
} | ||
}); | ||
@@ -215,3 +224,3 @@ if (metadata.tsModulePath != null) { | ||
F.createReturnStatement(F.createCallExpression(resolverAccess, undefined, usedResolverParams.map(function (name) { | ||
return F.createIdentifier(name); | ||
return _this.resolverParam(name); | ||
}))), | ||
@@ -226,6 +235,8 @@ ]); | ||
valueExpression = F.createCallExpression(prop, undefined, resolverParams.map(function (name) { | ||
return F.createIdentifier(name); | ||
return _this.resolverParam(name); | ||
})); | ||
} | ||
var usedWrapperParams = ["source"]; | ||
var usedWrapperParams = [ | ||
{ kind: "named", name: "source" }, | ||
]; | ||
if (resolverParams != null) { | ||
@@ -242,2 +253,16 @@ // Push with ... is safe because resolverParams is known to be | ||
}; | ||
// Either `args`, `context`, `info`, or a positional argument like | ||
// `args.someArg`. | ||
Codegen.prototype.resolverParam = function (param) { | ||
switch (param.kind) { | ||
case "named": | ||
return F.createIdentifier(param.name); | ||
case "positionalArg": | ||
return F.createPropertyAccessExpression(F.createIdentifier("args"), F.createIdentifier(param.inputDefinition.name.value)); | ||
default: { | ||
var _exhaustive = param; | ||
throw new Error("Unexpected param kind"); | ||
} | ||
} | ||
}; | ||
// If a field is smantically non-null, we need to wrap the resolver in a | ||
@@ -680,5 +705,8 @@ // runtime check to ensure that the resolver does not return null. | ||
var adding = false; | ||
for (var i = RESOLVER_ARGS.length - 1; i >= 0; i--) { | ||
var _loop_1 = function (i) { | ||
var name = RESOLVER_ARGS[i]; | ||
var used = resolverParams.includes(name); | ||
var used = resolverParams.some(function (param) { | ||
return ((param.kind === "named" && param.name === name) || | ||
(param.kind === "positionalArg" && name) === "args"); | ||
}); | ||
if (used) { | ||
@@ -688,5 +716,8 @@ adding = true; | ||
if (!adding) { | ||
continue; | ||
return "continue"; | ||
} | ||
wrapperArgs.unshift(used ? name : "_".concat(name)); | ||
}; | ||
for (var i = RESOLVER_ARGS.length - 1; i >= 0; i--) { | ||
_loop_1(i); | ||
} | ||
@@ -696,3 +727,5 @@ return wrapperArgs; | ||
function paramsAreInDefaultOrder(params) { | ||
return params.every(function (param, i) { return param === RESOLVER_ARGS[i + 1]; }); | ||
return params.every(function (param, i) { | ||
return param.kind === "named" && param.name === RESOLVER_ARGS[i + 1]; | ||
}); | ||
} | ||
@@ -699,0 +732,0 @@ function fieldDirective(field, name) { |
@@ -127,1 +127,8 @@ export declare const ISSUE_URL = "https://github.com/captbaritone/grats/issues"; | ||
export declare function invalidResolverParamType(): string; | ||
export declare function exportedArrowFunctionNotConst(): string; | ||
export declare function exportedFieldVariableMultipleDeclarations(n: number): string; | ||
export declare function fieldVariableNotTopLevelExported(): string; | ||
export declare function fieldVariableIsNotArrowFunction(): string; | ||
export declare function positionalResolverArgDoesNotHaveName(): string; | ||
export declare function positionalArgAndArgsObject(): string; | ||
export declare function contextOrInfoUsedInGraphQLPosition(kind: "CONTEXT" | "INFO"): string; |
@@ -119,2 +119,9 @@ "use strict"; | ||
exports.invalidResolverParamType = invalidResolverParamType; | ||
exports.exportedArrowFunctionNotConst = exportedArrowFunctionNotConst; | ||
exports.exportedFieldVariableMultipleDeclarations = exportedFieldVariableMultipleDeclarations; | ||
exports.fieldVariableNotTopLevelExported = fieldVariableNotTopLevelExported; | ||
exports.fieldVariableIsNotArrowFunction = fieldVariableIsNotArrowFunction; | ||
exports.positionalResolverArgDoesNotHaveName = positionalResolverArgDoesNotHaveName; | ||
exports.positionalArgAndArgsObject = positionalArgAndArgsObject; | ||
exports.contextOrInfoUsedInGraphQLPosition = contextOrInfoUsedInGraphQLPosition; | ||
var Extractor_1 = require("./Extractor"); | ||
@@ -331,3 +338,3 @@ exports.ISSUE_URL = "https://github.com/captbaritone/grats/issues"; | ||
function expectedNameIdentifier() { | ||
return "Expected an name identifier. Grats expected to find a name here which it could use to derive the GraphQL name."; | ||
return "Expected a name identifier. Grats expected to find a name here which it could use to derive the GraphQL name."; | ||
} | ||
@@ -519,1 +526,23 @@ // TODO: Add code action | ||
} | ||
function exportedArrowFunctionNotConst() { | ||
return "Expected `@".concat(Extractor_1.FIELD_TAG, "` arrow function to be declared as `const`."); | ||
} | ||
function exportedFieldVariableMultipleDeclarations(n) { | ||
return "Expected only one declaration when defining a `@".concat(Extractor_1.FIELD_TAG, "`, found ").concat(n, "."); | ||
} | ||
function fieldVariableNotTopLevelExported() { | ||
return "Expected `@".concat(Extractor_1.FIELD_TAG, "` to be an exported top-level declaration. Grats needs to import resolver functions into it's generated schema module, so the resolver function must be exported from the module."); | ||
} | ||
function fieldVariableIsNotArrowFunction() { | ||
return "Expected `@".concat(Extractor_1.FIELD_TAG, "` on variable declaration to be attached to an arrow function."); | ||
} | ||
function positionalResolverArgDoesNotHaveName() { | ||
return "Expected resolver argument to have a name. Grats needs to be able to see the name of the argument in order to derive a GraphQL argument name."; | ||
} | ||
function positionalArgAndArgsObject() { | ||
return "Unexpected arguments object in resolver that is also using positional GraphQL arguments. Grats expects that either all GraphQL arguments will be defined in a single object, or that all GraphQL arguments will be defined using positional arguments. The two strategies may not be combined."; | ||
} | ||
function contextOrInfoUsedInGraphQLPosition(kind) { | ||
var tag = kind === "CONTEXT" ? Extractor_1.CONTEXT_TAG : Extractor_1.INFO_TAG; | ||
return "Cannot use `".concat(tag, "` as a type in GraphQL type position."); | ||
} |
import { ListTypeNode, NamedTypeNode, Location as GraphQLLocation, NameNode, TypeNode, NonNullTypeNode, StringValueNode, ConstValueNode, ConstDirectiveNode, ConstArgumentNode, UnionTypeDefinitionNode, FieldDefinitionNode, InputValueDefinitionNode, FloatValueNode, IntValueNode, NullValueNode, BooleanValueNode, ConstListValueNode, ConstObjectValueNode, ConstObjectFieldNode, ObjectTypeDefinitionNode, EnumValueDefinitionNode, ScalarTypeDefinitionNode, InputObjectTypeDefinitionNode, EnumTypeDefinitionNode, InterfaceTypeDefinitionNode, ASTNode, ObjectTypeExtensionNode } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { UnresolvedResolverParam } from "./metadataDirectives"; | ||
import { TsLocatableNode } from "./utils/DiagnosticError"; | ||
import { UnresolvedResolverParam, InputValueDefinitionNodeOrResolverArg } from "./metadataDirectives"; | ||
import { DiagnosticResult, TsLocatableNode } from "./utils/DiagnosticError"; | ||
export declare class GraphQLConstructor { | ||
@@ -23,2 +23,3 @@ fieldMetadataDirective(node: ts.Node, metadata: { | ||
inputValueDefinition(node: ts.Node, name: NameNode, type: TypeNode, directives: readonly ConstDirectiveNode[] | null, defaultValue: ConstValueNode | null, description: StringValueNode | null): InputValueDefinitionNode; | ||
inputValueDefinitionOrResolverArg(node: ts.Node, name: DiagnosticResult<NameNode>, type: TypeNode, directives: readonly ConstDirectiveNode[] | null, defaultValue: ConstValueNode | null, description: StringValueNode | null): InputValueDefinitionNodeOrResolverArg; | ||
enumValueDefinition(node: ts.Node, name: NameNode, directives: readonly ConstDirectiveNode[] | undefined, description: StringValueNode | null): EnumValueDefinitionNode; | ||
@@ -25,0 +26,0 @@ scalarTypeDefinition(node: ts.Node, name: NameNode, directives: readonly ConstDirectiveNode[] | null, description: StringValueNode | null): ScalarTypeDefinitionNode; |
@@ -117,2 +117,13 @@ "use strict"; | ||
}; | ||
GraphQLConstructor.prototype.inputValueDefinitionOrResolverArg = function (node, name, type, directives, defaultValue, description) { | ||
return { | ||
kind: graphql_1.Kind.INPUT_VALUE_DEFINITION, | ||
loc: loc(node), | ||
description: description !== null && description !== void 0 ? description : undefined, | ||
name: name, | ||
type: type, | ||
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : undefined, | ||
directives: this._optionalList(directives), | ||
}; | ||
}; | ||
GraphQLConstructor.prototype.enumValueDefinition = function (node, name, directives, description) { | ||
@@ -119,0 +130,0 @@ return { |
@@ -1,2 +0,4 @@ | ||
import { ConstDirectiveNode, DefinitionNode, DocumentNode, Location, NamedTypeNode } from "graphql"; | ||
import * as ts from "typescript"; | ||
import { ConstDirectiveNode, ConstValueNode, DefinitionNode, DocumentNode, InputValueDefinitionNode, Kind, Location, NameNode, StringValueNode, TypeNode } from "graphql"; | ||
import { DiagnosticResult } from "./utils/DiagnosticError.js"; | ||
/** | ||
@@ -68,8 +70,31 @@ * In most cases we can use directives to annotate constructs | ||
} | ||
export type UnresolvedResolverParam = { | ||
/** | ||
* At extraction time we don't know if a resolver arg is context, info, or a | ||
* positional GraphQL argument. If it's a positional argument, we need to ensure | ||
* it has a valid name. If it's just info or context, it's fine if it doesn't | ||
* have a name e.g. (destructured). | ||
*/ | ||
export interface InputValueDefinitionNodeOrResolverArg { | ||
readonly kind: Kind.INPUT_VALUE_DEFINITION; | ||
readonly loc: Location; | ||
readonly description?: StringValueNode; | ||
readonly name: DiagnosticResult<NameNode>; | ||
readonly type: TypeNode; | ||
readonly defaultValue?: ConstValueNode; | ||
readonly directives?: ReadonlyArray<ConstDirectiveNode>; | ||
} | ||
export type UnresolvedResolverParam = NamedFieldParam | PositionalFieldParam | Unresolved; | ||
export type ResolvedResolverParam = NamedFieldParam | PositionalFieldParam; | ||
export type NamedFieldParam = { | ||
kind: "named"; | ||
sourceNode?: ts.Node; | ||
name: FieldParam; | ||
} | { | ||
}; | ||
export type PositionalFieldParam = { | ||
kind: "positionalArg"; | ||
inputDefinition: InputValueDefinitionNode; | ||
}; | ||
export type Unresolved = { | ||
kind: "unresolved"; | ||
namedTypeNode: NamedTypeNode; | ||
inputDefinition: InputValueDefinitionNodeOrResolverArg; | ||
}; | ||
@@ -76,0 +101,0 @@ export type FieldParam = "source" | "args" | "context" | "info"; |
@@ -13,5 +13,42 @@ "use strict"; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { | ||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { | ||
if (ar || !(i in from)) { | ||
if (!ar) ar = Array.prototype.slice.call(from, 0, i); | ||
ar[i] = from[i]; | ||
} | ||
} | ||
return to.concat(ar || Array.prototype.slice.call(from)); | ||
}; | ||
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.resolveResolverParams = resolveResolverParams; | ||
var graphql_1 = require("graphql"); | ||
var TypeContext_1 = require("../TypeContext"); | ||
var Result_1 = require("../utils/Result"); | ||
@@ -21,2 +58,3 @@ var DiagnosticError_1 = require("../utils/DiagnosticError"); | ||
var E = require("../Errors"); | ||
var GraphQLConstructor_1 = require("../GraphQLConstructor"); | ||
function resolveResolverParams(ctx, definitions) { | ||
@@ -30,2 +68,3 @@ var resolver = new ResolverParamsResolver(ctx); | ||
this.ctx = ctx; | ||
this.gql = new GraphQLConstructor_1.GraphQLConstructor(); | ||
} | ||
@@ -46,2 +85,3 @@ ResolverParamsResolver.prototype.resolve = function (definitions) { | ||
ResolverParamsResolver.prototype.transformField = function (field) { | ||
var e_1, _a; | ||
var _this = this; | ||
@@ -51,27 +91,61 @@ if (field.resolverParams == null) { | ||
} | ||
var nextResolverParams = field.resolverParams.map(function (param) { | ||
return _this.transformParam(param); | ||
// Resolve all the params individually | ||
var resolverParams = field.resolverParams.map(function (param) { return _this.transformParam(param); }); | ||
// Now we check to see if the params are a valid combination... | ||
var args = resolverParams.find(function (param) { | ||
return param.kind === "named" && param.name === "args"; | ||
}); | ||
return __assign(__assign({}, field), { resolverParams: nextResolverParams }); | ||
var positionalArgs = resolverParams.filter(function (param) { return param.kind === "positionalArg"; }); | ||
if (args != null && positionalArgs.length > 0) { | ||
this.errors.push((0, DiagnosticError_1.tsErr)((0, helpers_1.nullThrows)(args.sourceNode), E.positionalArgAndArgsObject(), [ | ||
(0, DiagnosticError_1.gqlRelated)((0, helpers_1.loc)(positionalArgs[0].inputDefinition), "Positional GraphQL argument defined here"), | ||
])); | ||
return field; | ||
} | ||
var fieldArgs = field.arguments == null ? [] : __spreadArray([], __read(field.arguments), false); | ||
try { | ||
// Add any positional args to the field's arguments | ||
for (var positionalArgs_1 = __values(positionalArgs), positionalArgs_1_1 = positionalArgs_1.next(); !positionalArgs_1_1.done; positionalArgs_1_1 = positionalArgs_1.next()) { | ||
var positionalArg = positionalArgs_1_1.value; | ||
fieldArgs.push(positionalArg.inputDefinition); | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (positionalArgs_1_1 && !positionalArgs_1_1.done && (_a = positionalArgs_1.return)) _a.call(positionalArgs_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
return __assign(__assign({}, field), { arguments: fieldArgs, resolverParams: resolverParams }); | ||
}; | ||
ResolverParamsResolver.prototype.transformParam = function (param) { | ||
var _a, _b; | ||
switch (param.kind) { | ||
case "named": | ||
case "positionalArg": | ||
return param; | ||
case "unresolved": { | ||
var resolved = this.ctx.gqlNameDefinitionForGqlName(param.namedTypeNode.name); | ||
if (resolved.kind === "ERROR") { | ||
this.errors.push(resolved.err); | ||
return param; | ||
} | ||
switch (resolved.value.kind) { | ||
case "CONTEXT": | ||
return { kind: "named", name: "context" }; | ||
case "INFO": | ||
return { kind: "named", name: "info" }; | ||
default: { | ||
this.errors.push((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(param.namedTypeNode), E.invalidResolverParamType())); | ||
var unwrappedType = this.gql.nullableType(param.inputDefinition.type); | ||
if (unwrappedType.kind === "NamedType" && | ||
unwrappedType.name.value === TypeContext_1.UNRESOLVED_REFERENCE_NAME) { | ||
var resolved = this.ctx.gqlNameDefinitionForGqlName(unwrappedType.name); | ||
if (resolved.kind === "ERROR") { | ||
this.errors.push(resolved.err); | ||
return param; | ||
} | ||
switch (resolved.value.kind) { | ||
case "CONTEXT": | ||
return { kind: "named", name: "context" }; | ||
case "INFO": | ||
return { kind: "named", name: "info" }; | ||
default: { | ||
// We'll assume it's supposed to be a positional arg. | ||
return (_a = this.resolveToPositionalArg(param)) !== null && _a !== void 0 ? _a : param; | ||
} | ||
} | ||
} | ||
// This can't be a context or info param so we'll assume it's supposed | ||
// to be a positional arg. | ||
return (_b = this.resolveToPositionalArg(param)) !== null && _b !== void 0 ? _b : param; | ||
} | ||
@@ -84,3 +158,13 @@ default: { | ||
}; | ||
ResolverParamsResolver.prototype.resolveToPositionalArg = function (unresolved) { | ||
if (unresolved.inputDefinition.name.kind === "ERROR") { | ||
this.errors.push(unresolved.inputDefinition.name.err); | ||
return null; | ||
} | ||
return { | ||
kind: "positionalArg", | ||
inputDefinition: __assign(__assign({}, unresolved.inputDefinition), { name: unresolved.inputDefinition.name.value }), | ||
}; | ||
}; | ||
return ResolverParamsResolver; | ||
}()); |
@@ -150,2 +150,5 @@ "use strict"; | ||
} | ||
if (nameDefinition.kind === "CONTEXT" || nameDefinition.kind === "INFO") { | ||
return (0, Result_1.err)((0, DiagnosticError_1.gqlErr)((0, helpers_1.loc)(unresolved), E.contextOrInfoUsedInGraphQLPosition(nameDefinition.kind), [(0, DiagnosticError_1.gqlRelated)((0, helpers_1.loc)(nameDefinition.name), "Defined here")])); | ||
} | ||
return (0, Result_1.ok)(__assign(__assign({}, unresolved), { value: nameDefinition.name.value })); | ||
@@ -192,2 +195,7 @@ }; | ||
} | ||
if (nameDefinition.kind === "CONTEXT" || nameDefinition.kind === "INFO") { | ||
return (0, Result_1.err)((0, DiagnosticError_1.tsErr)(node, E.contextOrInfoUsedInGraphQLPosition(nameDefinition.kind), [ | ||
(0, DiagnosticError_1.gqlRelated)((0, helpers_1.loc)(nameDefinition.name), "Defined here"), | ||
])); | ||
} | ||
return (0, Result_1.ok)(nameDefinition.name.value); | ||
@@ -194,0 +202,0 @@ }; |
{ | ||
"name": "grats", | ||
"version": "0.0.27", | ||
"version": "0.0.28", | ||
"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
377773
7736