graphql-language-service
Advanced tools
Comparing version 5.2.0-canary-f5992ff0.0 to 5.2.0
@@ -5,3 +5,2 @@ "use strict"; | ||
const graphql_1 = require("graphql"); | ||
const introspection_1 = require("graphql/type/introspection"); | ||
function getDefinitionState(tokenState) { | ||
@@ -24,10 +23,10 @@ let definitionState; | ||
function getFieldDef(schema, type, fieldName) { | ||
if (fieldName === introspection_1.SchemaMetaFieldDef.name && schema.getQueryType() === type) { | ||
return introspection_1.SchemaMetaFieldDef; | ||
if (fieldName === graphql_1.SchemaMetaFieldDef.name && schema.getQueryType() === type) { | ||
return graphql_1.SchemaMetaFieldDef; | ||
} | ||
if (fieldName === introspection_1.TypeMetaFieldDef.name && schema.getQueryType() === type) { | ||
return introspection_1.TypeMetaFieldDef; | ||
if (fieldName === graphql_1.TypeMetaFieldDef.name && schema.getQueryType() === type) { | ||
return graphql_1.TypeMetaFieldDef; | ||
} | ||
if (fieldName === introspection_1.TypeNameMetaFieldDef.name && (0, graphql_1.isCompositeType)(type)) { | ||
return introspection_1.TypeNameMetaFieldDef; | ||
if (fieldName === graphql_1.TypeNameMetaFieldDef.name && (0, graphql_1.isCompositeType)(type)) { | ||
return graphql_1.TypeNameMetaFieldDef; | ||
} | ||
@@ -34,0 +33,0 @@ if ('getFields' in type) { |
@@ -597,9 +597,10 @@ "use strict"; | ||
const token = runOnlineParser(queryText, (stream, state, style, index) => { | ||
if (index === cursor.line && | ||
stream.getCurrentPosition() + offset >= cursor.character + 1) { | ||
styleAtCursor = style; | ||
stateAtCursor = Object.assign({}, state); | ||
stringAtCursor = stream.current(); | ||
return 'BREAK'; | ||
if (index !== cursor.line || | ||
stream.getCurrentPosition() + offset < cursor.character + 1) { | ||
return; | ||
} | ||
styleAtCursor = style; | ||
stateAtCursor = Object.assign({}, state); | ||
stringAtCursor = stream.current(); | ||
return 'BREAK'; | ||
}); | ||
@@ -606,0 +607,0 @@ return { |
@@ -55,3 +55,3 @@ "use strict"; | ||
return { | ||
Field: (node) => { | ||
Field(node) { | ||
const tokenizedText = node.alias | ||
@@ -103,6 +103,6 @@ ? [buildToken('plain', node.alias), buildToken('plain', ': ')] | ||
] }, meta(node))), | ||
InputValueDefinition: (node) => { | ||
InputValueDefinition(node) { | ||
return Object.assign({ tokenizedText: [buildToken('plain', node.name)] }, meta(node)); | ||
}, | ||
FieldDefinition: (node) => { | ||
FieldDefinition(node) { | ||
return Object.assign({ tokenizedText: [buildToken('plain', node.name)] }, meta(node)); | ||
@@ -109,0 +109,0 @@ }, |
@@ -5,2 +5,4 @@ "use strict"; | ||
constructor(sourceText) { | ||
this._start = 0; | ||
this._pos = 0; | ||
this.getStartOfToken = () => this._start; | ||
@@ -97,4 +99,2 @@ this.getCurrentPosition = () => this._pos; | ||
this.current = () => this._sourceText.slice(this._start, this._pos); | ||
this._start = 0; | ||
this._pos = 0; | ||
this._sourceText = sourceText; | ||
@@ -101,0 +101,0 @@ } |
import type { Diagnostic as DiagnosticType, CompletionItem as CompletionItemType } from 'vscode-languageserver-types'; | ||
export { InsertTextFormat } from 'vscode-languageserver-types'; | ||
import type { ASTNode, GraphQLSchema } from 'graphql'; | ||
import type { DocumentNode, FragmentDefinitionNode, NamedTypeNode, TypeDefinitionNode, NameNode } from 'graphql/language'; | ||
import type { GraphQLArgument, GraphQLEnumValue, GraphQLField, GraphQLInputFieldMap, GraphQLInterfaceType, GraphQLObjectType, GraphQLType } from 'graphql/type/definition'; | ||
import type { GraphQLDirective } from 'graphql/type/directives'; | ||
import type { ASTNode, GraphQLSchema, DocumentNode, FragmentDefinitionNode, NamedTypeNode, TypeDefinitionNode, NameNode, GraphQLArgument, GraphQLEnumValue, GraphQLField, GraphQLInputFieldMap, GraphQLInterfaceType, GraphQLObjectType, GraphQLType, GraphQLDirective } from 'graphql'; | ||
export declare type Maybe<T> = T | null | undefined; | ||
@@ -8,0 +5,0 @@ import type { GraphQLConfig, GraphQLProjectConfig, GraphQLExtensionDeclaration } from 'graphql-config'; |
@@ -1,5 +0,5 @@ | ||
import { ASTNode } from 'graphql/language'; | ||
import { IPosition as TPosition } from '../types'; | ||
import { ASTNode } from 'graphql'; | ||
export declare function getASTNodeAtPosition(query: string, ast: ASTNode, point: TPosition): ASTNode | undefined; | ||
export declare function pointToOffset(text: string, point: TPosition): number; | ||
//# sourceMappingURL=getASTNodeAtPosition.d.ts.map |
@@ -9,2 +9,3 @@ import type { JSONSchema6, JSONSchema6Definition, JSONSchema6TypeName } from 'json-schema'; | ||
useMarkdownDescription?: boolean; | ||
scalarSchemas?: Record<string, JSONSchema6>; | ||
}; | ||
@@ -11,0 +12,0 @@ export declare const defaultJSONSchemaOptions: { |
@@ -25,2 +25,27 @@ "use strict"; | ||
} | ||
function renderDefinitionDescription(t, useMarkdown, description) { | ||
const into = []; | ||
const type = 'type' in t ? t.type : t; | ||
if ('type' in t && t.description) { | ||
text(into, t.description); | ||
text(into, '\n\n'); | ||
} | ||
text(into, renderTypeToString(type, useMarkdown)); | ||
if (description) { | ||
text(into, '\n'); | ||
text(into, description); | ||
} | ||
else if (!(0, graphql_1.isScalarType)(type) && 'description' in type && type.description) { | ||
text(into, '\n'); | ||
text(into, type.description); | ||
} | ||
else if ('ofType' in type && | ||
!(0, graphql_1.isScalarType)(type.ofType) && | ||
'description' in type.ofType && | ||
type.ofType.description) { | ||
text(into, '\n'); | ||
text(into, type.ofType.description); | ||
} | ||
return into.join(''); | ||
} | ||
function renderTypeToString(t, useMarkdown) { | ||
@@ -37,9 +62,9 @@ const into = []; | ||
} | ||
const scalarTypesMap = { | ||
Int: 'integer', | ||
String: 'string', | ||
Float: 'number', | ||
ID: 'string', | ||
Boolean: 'boolean', | ||
DateTime: 'string', | ||
const defaultScalarTypesMap = { | ||
Int: { type: 'integer' }, | ||
String: { type: 'string' }, | ||
Float: { type: 'number' }, | ||
ID: { type: 'string' }, | ||
Boolean: { type: 'boolean' }, | ||
DateTime: { type: 'string' }, | ||
}; | ||
@@ -58,25 +83,52 @@ class Marker { | ||
} | ||
function getJSONSchemaFromGraphQLType(type, options) { | ||
let required = false; | ||
function getJSONSchemaFromGraphQLType(fieldOrType, options) { | ||
var _a, _b; | ||
let definition = Object.create(null); | ||
const definitions = Object.create(null); | ||
if ('defaultValue' in type && type.defaultValue !== undefined) { | ||
definition.default = type.defaultValue; | ||
const isField = 'type' in fieldOrType; | ||
const type = isField ? fieldOrType.type : fieldOrType; | ||
const baseType = (0, graphql_1.isNonNullType)(type) ? type.ofType : type; | ||
const required = (0, graphql_1.isNonNullType)(type); | ||
if ((0, graphql_1.isScalarType)(baseType)) { | ||
if ((_a = options === null || options === void 0 ? void 0 : options.scalarSchemas) === null || _a === void 0 ? void 0 : _a[baseType.name]) { | ||
definition = JSON.parse(JSON.stringify(options.scalarSchemas[baseType.name])); | ||
} | ||
else { | ||
definition.type = ['string', 'number', 'boolean', 'integer']; | ||
} | ||
if (!required) { | ||
if (Array.isArray(definition.type)) { | ||
definition.type.push('null'); | ||
} | ||
else if (definition.type) { | ||
definition.type = [definition.type, 'null']; | ||
} | ||
else if (definition.enum) { | ||
definition.enum.push(null); | ||
} | ||
else if (definition.oneOf) { | ||
definition.oneOf.push({ type: 'null' }); | ||
} | ||
else { | ||
definition = { | ||
oneOf: [definition, { type: 'null' }], | ||
}; | ||
} | ||
} | ||
} | ||
if ((0, graphql_1.isEnumType)(type)) { | ||
definition.type = 'string'; | ||
definition.enum = type.getValues().map(val => val.name); | ||
else if ((0, graphql_1.isEnumType)(baseType)) { | ||
definition.enum = baseType.getValues().map(val => val.name); | ||
if (!required) { | ||
definition.enum.push(null); | ||
} | ||
} | ||
if ((0, graphql_1.isScalarType)(type) && scalarTypesMap[type.name]) { | ||
definition.type = scalarTypesMap[type.name]; | ||
} | ||
if ((0, graphql_1.isListType)(type)) { | ||
definition.type = 'array'; | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType(type.ofType, options); | ||
if (def.$ref) { | ||
definition.items = { $ref: def.$ref }; | ||
else if ((0, graphql_1.isListType)(baseType)) { | ||
if (required) { | ||
definition.type = 'array'; | ||
} | ||
else { | ||
definition.items = def; | ||
definition.type = ['array', 'null']; | ||
} | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType(baseType.ofType, options); | ||
definition.items = def; | ||
if (defs) { | ||
@@ -88,16 +140,14 @@ for (const defName of Object.keys(defs)) { | ||
} | ||
if ((0, graphql_1.isNonNullType)(type)) { | ||
required = true; | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType(type.ofType, options); | ||
definition = def; | ||
if (defs) { | ||
for (const defName of Object.keys(defs)) { | ||
definitions[defName] = defs[defName]; | ||
} | ||
else if ((0, graphql_1.isInputObjectType)(baseType)) { | ||
if (required) { | ||
definition.$ref = `#/definitions/${baseType.name}`; | ||
} | ||
} | ||
if ((0, graphql_1.isInputObjectType)(type)) { | ||
definition.$ref = `#/definitions/${type.name}`; | ||
if (options === null || options === void 0 ? void 0 : options.definitionMarker.mark(type.name)) { | ||
const fields = type.getFields(); | ||
else { | ||
definition.oneOf = [ | ||
{ $ref: `#/definitions/${baseType.name}` }, | ||
{ type: 'null' }, | ||
]; | ||
} | ||
if ((_b = options === null || options === void 0 ? void 0 : options.definitionMarker) === null || _b === void 0 ? void 0 : _b.mark(baseType.name)) { | ||
const fields = baseType.getFields(); | ||
const fieldDef = { | ||
@@ -108,31 +158,10 @@ type: 'object', | ||
}; | ||
if (type.description) { | ||
fieldDef.description = | ||
type.description + '\n' + renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
fieldDef.markdownDescription = | ||
type.description + '\n' + renderTypeToString(type, true); | ||
} | ||
fieldDef.description = renderDefinitionDescription(baseType); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
fieldDef.markdownDescription = renderDefinitionDescription(baseType, true); | ||
} | ||
else { | ||
fieldDef.description = renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
fieldDef.markdownDescription = renderTypeToString(type, true); | ||
} | ||
} | ||
for (const fieldName of Object.keys(fields)) { | ||
const field = fields[fieldName]; | ||
const { required: fieldRequired, definition: typeDefinition, definitions: typeDefinitions, } = getJSONSchemaFromGraphQLType(field.type, options); | ||
const { definition: fieldDefinition, } = getJSONSchemaFromGraphQLType(field, options); | ||
fieldDef.properties[fieldName] = Object.assign(Object.assign({}, typeDefinition), fieldDefinition); | ||
const renderedField = renderTypeToString(field.type); | ||
fieldDef.properties[fieldName].description = field.description | ||
? field.description + '\n' + renderedField | ||
: renderedField; | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
const renderedFieldMarkdown = renderTypeToString(field.type, true); | ||
fieldDef.properties[fieldName].markdownDescription = field.description | ||
? field.description + '\n' + renderedFieldMarkdown | ||
: renderedFieldMarkdown; | ||
} | ||
const { required: fieldRequired, definition: fieldDefinition, definitions: typeDefinitions, } = getJSONSchemaFromGraphQLType(field, options); | ||
fieldDef.properties[fieldName] = fieldDefinition; | ||
if (fieldRequired) { | ||
@@ -147,20 +176,12 @@ fieldDef.required.push(fieldName); | ||
} | ||
definitions[type.name] = fieldDef; | ||
definitions[baseType.name] = fieldDef; | ||
} | ||
} | ||
if ('description' in type && | ||
!(0, graphql_1.isScalarType)(type) && | ||
type.description && | ||
!definition.description) { | ||
definition.description = type.description + '\n' + renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
definition.markdownDescription = | ||
type.description + '\n' + renderTypeToString(type, true); | ||
} | ||
if ('defaultValue' in fieldOrType && fieldOrType.defaultValue !== undefined) { | ||
definition.default = fieldOrType.defaultValue; | ||
} | ||
else { | ||
definition.description = renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
definition.markdownDescription = renderTypeToString(type, true); | ||
} | ||
const { description } = definition; | ||
definition.description = renderDefinitionDescription(fieldOrType, false, description); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
definition.markdownDescription = renderDefinitionDescription(fieldOrType, true, description); | ||
} | ||
@@ -172,3 +193,3 @@ return { required, definition, definitions }; | ||
const jsonSchema = { | ||
$schema: 'https://json-schema.org/draft/2020-12/schema', | ||
$schema: 'http://json-schema.org/draft-04/schema', | ||
type: 'object', | ||
@@ -178,3 +199,3 @@ properties: {}, | ||
}; | ||
const runtimeOptions = Object.assign(Object.assign({}, options), { definitionMarker: new Marker() }); | ||
const runtimeOptions = Object.assign(Object.assign({}, options), { definitionMarker: new Marker(), scalarSchemas: Object.assign(Object.assign({}, defaultScalarTypesMap), options === null || options === void 0 ? void 0 : options.scalarSchemas) }); | ||
if (variableToType) { | ||
@@ -181,0 +202,0 @@ for (const [variableName, type] of Object.entries(variableToType)) { |
@@ -1,2 +0,2 @@ | ||
import { Location } from 'graphql/language'; | ||
import { Location } from 'graphql'; | ||
import { IRange, IPosition } from '../types'; | ||
@@ -3,0 +3,0 @@ export declare class Range implements IRange { |
@@ -1,3 +0,2 @@ | ||
import { isCompositeType, } from 'graphql'; | ||
import { SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from 'graphql/type/introspection'; | ||
import { isCompositeType, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from 'graphql'; | ||
export function getDefinitionState(tokenState) { | ||
@@ -4,0 +3,0 @@ let definitionState; |
@@ -591,9 +591,10 @@ import { isInterfaceType, GraphQLInterfaceType, GraphQLObjectType, Kind, DirectiveLocation, isListType, isNonNullType, isScalarType, isObjectType, isUnionType, isEnumType, isInputObjectType, isOutputType, GraphQLBoolean, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, assertAbstractType, doTypesOverlap, getNamedType, getNullableType, isAbstractType, isCompositeType, isInputType, visit, BREAK, parse, } from 'graphql'; | ||
const token = runOnlineParser(queryText, (stream, state, style, index) => { | ||
if (index === cursor.line && | ||
stream.getCurrentPosition() + offset >= cursor.character + 1) { | ||
styleAtCursor = style; | ||
stateAtCursor = Object.assign({}, state); | ||
stringAtCursor = stream.current(); | ||
return 'BREAK'; | ||
if (index !== cursor.line || | ||
stream.getCurrentPosition() + offset < cursor.character + 1) { | ||
return; | ||
} | ||
styleAtCursor = style; | ||
stateAtCursor = Object.assign({}, state); | ||
stringAtCursor = stream.current(); | ||
return 'BREAK'; | ||
}); | ||
@@ -600,0 +601,0 @@ return { |
@@ -51,3 +51,3 @@ import { Kind, parse, visit, } from 'graphql'; | ||
return { | ||
Field: (node) => { | ||
Field(node) { | ||
const tokenizedText = node.alias | ||
@@ -99,6 +99,6 @@ ? [buildToken('plain', node.alias), buildToken('plain', ': ')] | ||
] }, meta(node))), | ||
InputValueDefinition: (node) => { | ||
InputValueDefinition(node) { | ||
return Object.assign({ tokenizedText: [buildToken('plain', node.name)] }, meta(node)); | ||
}, | ||
FieldDefinition: (node) => { | ||
FieldDefinition(node) { | ||
return Object.assign({ tokenizedText: [buildToken('plain', node.name)] }, meta(node)); | ||
@@ -105,0 +105,0 @@ }, |
export default class CharacterStream { | ||
constructor(sourceText) { | ||
this._start = 0; | ||
this._pos = 0; | ||
this.getStartOfToken = () => this._start; | ||
@@ -94,4 +96,2 @@ this.getCurrentPosition = () => this._pos; | ||
this.current = () => this._sourceText.slice(this._start, this._pos); | ||
this._start = 0; | ||
this._pos = 0; | ||
this._sourceText = sourceText; | ||
@@ -98,0 +98,0 @@ } |
import type { Diagnostic as DiagnosticType, CompletionItem as CompletionItemType } from 'vscode-languageserver-types'; | ||
export { InsertTextFormat } from 'vscode-languageserver-types'; | ||
import type { ASTNode, GraphQLSchema } from 'graphql'; | ||
import type { DocumentNode, FragmentDefinitionNode, NamedTypeNode, TypeDefinitionNode, NameNode } from 'graphql/language'; | ||
import type { GraphQLArgument, GraphQLEnumValue, GraphQLField, GraphQLInputFieldMap, GraphQLInterfaceType, GraphQLObjectType, GraphQLType } from 'graphql/type/definition'; | ||
import type { GraphQLDirective } from 'graphql/type/directives'; | ||
import type { ASTNode, GraphQLSchema, DocumentNode, FragmentDefinitionNode, NamedTypeNode, TypeDefinitionNode, NameNode, GraphQLArgument, GraphQLEnumValue, GraphQLField, GraphQLInputFieldMap, GraphQLInterfaceType, GraphQLObjectType, GraphQLType, GraphQLDirective } from 'graphql'; | ||
export declare type Maybe<T> = T | null | undefined; | ||
@@ -8,0 +5,0 @@ import type { GraphQLConfig, GraphQLProjectConfig, GraphQLExtensionDeclaration } from 'graphql-config'; |
@@ -1,5 +0,5 @@ | ||
import { ASTNode } from 'graphql/language'; | ||
import { IPosition as TPosition } from '../types'; | ||
import { ASTNode } from 'graphql'; | ||
export declare function getASTNodeAtPosition(query: string, ast: ASTNode, point: TPosition): ASTNode | undefined; | ||
export declare function pointToOffset(text: string, point: TPosition): number; | ||
//# sourceMappingURL=getASTNodeAtPosition.d.ts.map |
@@ -9,2 +9,3 @@ import type { JSONSchema6, JSONSchema6Definition, JSONSchema6TypeName } from 'json-schema'; | ||
useMarkdownDescription?: boolean; | ||
scalarSchemas?: Record<string, JSONSchema6>; | ||
}; | ||
@@ -11,0 +12,0 @@ export declare const defaultJSONSchemaOptions: { |
@@ -22,2 +22,27 @@ import { isEnumType, isInputObjectType, isListType, isNonNullType, isScalarType, } from 'graphql'; | ||
} | ||
function renderDefinitionDescription(t, useMarkdown, description) { | ||
const into = []; | ||
const type = 'type' in t ? t.type : t; | ||
if ('type' in t && t.description) { | ||
text(into, t.description); | ||
text(into, '\n\n'); | ||
} | ||
text(into, renderTypeToString(type, useMarkdown)); | ||
if (description) { | ||
text(into, '\n'); | ||
text(into, description); | ||
} | ||
else if (!isScalarType(type) && 'description' in type && type.description) { | ||
text(into, '\n'); | ||
text(into, type.description); | ||
} | ||
else if ('ofType' in type && | ||
!isScalarType(type.ofType) && | ||
'description' in type.ofType && | ||
type.ofType.description) { | ||
text(into, '\n'); | ||
text(into, type.ofType.description); | ||
} | ||
return into.join(''); | ||
} | ||
function renderTypeToString(t, useMarkdown) { | ||
@@ -34,9 +59,9 @@ const into = []; | ||
} | ||
const scalarTypesMap = { | ||
Int: 'integer', | ||
String: 'string', | ||
Float: 'number', | ||
ID: 'string', | ||
Boolean: 'boolean', | ||
DateTime: 'string', | ||
const defaultScalarTypesMap = { | ||
Int: { type: 'integer' }, | ||
String: { type: 'string' }, | ||
Float: { type: 'number' }, | ||
ID: { type: 'string' }, | ||
Boolean: { type: 'boolean' }, | ||
DateTime: { type: 'string' }, | ||
}; | ||
@@ -55,25 +80,52 @@ class Marker { | ||
} | ||
function getJSONSchemaFromGraphQLType(type, options) { | ||
let required = false; | ||
function getJSONSchemaFromGraphQLType(fieldOrType, options) { | ||
var _a, _b; | ||
let definition = Object.create(null); | ||
const definitions = Object.create(null); | ||
if ('defaultValue' in type && type.defaultValue !== undefined) { | ||
definition.default = type.defaultValue; | ||
const isField = 'type' in fieldOrType; | ||
const type = isField ? fieldOrType.type : fieldOrType; | ||
const baseType = isNonNullType(type) ? type.ofType : type; | ||
const required = isNonNullType(type); | ||
if (isScalarType(baseType)) { | ||
if ((_a = options === null || options === void 0 ? void 0 : options.scalarSchemas) === null || _a === void 0 ? void 0 : _a[baseType.name]) { | ||
definition = JSON.parse(JSON.stringify(options.scalarSchemas[baseType.name])); | ||
} | ||
else { | ||
definition.type = ['string', 'number', 'boolean', 'integer']; | ||
} | ||
if (!required) { | ||
if (Array.isArray(definition.type)) { | ||
definition.type.push('null'); | ||
} | ||
else if (definition.type) { | ||
definition.type = [definition.type, 'null']; | ||
} | ||
else if (definition.enum) { | ||
definition.enum.push(null); | ||
} | ||
else if (definition.oneOf) { | ||
definition.oneOf.push({ type: 'null' }); | ||
} | ||
else { | ||
definition = { | ||
oneOf: [definition, { type: 'null' }], | ||
}; | ||
} | ||
} | ||
} | ||
if (isEnumType(type)) { | ||
definition.type = 'string'; | ||
definition.enum = type.getValues().map(val => val.name); | ||
else if (isEnumType(baseType)) { | ||
definition.enum = baseType.getValues().map(val => val.name); | ||
if (!required) { | ||
definition.enum.push(null); | ||
} | ||
} | ||
if (isScalarType(type) && scalarTypesMap[type.name]) { | ||
definition.type = scalarTypesMap[type.name]; | ||
} | ||
if (isListType(type)) { | ||
definition.type = 'array'; | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType(type.ofType, options); | ||
if (def.$ref) { | ||
definition.items = { $ref: def.$ref }; | ||
else if (isListType(baseType)) { | ||
if (required) { | ||
definition.type = 'array'; | ||
} | ||
else { | ||
definition.items = def; | ||
definition.type = ['array', 'null']; | ||
} | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType(baseType.ofType, options); | ||
definition.items = def; | ||
if (defs) { | ||
@@ -85,16 +137,14 @@ for (const defName of Object.keys(defs)) { | ||
} | ||
if (isNonNullType(type)) { | ||
required = true; | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType(type.ofType, options); | ||
definition = def; | ||
if (defs) { | ||
for (const defName of Object.keys(defs)) { | ||
definitions[defName] = defs[defName]; | ||
} | ||
else if (isInputObjectType(baseType)) { | ||
if (required) { | ||
definition.$ref = `#/definitions/${baseType.name}`; | ||
} | ||
} | ||
if (isInputObjectType(type)) { | ||
definition.$ref = `#/definitions/${type.name}`; | ||
if (options === null || options === void 0 ? void 0 : options.definitionMarker.mark(type.name)) { | ||
const fields = type.getFields(); | ||
else { | ||
definition.oneOf = [ | ||
{ $ref: `#/definitions/${baseType.name}` }, | ||
{ type: 'null' }, | ||
]; | ||
} | ||
if ((_b = options === null || options === void 0 ? void 0 : options.definitionMarker) === null || _b === void 0 ? void 0 : _b.mark(baseType.name)) { | ||
const fields = baseType.getFields(); | ||
const fieldDef = { | ||
@@ -105,31 +155,10 @@ type: 'object', | ||
}; | ||
if (type.description) { | ||
fieldDef.description = | ||
type.description + '\n' + renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
fieldDef.markdownDescription = | ||
type.description + '\n' + renderTypeToString(type, true); | ||
} | ||
fieldDef.description = renderDefinitionDescription(baseType); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
fieldDef.markdownDescription = renderDefinitionDescription(baseType, true); | ||
} | ||
else { | ||
fieldDef.description = renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
fieldDef.markdownDescription = renderTypeToString(type, true); | ||
} | ||
} | ||
for (const fieldName of Object.keys(fields)) { | ||
const field = fields[fieldName]; | ||
const { required: fieldRequired, definition: typeDefinition, definitions: typeDefinitions, } = getJSONSchemaFromGraphQLType(field.type, options); | ||
const { definition: fieldDefinition, } = getJSONSchemaFromGraphQLType(field, options); | ||
fieldDef.properties[fieldName] = Object.assign(Object.assign({}, typeDefinition), fieldDefinition); | ||
const renderedField = renderTypeToString(field.type); | ||
fieldDef.properties[fieldName].description = field.description | ||
? field.description + '\n' + renderedField | ||
: renderedField; | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
const renderedFieldMarkdown = renderTypeToString(field.type, true); | ||
fieldDef.properties[fieldName].markdownDescription = field.description | ||
? field.description + '\n' + renderedFieldMarkdown | ||
: renderedFieldMarkdown; | ||
} | ||
const { required: fieldRequired, definition: fieldDefinition, definitions: typeDefinitions, } = getJSONSchemaFromGraphQLType(field, options); | ||
fieldDef.properties[fieldName] = fieldDefinition; | ||
if (fieldRequired) { | ||
@@ -144,20 +173,12 @@ fieldDef.required.push(fieldName); | ||
} | ||
definitions[type.name] = fieldDef; | ||
definitions[baseType.name] = fieldDef; | ||
} | ||
} | ||
if ('description' in type && | ||
!isScalarType(type) && | ||
type.description && | ||
!definition.description) { | ||
definition.description = type.description + '\n' + renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
definition.markdownDescription = | ||
type.description + '\n' + renderTypeToString(type, true); | ||
} | ||
if ('defaultValue' in fieldOrType && fieldOrType.defaultValue !== undefined) { | ||
definition.default = fieldOrType.defaultValue; | ||
} | ||
else { | ||
definition.description = renderTypeToString(type); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
definition.markdownDescription = renderTypeToString(type, true); | ||
} | ||
const { description } = definition; | ||
definition.description = renderDefinitionDescription(fieldOrType, false, description); | ||
if (options === null || options === void 0 ? void 0 : options.useMarkdownDescription) { | ||
definition.markdownDescription = renderDefinitionDescription(fieldOrType, true, description); | ||
} | ||
@@ -169,3 +190,3 @@ return { required, definition, definitions }; | ||
const jsonSchema = { | ||
$schema: 'https://json-schema.org/draft/2020-12/schema', | ||
$schema: 'http://json-schema.org/draft-04/schema', | ||
type: 'object', | ||
@@ -175,3 +196,3 @@ properties: {}, | ||
}; | ||
const runtimeOptions = Object.assign(Object.assign({}, options), { definitionMarker: new Marker() }); | ||
const runtimeOptions = Object.assign(Object.assign({}, options), { definitionMarker: new Marker(), scalarSchemas: Object.assign(Object.assign({}, defaultScalarTypesMap), options === null || options === void 0 ? void 0 : options.scalarSchemas) }); | ||
if (variableToType) { | ||
@@ -178,0 +199,0 @@ for (const [variableName, type] of Object.entries(variableToType)) { |
@@ -1,2 +0,2 @@ | ||
import { Location } from 'graphql/language'; | ||
import { Location } from 'graphql'; | ||
import { IRange, IPosition } from '../types'; | ||
@@ -3,0 +3,0 @@ export declare class Range implements IRange { |
{ | ||
"name": "graphql-language-service", | ||
"version": "5.2.0-canary-f5992ff0.0", | ||
"version": "5.2.0", | ||
"description": "The official, runtime independent Language Service for GraphQL", | ||
@@ -38,9 +38,9 @@ "contributors": [ | ||
"dependencies": { | ||
"vscode-languageserver-types": "^3.17.1", | ||
"nullthrows": "^1.0.0" | ||
"nullthrows": "^1.0.0", | ||
"vscode-languageserver-types": "^3.17.1" | ||
}, | ||
"devDependencies": { | ||
"@types/picomatch": "^2.3.0", | ||
"@types/benchmark": "^1.0.33", | ||
"@types/json-schema": "7.0.9", | ||
"@types/picomatch": "^2.3.0", | ||
"benchmark": "^2.1.4", | ||
@@ -47,0 +47,0 @@ "graphql": "^16.4.0", |
@@ -15,10 +15,7 @@ /** | ||
isCompositeType, | ||
} from 'graphql'; | ||
import { | ||
SchemaMetaFieldDef, | ||
TypeMetaFieldDef, | ||
TypeNameMetaFieldDef, | ||
} from 'graphql/type/introspection'; | ||
} from 'graphql'; | ||
import { CompletionItemBase, AllTypeInfo } from '../types'; | ||
import { ContextTokenUnion, State } from '../parser'; | ||
@@ -25,0 +22,0 @@ |
@@ -973,10 +973,11 @@ /** | ||
if ( | ||
index === cursor.line && | ||
stream.getCurrentPosition() + offset >= cursor.character + 1 | ||
index !== cursor.line || | ||
stream.getCurrentPosition() + offset < cursor.character + 1 | ||
) { | ||
styleAtCursor = style; | ||
stateAtCursor = { ...state }; | ||
stringAtCursor = stream.current(); | ||
return 'BREAK'; | ||
return; | ||
} | ||
styleAtCursor = style; | ||
stateAtCursor = { ...state }; | ||
stringAtCursor = stream.current(); | ||
return 'BREAK'; | ||
}); | ||
@@ -983,0 +984,0 @@ |
@@ -121,3 +121,3 @@ /** | ||
return { | ||
Field: (node: FieldNode) => { | ||
Field(node: FieldNode) { | ||
const tokenizedText = node.alias | ||
@@ -195,3 +195,3 @@ ? [buildToken('plain', node.alias), buildToken('plain', ': ')] | ||
}), | ||
InputValueDefinition: (node: InputValueDefinitionNode) => { | ||
InputValueDefinition(node: InputValueDefinitionNode) { | ||
return { | ||
@@ -202,3 +202,3 @@ tokenizedText: [buildToken('plain', node.name)], | ||
}, | ||
FieldDefinition: (node: FieldDefinitionNode) => { | ||
FieldDefinition(node: FieldDefinitionNode) { | ||
return { | ||
@@ -209,3 +209,2 @@ tokenizedText: [buildToken('plain', node.name)], | ||
}, | ||
InlineFragment: (node: InlineFragmentNode) => node.selectionSet, | ||
@@ -212,0 +211,0 @@ }; |
@@ -23,9 +23,7 @@ /** | ||
export default class CharacterStream implements CharacterStreamInterface { | ||
private _start: number; | ||
private _pos: number; | ||
private _start = 0; | ||
private _pos = 0; | ||
private _sourceText: string; | ||
constructor(sourceText: string) { | ||
this._start = 0; | ||
this._pos = 0; | ||
this._sourceText = sourceText; | ||
@@ -32,0 +30,0 @@ } |
@@ -16,5 +16,5 @@ /** | ||
import type { ASTNode, GraphQLSchema } from 'graphql'; | ||
import type { | ||
ASTNode, | ||
GraphQLSchema, | ||
DocumentNode, | ||
@@ -25,5 +25,2 @@ FragmentDefinitionNode, | ||
NameNode, | ||
} from 'graphql/language'; | ||
import type { | ||
GraphQLArgument, | ||
@@ -36,4 +33,4 @@ GraphQLEnumValue, | ||
GraphQLType, | ||
} from 'graphql/type/definition'; | ||
import type { GraphQLDirective } from 'graphql/type/directives'; | ||
GraphQLDirective, | ||
} from 'graphql'; | ||
@@ -40,0 +37,0 @@ export type Maybe<T> = T | null | undefined; |
@@ -28,3 +28,11 @@ /** | ||
schema, | ||
parse(`query($id: ID, $string: String!, $boolean: Boolean, $number: Int!, $price: Float) { | ||
parse(`query( | ||
$id: ID, | ||
$string: String!, | ||
$boolean: Boolean, | ||
$number: Int!, | ||
$price: Float, | ||
$custom: SomeCustomScalar, | ||
$anotherCustom: SomeCustomScalar! | ||
) { | ||
characters{ | ||
@@ -38,7 +46,7 @@ name | ||
expect(jsonSchema.required).toEqual(['string', 'number']); | ||
expect(jsonSchema.required).toEqual(['string', 'number', 'anotherCustom']); | ||
expect(jsonSchema.properties).toEqual({ | ||
boolean: { | ||
type: 'boolean', | ||
type: ['boolean', 'null'], | ||
description: 'Boolean', | ||
@@ -56,7 +64,201 @@ }, | ||
description: 'Float', | ||
type: 'number', | ||
type: ['number', 'null'], | ||
}, | ||
custom: { | ||
description: 'SomeCustomScalar', | ||
type: ['string', 'number', 'boolean', 'integer', 'null'], | ||
}, | ||
anotherCustom: { | ||
description: 'SomeCustomScalar!', | ||
type: ['string', 'number', 'boolean', 'integer'], | ||
}, | ||
}); | ||
}); | ||
it('should handle custom scalar schemas', () => { | ||
const variableToType = collectVariables( | ||
schema, | ||
parse(`query( | ||
$email: EmailAddress!, | ||
$optionalEmail: EmailAddress, | ||
$evenNumber: Even!, | ||
$optionalEvenNumber: Even, | ||
$special: SpecialScalar!, | ||
$optionalSpecial: SpecialScalar, | ||
$specialDate: SpecialDate!, | ||
$optionalSpecialDate: SpecialDate, | ||
$foobar: FooBar!, | ||
$optionalFoobar: FooBar, | ||
$foo: Foo!, | ||
$optionalFoo: Foo, | ||
$customInput: CustomScalarsInput!, | ||
$optionalCustomInput: CustomScalarsInput | ||
) { | ||
characters{ | ||
name | ||
} | ||
}`), | ||
); | ||
const jsonSchema = getVariablesJSONSchema(variableToType, { | ||
scalarSchemas: { | ||
EmailAddress: { | ||
type: 'string', | ||
format: 'email', | ||
}, | ||
Even: { | ||
type: 'integer', | ||
multipleOf: 2, | ||
description: 'An even number.', | ||
}, | ||
SpecialScalar: { | ||
type: ['string'], | ||
minLength: 5, | ||
}, | ||
FooBar: { | ||
enum: ['foo', 'bar'], | ||
}, | ||
Foo: { | ||
const: 'foo', | ||
}, | ||
SpecialDate: { | ||
description: 'A date or date time.', | ||
oneOf: [ | ||
{ | ||
type: 'string', | ||
format: 'date-time', | ||
}, | ||
{ | ||
type: 'string', | ||
format: 'date', | ||
}, | ||
], | ||
}, | ||
}, | ||
}); | ||
expect(jsonSchema.required).toEqual([ | ||
'email', | ||
'evenNumber', | ||
'special', | ||
'specialDate', | ||
'foobar', | ||
'foo', | ||
'customInput', | ||
]); | ||
expect(jsonSchema.definitions).toEqual({ | ||
CustomScalarsInput: { | ||
description: 'CustomScalarsInput\nAn input type with custom scalars', | ||
properties: { | ||
email: { | ||
description: 'example email\n\nEmailAddress', | ||
format: 'email', | ||
type: ['string', 'null'], | ||
}, | ||
even: { | ||
description: 'example even\n\nEven\nAn even number.', | ||
multipleOf: 2, | ||
type: ['integer', 'null'], | ||
}, | ||
}, | ||
required: [], | ||
type: 'object', | ||
}, | ||
}); | ||
expect(jsonSchema.properties).toEqual({ | ||
email: { | ||
type: 'string', | ||
format: 'email', | ||
description: 'EmailAddress!', | ||
}, | ||
optionalEmail: { | ||
type: ['string', 'null'], | ||
format: 'email', | ||
description: 'EmailAddress', | ||
}, | ||
evenNumber: { | ||
type: 'integer', | ||
multipleOf: 2, | ||
description: 'Even!\nAn even number.', | ||
}, | ||
optionalEvenNumber: { | ||
type: ['integer', 'null'], | ||
multipleOf: 2, | ||
description: 'Even\nAn even number.', | ||
}, | ||
special: { | ||
type: ['string'], | ||
minLength: 5, | ||
description: 'SpecialScalar!', | ||
}, | ||
optionalSpecial: { | ||
type: ['string', 'null'], | ||
minLength: 5, | ||
description: 'SpecialScalar', | ||
}, | ||
foobar: { | ||
enum: ['foo', 'bar'], | ||
description: 'FooBar!', | ||
}, | ||
optionalFoobar: { | ||
enum: ['foo', 'bar', null], | ||
description: 'FooBar', | ||
}, | ||
foo: { | ||
const: 'foo', | ||
description: 'Foo!', | ||
}, | ||
optionalFoo: { | ||
oneOf: [{ const: 'foo' }, { type: 'null' }], | ||
description: 'Foo', | ||
}, | ||
specialDate: { | ||
description: 'SpecialDate!\nA date or date time.', | ||
oneOf: [ | ||
{ | ||
type: 'string', | ||
format: 'date-time', | ||
}, | ||
{ | ||
type: 'string', | ||
format: 'date', | ||
}, | ||
], | ||
}, | ||
optionalSpecialDate: { | ||
description: 'SpecialDate\nA date or date time.', | ||
oneOf: [ | ||
{ | ||
type: 'string', | ||
format: 'date-time', | ||
}, | ||
{ | ||
type: 'string', | ||
format: 'date', | ||
}, | ||
{ | ||
type: 'null', | ||
}, | ||
], | ||
}, | ||
customInput: { | ||
$ref: '#/definitions/CustomScalarsInput', | ||
description: 'CustomScalarsInput!\nAn input type with custom scalars', | ||
}, | ||
optionalCustomInput: { | ||
description: 'CustomScalarsInput\nAn input type with custom scalars', | ||
oneOf: [ | ||
{ | ||
$ref: '#/definitions/CustomScalarsInput', | ||
}, | ||
{ | ||
type: 'null', | ||
}, | ||
], | ||
}, | ||
}); | ||
}); | ||
it('should handle input object types', () => { | ||
@@ -79,7 +281,7 @@ const variableToType = collectVariables( | ||
$ref: '#/definitions/InputType', | ||
description: 'InputType!', | ||
description: 'InputType!\nexample input type', | ||
}, | ||
anotherInput: { | ||
$ref: '#/definitions/InputType', | ||
description: 'example input type\nInputType', | ||
oneOf: [{ $ref: '#/definitions/InputType' }, { type: 'null' }], | ||
description: 'InputType\nexample input type', | ||
}, | ||
@@ -90,11 +292,11 @@ }); | ||
type: 'object', | ||
description: 'example input type\nInputType', | ||
description: 'InputType\nexample input type', | ||
properties: { | ||
key: { | ||
description: 'example key\nString!', | ||
description: 'example key\n\nString!', | ||
type: 'string', | ||
}, | ||
value: { | ||
description: 'example value\nInt', | ||
type: 'integer', | ||
description: 'example value\n\nInt', | ||
type: ['integer', 'null'], | ||
default: 42, | ||
@@ -104,10 +306,14 @@ }, | ||
$ref: '#/definitions/ChildInputType', | ||
description: 'nesting a whole object!\nChildInputType!', | ||
description: 'nesting a whole object!\n\nChildInputType!', | ||
}, | ||
exampleList: { | ||
type: 'array', | ||
type: ['array', 'null'], | ||
items: { | ||
$ref: '#/definitions/ChildInputType', | ||
description: 'ChildInputType', | ||
oneOf: [ | ||
{ $ref: '#/definitions/ChildInputType' }, | ||
{ type: 'null' }, | ||
], | ||
}, | ||
description: 'list type with default\n[ChildInputType]', | ||
description: 'list type with default\n\n[ChildInputType]', | ||
default: [ | ||
@@ -124,3 +330,3 @@ { | ||
items: { | ||
type: 'string', | ||
type: ['string', 'null'], | ||
description: 'String', | ||
@@ -143,4 +349,4 @@ }, | ||
favoriteBook: { | ||
type: 'string', | ||
description: 'favorite book\nString', | ||
type: ['string', 'null'], | ||
description: 'favorite book\n\nString', | ||
default: 'Where the wild things are', | ||
@@ -159,3 +365,3 @@ }, | ||
schema, | ||
parse(`query($input: InputType!, $anotherInput: InputType, $episode: Episode) { | ||
parse(`query($input: InputType!, $anotherInput: InputType, $episode: Episode, $anotherEpisode: Episode!) { | ||
characters { | ||
@@ -171,3 +377,3 @@ name | ||
expect(jsonSchema.required).toEqual(['input']); | ||
expect(jsonSchema.required).toEqual(['input', 'anotherEpisode']); | ||
@@ -177,18 +383,20 @@ expect(jsonSchema.properties).toEqual({ | ||
$ref: '#/definitions/InputType', | ||
description: 'InputType!', | ||
markdownDescription: mdTicks('InputType!'), | ||
description: 'InputType!\nexample input type', | ||
markdownDescription: '```graphql\nInputType!\n```\nexample input type', | ||
}, | ||
anotherInput: { | ||
$ref: '#/definitions/InputType', | ||
// description: 'example input type', | ||
// TODO: fix this for non-nulls? | ||
description: 'example input type\nInputType', | ||
markdownDescription: 'example input type\n```graphql\nInputType\n```', | ||
oneOf: [{ $ref: '#/definitions/InputType' }, { type: 'null' }], | ||
description: 'InputType\nexample input type', | ||
markdownDescription: '```graphql\nInputType\n```\nexample input type', | ||
}, | ||
episode: { | ||
enum: ['NEWHOPE', 'EMPIRE', 'JEDI'], | ||
enum: ['NEWHOPE', 'EMPIRE', 'JEDI', null], | ||
description: 'Episode', | ||
type: 'string', | ||
markdownDescription: mdTicks('Episode'), | ||
}, | ||
anotherEpisode: { | ||
enum: ['NEWHOPE', 'EMPIRE', 'JEDI'], | ||
description: 'Episode!', | ||
markdownDescription: mdTicks('Episode!'), | ||
}, | ||
}); | ||
@@ -198,19 +406,19 @@ expect(jsonSchema.definitions).toEqual({ | ||
type: 'object', | ||
description: 'example input type\nInputType', | ||
markdownDescription: `example input type\n${mdTicks('InputType')}`, | ||
description: 'InputType\nexample input type', | ||
markdownDescription: `${mdTicks('InputType')}\nexample input type`, | ||
properties: { | ||
key: { | ||
description: 'example key\nString!', | ||
markdownDescription: `example key\n${mdTicks('String!')}`, | ||
description: 'example key\n\nString!', | ||
markdownDescription: `example key\n\n${mdTicks('String!')}`, | ||
type: 'string', | ||
}, | ||
value: { | ||
description: 'example value\nInt', | ||
markdownDescription: `example value\n${mdTicks('Int')}`, | ||
type: 'integer', | ||
description: 'example value\n\nInt', | ||
markdownDescription: `example value\n\n${mdTicks('Int')}`, | ||
type: ['integer', 'null'], | ||
default: 42, | ||
}, | ||
exampleObject: { | ||
description: 'nesting a whole object!\nChildInputType!', | ||
markdownDescription: `nesting a whole object!\n${mdTicks( | ||
description: 'nesting a whole object!\n\nChildInputType!', | ||
markdownDescription: `nesting a whole object!\n\n${mdTicks( | ||
'ChildInputType!', | ||
@@ -221,8 +429,13 @@ )}`, | ||
exampleList: { | ||
type: 'array', | ||
type: ['array', 'null'], | ||
items: { | ||
$ref: '#/definitions/ChildInputType', | ||
description: 'ChildInputType', | ||
markdownDescription: '```graphql\nChildInputType\n```', | ||
oneOf: [ | ||
{ $ref: '#/definitions/ChildInputType' }, | ||
{ type: 'null' }, | ||
], | ||
}, | ||
description: 'list type with default\n[ChildInputType]', | ||
markdownDescription: `list type with default\n${mdTicks( | ||
description: 'list type with default\n\n[ChildInputType]', | ||
markdownDescription: `list type with default\n\n${mdTicks( | ||
'[ChildInputType]', | ||
@@ -242,3 +455,3 @@ )}`, | ||
items: { | ||
type: 'string', | ||
type: ['string', 'null'], | ||
description: 'String', | ||
@@ -258,5 +471,5 @@ markdownDescription: mdTicks('String'), | ||
default: 'Where the wild things are', | ||
description: 'favorite book\nString', | ||
markdownDescription: 'favorite book\n```graphql\nString\n```', | ||
type: 'string', | ||
description: 'favorite book\n\nString', | ||
markdownDescription: 'favorite book\n\n```graphql\nString\n```', | ||
type: ['string', 'null'], | ||
}, | ||
@@ -263,0 +476,0 @@ isChild: { |
@@ -10,3 +10,3 @@ /** | ||
import { Location } from 'graphql/language'; | ||
import { Location } from 'graphql'; | ||
import { Range, Position, offsetToPosition, locToRange } from '../Range'; | ||
@@ -13,0 +13,0 @@ |
@@ -10,6 +10,4 @@ /** | ||
import { ASTNode } from 'graphql/language'; | ||
import { IPosition as TPosition } from '../types'; | ||
import { visit } from 'graphql'; | ||
import { ASTNode, visit } from 'graphql'; | ||
@@ -16,0 +14,0 @@ export function getASTNodeAtPosition( |
@@ -46,2 +46,6 @@ /** | ||
useMarkdownDescription?: boolean; | ||
/** | ||
* Scalar schema mappings. | ||
*/ | ||
scalarSchemas?: Record<string, JSONSchema6>; | ||
}; | ||
@@ -88,2 +92,40 @@ type JSONSchemaRunningOptions = JSONSchemaOptions & { | ||
function renderDefinitionDescription( | ||
t: GraphQLInputType | GraphQLInputField, | ||
useMarkdown?: boolean, | ||
description?: string | undefined | null, | ||
) { | ||
const into: string[] = []; | ||
const type = 'type' in t ? t.type : t; | ||
// input field description | ||
if ('type' in t && t.description) { | ||
text(into, t.description); | ||
text(into, '\n\n'); | ||
} | ||
// type | ||
text(into, renderTypeToString(type, useMarkdown)); | ||
// type description | ||
if (description) { | ||
text(into, '\n'); | ||
text(into, description); | ||
} else if (!isScalarType(type) && 'description' in type && type.description) { | ||
text(into, '\n'); | ||
text(into, type.description); | ||
} else if ( | ||
'ofType' in type && | ||
!isScalarType(type.ofType) && | ||
'description' in type.ofType && | ||
type.ofType.description | ||
) { | ||
text(into, '\n'); | ||
text(into, type.ofType.description); | ||
} | ||
return into.join(''); | ||
} | ||
function renderTypeToString( | ||
@@ -104,10 +146,10 @@ t: GraphQLInputType | GraphQLInputField, | ||
const scalarTypesMap: { [key: string]: JSONSchema6TypeName } = { | ||
Int: 'integer', | ||
String: 'string', | ||
Float: 'number', | ||
ID: 'string', | ||
Boolean: 'boolean', | ||
const defaultScalarTypesMap: { [key: string]: JSONSchema6 } = { | ||
Int: { type: 'integer' }, | ||
String: { type: 'string' }, | ||
Float: { type: 'number' }, | ||
ID: { type: 'string' }, | ||
Boolean: { type: 'boolean' }, | ||
// { "type": "string", "format": "date" } is not compatible with proposed DateTime GraphQL-Scalars.com spec | ||
DateTime: 'string', | ||
DateTime: { type: 'string' }, | ||
}; | ||
@@ -134,45 +176,61 @@ | ||
function getJSONSchemaFromGraphQLType( | ||
type: GraphQLInputType | GraphQLInputField, | ||
fieldOrType: GraphQLInputType | GraphQLInputField, | ||
options?: JSONSchemaRunningOptions, | ||
): DefinitionResult { | ||
let required = false; | ||
let definition: CombinedSchema = Object.create(null); | ||
const definitions: Definitions = Object.create(null); | ||
// TODO: test that this works? | ||
if ('defaultValue' in type && type.defaultValue !== undefined) { | ||
definition.default = type.defaultValue as JSONSchema4Type | undefined; | ||
} | ||
if (isEnumType(type)) { | ||
definition.type = 'string'; | ||
definition.enum = type.getValues().map(val => val.name); | ||
} | ||
// field or type | ||
const isField = 'type' in fieldOrType; | ||
// type | ||
const type = isField ? fieldOrType.type : fieldOrType; | ||
// base type | ||
const baseType = isNonNullType(type) ? type.ofType : type; | ||
const required = isNonNullType(type); | ||
if (isScalarType(type) && scalarTypesMap[type.name]) { | ||
definition.type = scalarTypesMap[type.name]; | ||
} | ||
if (isListType(type)) { | ||
definition.type = 'array'; | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType( | ||
type.ofType, | ||
options, | ||
); | ||
if (def.$ref) { | ||
definition.items = { $ref: def.$ref }; | ||
if (isScalarType(baseType)) { | ||
// scalars | ||
if (options?.scalarSchemas?.[baseType.name]) { | ||
// deep clone | ||
definition = JSON.parse( | ||
JSON.stringify(options.scalarSchemas[baseType.name]), | ||
); | ||
} else { | ||
definition.items = def; | ||
// any | ||
definition.type = ['string', 'number', 'boolean', 'integer']; | ||
} | ||
if (defs) { | ||
for (const defName of Object.keys(defs)) { | ||
definitions[defName] = defs[defName]; | ||
if (!required) { | ||
if (Array.isArray(definition.type)) { | ||
definition.type.push('null'); | ||
} else if (definition.type) { | ||
definition.type = [definition.type, 'null']; | ||
} else if (definition.enum) { | ||
definition.enum.push(null); | ||
} else if (definition.oneOf) { | ||
definition.oneOf.push({ type: 'null' }); | ||
} else { | ||
definition = { | ||
oneOf: [definition, { type: 'null' }], | ||
}; | ||
} | ||
} | ||
} | ||
if (isNonNullType(type)) { | ||
required = true; | ||
} else if (isEnumType(baseType)) { | ||
definition.enum = baseType.getValues().map(val => val.name); | ||
if (!required) { | ||
definition.enum.push(null); | ||
} | ||
} else if (isListType(baseType)) { | ||
if (required) { | ||
definition.type = 'array'; | ||
} else { | ||
definition.type = ['array', 'null']; | ||
} | ||
const { definition: def, definitions: defs } = getJSONSchemaFromGraphQLType( | ||
type.ofType, | ||
baseType.ofType, | ||
options, | ||
); | ||
definition = def; | ||
definition.items = def; | ||
if (defs) { | ||
@@ -183,7 +241,13 @@ for (const defName of Object.keys(defs)) { | ||
} | ||
} | ||
if (isInputObjectType(type)) { | ||
definition.$ref = `#/definitions/${type.name}`; | ||
if (options?.definitionMarker.mark(type.name)) { | ||
const fields = type.getFields(); | ||
} else if (isInputObjectType(baseType)) { | ||
if (required) { | ||
definition.$ref = `#/definitions/${baseType.name}`; | ||
} else { | ||
definition.oneOf = [ | ||
{ $ref: `#/definitions/${baseType.name}` }, | ||
{ type: 'null' }, | ||
]; | ||
} | ||
if (options?.definitionMarker?.mark(baseType.name)) { | ||
const fields = baseType.getFields(); | ||
@@ -195,16 +259,10 @@ const fieldDef: PropertiedJSON6 = { | ||
}; | ||
if (type.description) { | ||
fieldDef.description = | ||
type.description + '\n' + renderTypeToString(type); | ||
if (options?.useMarkdownDescription) { | ||
// @ts-expect-error | ||
fieldDef.markdownDescription = | ||
type.description + '\n' + renderTypeToString(type, true); | ||
} | ||
} else { | ||
fieldDef.description = renderTypeToString(type); | ||
if (options?.useMarkdownDescription) { | ||
// @ts-expect-error | ||
fieldDef.markdownDescription = renderTypeToString(type, true); | ||
} | ||
fieldDef.description = renderDefinitionDescription(baseType); | ||
if (options?.useMarkdownDescription) { | ||
// @ts-expect-error | ||
fieldDef.markdownDescription = renderDefinitionDescription( | ||
baseType, | ||
true, | ||
); | ||
} | ||
@@ -216,30 +274,8 @@ | ||
required: fieldRequired, | ||
definition: typeDefinition, | ||
definition: fieldDefinition, | ||
definitions: typeDefinitions, | ||
} = getJSONSchemaFromGraphQLType(field.type, options); | ||
const { | ||
definition: fieldDefinition, | ||
// definitions: fieldDefinitions, | ||
} = getJSONSchemaFromGraphQLType(field, options); | ||
fieldDef.properties[fieldName] = { | ||
...typeDefinition, | ||
...fieldDefinition, | ||
} as JSONSchema6; | ||
fieldDef.properties[fieldName] = fieldDefinition; | ||
const renderedField = renderTypeToString(field.type); | ||
fieldDef.properties[fieldName].description = field.description | ||
? field.description + '\n' + renderedField | ||
: renderedField; | ||
if (options?.useMarkdownDescription) { | ||
const renderedFieldMarkdown = renderTypeToString(field.type, true); | ||
fieldDef.properties[ | ||
fieldName | ||
// @ts-expect-error | ||
].markdownDescription = field.description | ||
? field.description + '\n' + renderedFieldMarkdown | ||
: renderedFieldMarkdown; | ||
} | ||
if (fieldRequired) { | ||
@@ -254,26 +290,28 @@ fieldDef.required!.push(fieldName); | ||
} | ||
definitions[type.name] = fieldDef; | ||
definitions[baseType.name] = fieldDef; | ||
} | ||
} | ||
// append descriptions | ||
if ( | ||
'description' in type && | ||
!isScalarType(type) && | ||
type.description && | ||
!definition.description | ||
) { | ||
definition.description = type.description + '\n' + renderTypeToString(type); | ||
if (options?.useMarkdownDescription) { | ||
// @ts-expect-error | ||
definition.markdownDescription = | ||
type.description + '\n' + renderTypeToString(type, true); | ||
} | ||
} else { | ||
definition.description = renderTypeToString(type); | ||
if (options?.useMarkdownDescription) { | ||
// @ts-expect-error | ||
definition.markdownDescription = renderTypeToString(type, true); | ||
} | ||
if ('defaultValue' in fieldOrType && fieldOrType.defaultValue !== undefined) { | ||
definition.default = fieldOrType.defaultValue as | ||
| JSONSchema4Type | ||
| undefined; | ||
} | ||
// append to type descriptions, or schema description | ||
const { description } = definition; | ||
definition.description = renderDefinitionDescription( | ||
fieldOrType, | ||
false, | ||
description, | ||
); | ||
if (options?.useMarkdownDescription) { | ||
// @ts-expect-error | ||
definition.markdownDescription = renderDefinitionDescription( | ||
fieldOrType, | ||
true, | ||
description, | ||
); | ||
} | ||
return { required, definition, definitions }; | ||
@@ -324,3 +362,7 @@ } | ||
const jsonSchema: PropertiedJSON6 = { | ||
$schema: 'https://json-schema.org/draft/2020-12/schema', | ||
// this gets monaco-json validation working again | ||
// otherwise it shows an error for newer schema draft versions | ||
// variables and graphql types are simple and compatible with all versions of json schema | ||
// since draft 4. package.json and many other schemas still use draft 4 | ||
$schema: 'http://json-schema.org/draft-04/schema', | ||
type: 'object', | ||
@@ -334,2 +376,6 @@ properties: {}, | ||
definitionMarker: new Marker(), | ||
scalarSchemas: { | ||
...defaultScalarTypesMap, | ||
...options?.scalarSchemas, | ||
}, | ||
}; | ||
@@ -336,0 +382,0 @@ |
@@ -10,3 +10,3 @@ /** | ||
import { Location } from 'graphql/language'; | ||
import { Location } from 'graphql'; | ||
import { IRange, IPosition } from '../types'; | ||
@@ -13,0 +13,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
772122
14688
0
245