Mirror: TypeScript LSP plugin that finds GraphQL documents in your code and provides diagnostics, auto-complete and hover-information.

only when file is changed

Changed files
+29 -2
example
src
+9 -1
example/src/index.generated.ts
···
export type PokemonQuery = { __typename?: 'Query', pokemon?: { __typename?: 'Pokemon', id: string, name: string } | null };
export const PokemonFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"pokemonFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Pokemon"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode<PokemonFieldsFragment, unknown>;
export const PokemonsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Pokemons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pokemons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<PokemonsQuery, PokemonsQueryVariables>;
-
export const PokemonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Pokemon"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pokemon"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"StringValue","value":"1","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"pokemonFields"}}]}}]}},...PokemonFieldsFragmentDoc.definitions]} as unknown as DocumentNode<PokemonQuery, PokemonQueryVariables>;
···
export type PokemonQuery = { __typename?: 'Query', pokemon?: { __typename?: 'Pokemon', id: string, name: string } | null };
+
export type DonkemonQueryVariables = Exact<{
+
id: Scalars['ID'];
+
}>;
+
+
+
export type DonkemonQuery = { __typename?: 'Query', pokemon?: { __typename?: 'Pokemon', id: string } | null };
+
export const PokemonFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"pokemonFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Pokemon"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode<PokemonFieldsFragment, unknown>;
export const PokemonsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Pokemons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pokemons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<PokemonsQuery, PokemonsQueryVariables>;
+
export const PokemonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Pokemon"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pokemon"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"StringValue","value":"1","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"pokemonFields"}}]}}]}},...PokemonFieldsFragmentDoc.definitions]} as unknown as DocumentNode<PokemonQuery, PokemonQueryVariables>;
+
export const DonkemonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Donkemon"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pokemon"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<DonkemonQuery, DonkemonQueryVariables>;
+8 -1
example/src/index.ts
···
query Pokemons {
pokemons {
id
-
name
}
}
···
${PokemonFields}
` as typeof import('./index.generated').PokemonDocument
const urqlClient = createClient({
url: '',
exchanges: []
···
query Pokemons {
pokemons {
id
+
name
}
}
···
${PokemonFields}
` as typeof import('./index.generated').PokemonDocument
+
const Donkemon = gql`
+
query Donkemon ($id: ID!) {
+
pokemon(id: $id) {
+
id
+
}
+
}
+
` as typeof import('./index.generated').DonkemonDocument
const urqlClient = createClient({
url: '',
exchanges: []
+12
src/index.ts
···
import { isNoSubstitutionTemplateLiteral, ScriptElementKind, isIdentifier, isTaggedTemplateExpression, isToken, isTemplateExpression, isImportTypeNode, ImportTypeNode } from "typescript";
import { getHoverInformation, getAutocompleteSuggestions, getDiagnostics, Diagnostic } from 'graphql-language-service'
import { parse, Kind, FragmentDefinitionNode, OperationDefinitionNode } from 'graphql'
import { Cursor } from "./cursor";
import { loadSchema } from "./getSchema";
···
const nameParts = name.split('.');
nameParts[nameParts.length - 1] = 'generated.ts'
parts[parts.length - 1] = nameParts.join('.')
generateTypedDocumentNodes(schema.current, parts.join('/'), texts.join('\n')).then(() => {
nodes.forEach((node, i) => {
const queryText = texts[i] || '';
const parsed = parse(queryText);
const isFragment = parsed.definitions.every(x => x.kind === Kind.FRAGMENT_DEFINITION);
let name = '';
if (isFragment) {
const fragmentNode = parsed.definitions[0] as FragmentDefinitionNode;
name = fragmentNode.name.value;
···
isGlobalCompletion: false,
isMemberCompletion: false,
isNewIdentifierLocation: false,
entries: [...suggestions.map(suggestion => ({
kind: ScriptElementKind.variableElement,
name: suggestion.label,
···
import { isNoSubstitutionTemplateLiteral, ScriptElementKind, isIdentifier, isTaggedTemplateExpression, isToken, isTemplateExpression, isImportTypeNode, ImportTypeNode } from "typescript";
import { getHoverInformation, getAutocompleteSuggestions, getDiagnostics, Diagnostic } from 'graphql-language-service'
import { parse, Kind, FragmentDefinitionNode, OperationDefinitionNode } from 'graphql'
+
import fs from 'fs';
import { Cursor } from "./cursor";
import { loadSchema } from "./getSchema";
···
const nameParts = name.split('.');
nameParts[nameParts.length - 1] = 'generated.ts'
parts[parts.length - 1] = nameParts.join('.')
+
const contents = fs.readFileSync(filename, 'utf-8');
+
const currentText = source.getFullText();
+
if (contents !== currentText) {
+
return [
+
...newDiagnostics,
+
...originalDiagnostics
+
]
+
}
+
generateTypedDocumentNodes(schema.current, parts.join('/'), texts.join('\n')).then(() => {
nodes.forEach((node, i) => {
const queryText = texts[i] || '';
const parsed = parse(queryText);
const isFragment = parsed.definitions.every(x => x.kind === Kind.FRAGMENT_DEFINITION);
let name = '';
+
if (isFragment) {
const fragmentNode = parsed.definitions[0] as FragmentDefinitionNode;
name = fragmentNode.name.value;
···
isGlobalCompletion: false,
isMemberCompletion: false,
isNewIdentifierLocation: false,
+
// TODO: check whether we can add descriptions to the entries
entries: [...suggestions.map(suggestion => ({
kind: ScriptElementKind.variableElement,
name: suggestion.label,