codemirror-graphql
Advanced tools
Comparing version 0.2.1 to 0.2.2
141
hint.js
@@ -18,6 +18,8 @@ /** | ||
var _graphqlType = require('graphql/type'); | ||
var _graphql = require('graphql'); | ||
var _graphqlTypeIntrospection = require('graphql/type/introspection'); | ||
require('./mode'); | ||
/** | ||
@@ -75,3 +77,3 @@ * Registers a "hint" helper for CodeMirror. | ||
} | ||
if (_graphqlType.isAbstractType(typeInfo.parentType)) { | ||
if (_graphql.isAbstractType(typeInfo.parentType)) { | ||
fields.push(_graphqlTypeIntrospection.TypeNameMetaFieldDef); | ||
@@ -124,4 +126,4 @@ } | ||
if (kind === 'EnumValue' || kind === 'ListValue' && step === 1 || kind === 'ObjectField' && step === 2 || kind === 'Argument' && step === 2) { | ||
var namedInputType = _graphqlType.getNamedType(typeInfo.inputType); | ||
if (namedInputType instanceof _graphqlType.GraphQLEnumType) { | ||
var namedInputType = _graphql.getNamedType(typeInfo.inputType); | ||
if (namedInputType instanceof _graphql.GraphQLEnumType) { | ||
var valueMap = namedInputType.getValues(); | ||
@@ -138,4 +140,4 @@ var values = Object.keys(valueMap).map(function (valueName) { | ||
})); | ||
} else if (namedInputType === _graphqlType.GraphQLBoolean) { | ||
return hintList(editor, options, cur, token, [{ text: 'true', type: _graphqlType.GraphQLBoolean, description: 'Not false.' }, { text: 'false', type: _graphqlType.GraphQLBoolean, description: 'Not true.' }]); | ||
} else if (namedInputType === _graphql.GraphQLBoolean) { | ||
return hintList(editor, options, cur, token, [{ text: 'true', type: _graphql.GraphQLBoolean, description: 'Not false.' }, { text: 'false', type: _graphql.GraphQLBoolean, description: 'Not true.' }]); | ||
} | ||
@@ -148,8 +150,10 @@ } | ||
if (typeInfo.parentType) { | ||
possibleTypes = _graphqlType.isAbstractType(typeInfo.parentType) ? typeInfo.parentType.getPossibleTypes() : [typeInfo.parentType]; | ||
possibleTypes = _graphql.isAbstractType(typeInfo.parentType) ? typeInfo.parentType.getPossibleTypes() : [typeInfo.parentType]; | ||
} else { | ||
var typeMap = schema.getTypeMap(); | ||
possibleTypes = Object.keys(typeMap).map(function (typeName) { | ||
return typeMap[typeName]; | ||
}).filter(_graphqlType.isCompositeType); | ||
(function () { | ||
var typeMap = schema.getTypeMap(); | ||
possibleTypes = Object.keys(typeMap).map(function (typeName) { | ||
return typeMap[typeName]; | ||
}).filter(_graphql.isCompositeType); | ||
})(); | ||
} | ||
@@ -164,2 +168,35 @@ return hintList(editor, options, cur, token, possibleTypes.map(function (type) { | ||
// Fragment spread names | ||
if (kind === 'FragmentSpread' && step === 1) { | ||
var _ret2 = (function () { | ||
var typeMap = schema.getTypeMap(); | ||
var defState = getDefinitionState(token.state); | ||
var fragments = getFragmentDefinitions(editor); | ||
// Filter down to only the fragments which may exist here. | ||
var relevantFrags = fragments.filter(function (frag) { | ||
return( | ||
// Only include fragments with known types. | ||
typeMap[frag.typeCondition.name.value] && | ||
// Only include fragments which are not cyclic. | ||
!(defState && defState.kind === 'FragmentDefinition' && defState.name === frag.name.value) && | ||
// Only include fragments which could possibly be spread here. | ||
_graphql.doTypesOverlap(typeInfo.parentType, typeMap[frag.typeCondition.name.value]) | ||
); | ||
}); | ||
return { | ||
v: hintList(editor, options, cur, token, relevantFrags.map(function (frag) { | ||
return { | ||
text: frag.name.value, | ||
type: typeMap[frag.typeCondition.name.value], | ||
description: 'fragment ' + frag.name.value + ' on ' + frag.typeCondition.name.value | ||
}; | ||
})) | ||
}; | ||
})(); | ||
if (typeof _ret2 === 'object') return _ret2.v; | ||
} | ||
// Variable definition types | ||
@@ -170,3 +207,3 @@ if (kind === 'VariableDefinition' && step === 2 || kind === 'ListType' && step === 1 || kind === 'NamedType' && (state.prevState.kind === 'VariableDefinition' || state.prevState.kind === 'ListType')) { | ||
return inputTypeMap[typeName]; | ||
}).filter(_graphqlType.isInputType); | ||
}).filter(_graphql.isInputType); | ||
return hintList(editor, options, cur, token, inputTypes.map(function (type) { | ||
@@ -210,3 +247,4 @@ return { | ||
switch (state.kind) { | ||
case 'Query':case 'ShortQuery': | ||
case 'Query': | ||
case 'ShortQuery': | ||
info.type = schema.getQueryType(); | ||
@@ -222,3 +260,5 @@ break; | ||
case 'FragmentDefinition': | ||
info.type = state.type && schema.getType(state.type); | ||
if (state.type) { | ||
info.type = schema.getType(state.type); | ||
} | ||
break; | ||
@@ -230,3 +270,3 @@ case 'Field': | ||
case 'SelectionSet': | ||
info.parentType = _graphqlType.getNamedType(info.type); | ||
info.parentType = _graphql.getNamedType(info.type); | ||
break; | ||
@@ -252,8 +292,8 @@ case 'Directive': | ||
case 'ListValue': | ||
var nullableType = _graphqlType.getNullableType(info.inputType); | ||
info.inputType = nullableType instanceof _graphqlType.GraphQLList ? nullableType.ofType : null; | ||
var nullableType = _graphql.getNullableType(info.inputType); | ||
info.inputType = nullableType instanceof _graphql.GraphQLList ? nullableType.ofType : null; | ||
break; | ||
case 'ObjectValue': | ||
var objectType = _graphqlType.getNamedType(info.inputType); | ||
info.objectFieldDefs = objectType instanceof _graphqlType.GraphQLInputObjectType ? objectType.getFields() : null; | ||
var objectType = _graphql.getNamedType(info.inputType); | ||
info.objectFieldDefs = objectType instanceof _graphql.GraphQLInputObjectType ? objectType.getFields() : null; | ||
break; | ||
@@ -270,2 +310,22 @@ case 'ObjectField': | ||
// Utility for returning the state representing the Definition this token state | ||
// is within, if any. | ||
function getDefinitionState(tokenState) { | ||
var definitionState = undefined; | ||
forEachState(tokenState, function (state) { | ||
switch (state.kind) { | ||
case 'Query': | ||
case 'ShortQuery': | ||
case 'Mutation': | ||
case 'Subscription': | ||
case 'FragmentDefinition': | ||
definitionState = state; | ||
break; | ||
} | ||
}); | ||
return definitionState; | ||
} | ||
// Utility for iterating through a state stack bottom-up. | ||
@@ -284,2 +344,41 @@ function forEachState(stack, fn) { | ||
// Finds all fragment definition ASTs in a source. | ||
function getFragmentDefinitions(editor) { | ||
var fragmentDefs = []; | ||
runMode(editor, 'graphql', function (state) { | ||
if (state.kind === 'FragmentDefinition' && state.name && state.type) { | ||
fragmentDefs.push({ | ||
kind: 'FragmentDefinition', | ||
name: { | ||
kind: 'Name', | ||
value: state.name | ||
}, | ||
typeCondition: { | ||
kind: 'NamedType', | ||
name: { | ||
kind: 'Name', | ||
value: state.type | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
return fragmentDefs; | ||
} | ||
// Utility for efficiently running a codemirror mode over an editor's current | ||
// state, calling an iterFn function on each token. | ||
function runMode(editor, modeSpec, iterFn) { | ||
var mode = _codemirror2['default'].getMode(_codemirror2['default'].defaults, modeSpec); | ||
var state = _codemirror2['default'].startState(mode); | ||
editor.eachLine(function (line) { | ||
var stream = new _codemirror2['default'].StringStream(line.text); | ||
while (!stream.eol()) { | ||
var style = mode.token(stream, state); | ||
iterFn(state, style, stream); | ||
stream.start = stream.pos; | ||
} | ||
}); | ||
} | ||
// Gets the field definition given a type and field name | ||
@@ -293,3 +392,3 @@ function getFieldDef(schema, type, fieldName) { | ||
} | ||
if (fieldName === _graphqlTypeIntrospection.TypeNameMetaFieldDef.name && _graphqlType.isCompositeType(type)) { | ||
if (fieldName === _graphqlTypeIntrospection.TypeNameMetaFieldDef.name && _graphql.isCompositeType(type)) { | ||
return _graphqlTypeIntrospection.TypeNameMetaFieldDef; | ||
@@ -309,3 +408,3 @@ } | ||
var tokenStart = token.type === null ? token.end : /\w/.test(token.string[0]) ? token.start : token.start + 1; | ||
var tokenStart = token.type !== null && /\w/.test(token.string[0]) ? token.start : token.end; | ||
@@ -312,0 +411,0 @@ var results = { |
@@ -18,6 +18,4 @@ /** | ||
var _graphqlLanguage = require('graphql/language'); | ||
var _graphql = require('graphql'); | ||
var _graphqlValidation = require('graphql/validation'); | ||
/** | ||
@@ -38,3 +36,3 @@ * Registers a "lint" helper for CodeMirror. | ||
try { | ||
var ast = _graphqlLanguage.parse(text); | ||
var ast = _graphql.parse(text); | ||
} catch (error) { | ||
@@ -52,3 +50,3 @@ var location = error.locations[0]; | ||
} | ||
var errors = schema ? _graphqlValidation.validate(schema, ast) : []; | ||
var errors = schema ? _graphql.validate(schema, ast) : []; | ||
return mapCat(errors, function (error) { | ||
@@ -55,0 +53,0 @@ return errorAnnotations(editor, error); |
15
mode.js
@@ -238,2 +238,13 @@ /** | ||
// An constraint described as `but not` in the GraphQL spec. | ||
function butNot(rule, exclusions) { | ||
var ruleMatch = rule.match; | ||
rule.match = function (token) { | ||
return ruleMatch(token) && exclusions.every(function (exclusion) { | ||
return !exclusion.match(token); | ||
}); | ||
}; | ||
return rule; | ||
} | ||
// An optional rule. | ||
@@ -351,3 +362,3 @@ function opt(ofRule) { | ||
Selection: function Selection(token, stream) { | ||
return token.value === '...' ? stream.match(/[\s\u00a0,]*on\b/, false) ? 'InlineFragment' : 'FragmentSpread' : stream.match(/[\s\u00a0,]*:/, false) ? 'AliasedField' : 'Field'; | ||
return token.value === '...' ? stream.match(/[\s\u00a0,]*(on\b|@|{)/, false) ? 'InlineFragment' : 'FragmentSpread' : stream.match(/[\s\u00a0,]*:/, false) ? 'AliasedField' : 'Field'; | ||
}, | ||
@@ -361,3 +372,3 @@ // Note: this minor deviation of "AliasedField" simplifies the lookahead. | ||
InlineFragment: [p('...'), opt('TypeCondition'), list('Directive'), 'SelectionSet'], | ||
FragmentDefinition: [word('fragment'), name('def'), 'TypeCondition', list('Directive'), 'SelectionSet'], | ||
FragmentDefinition: [word('fragment'), opt(butNot(name('def'), [word('on')])), 'TypeCondition', list('Directive'), 'SelectionSet'], | ||
TypeCondition: [word('on'), type('atom')], | ||
@@ -364,0 +375,0 @@ // Variables could be parsed in cases where only Const is expected by spec. |
{ | ||
"name": "codemirror-graphql", | ||
"version": "0.2.1", | ||
"version": "0.2.2", | ||
"description": "GraphQL mode and helpers for CodeMirror.", | ||
@@ -46,6 +46,6 @@ "contributors": [ | ||
"preversion": ". ./resources/checkgit.sh && npm test", | ||
"prepublish": "npm run build" | ||
"prepublish": ". ./resources/prepublish.sh" | ||
}, | ||
"peerDependencies": { | ||
"graphql": "^0.4.8", | ||
"graphql": "^0.4.16", | ||
"codemirror": "^5.6.0" | ||
@@ -56,13 +56,13 @@ }, | ||
"babel-core": "5.8.23", | ||
"babel-eslint": "4.0.10", | ||
"chai": "3.2.0", | ||
"chai-subset": "1.0.1", | ||
"babel-eslint": "4.1.8", | ||
"chai": "3.5.0", | ||
"chai-subset": "1.2.0", | ||
"codemirror": "5.6.0", | ||
"eslint": "1.2.1", | ||
"flow-bin": "0.16.0", | ||
"graphql": "0.4.8", | ||
"jsdom": "6.5.1", | ||
"mocha": "2.2.5", | ||
"sane": "1.3.0" | ||
"eslint": "1.10.3", | ||
"flow-bin": "0.21.0", | ||
"graphql": "0.4.16", | ||
"jsdom": "8.0.2", | ||
"mocha": "2.4.5", | ||
"sane": "1.3.1" | ||
} | ||
} |
GraphQL mode for CodeMirror | ||
=========================== | ||
[![Build Status](https://travis-ci.org/graphql/codemirror-graphql.svg?branch=master)](https://travis-ci.org/graphql/codemirror-graphql) | ||
Provides CodeMirror with a parser mode for GraphQL along with a live linter and | ||
@@ -12,3 +14,3 @@ typeahead hinter powered by your GraphQL Schema. | ||
``` | ||
npm install --save codemirror-graphiql | ||
npm install --save codemirror-graphql | ||
``` | ||
@@ -15,0 +17,0 @@ |
35378
864
41