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

resolve introspection from url (#26)

+5
.changeset/five-dancers-refuse.md
···
···
+
---
+
'@0no-co/graphqlsp': minor
+
---
+
+
Add ability to specify a URL for your schema, GraphQLSP will then fetch the introspection from the specified URL
+174 -63
example/src/index.generated.ts
···
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
-
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
-
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
-
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
···
Float: number;
};
/** Move a Pokémon can perform with the associated damage and type. */
export type Attack = {
__typename?: 'Attack';
-
damage?: Maybe<Scalars['Int']>;
name?: Maybe<Scalars['String']>;
type?: Maybe<PokemonType>;
-
};
-
-
export type AttacksConnection = {
-
__typename?: 'AttacksConnection';
-
fast?: Maybe<Array<Maybe<Attack>>>;
-
special?: Maybe<Array<Maybe<Attack>>>;
};
/** Requirement that prevents an evolution through regular means of levelling up. */
···
name?: Maybe<Scalars['String']>;
};
export type Pokemon = {
__typename?: 'Pokemon';
-
attacks?: Maybe<AttacksConnection>;
-
/** @deprecated And this is the reason why */
classification?: Maybe<Scalars['String']>;
evolutionRequirements?: Maybe<Array<Maybe<EvolutionRequirement>>>;
-
evolutions?: Maybe<Array<Maybe<Pokemon>>>;
/** Likelihood of an attempt to catch a Pokémon to fail. */
fleeRate?: Maybe<Scalars['Float']>;
-
height?: Maybe<PokemonDimension>;
-
id: Scalars['ID'];
/** Maximum combat power a Pokémon may achieve at max level. */
maxCP?: Maybe<Scalars['Int']>;
/** Maximum health points a Pokémon may achieve at max level. */
maxHP?: Maybe<Scalars['Int']>;
-
name: Scalars['String'];
-
resistant?: Maybe<Array<Maybe<PokemonType>>>;
-
types?: Maybe<Array<Maybe<PokemonType>>>;
-
weaknesses?: Maybe<Array<Maybe<PokemonType>>>;
-
weight?: Maybe<PokemonDimension>;
-
};
-
-
export type PokemonDimension = {
-
__typename?: 'PokemonDimension';
-
maximum?: Maybe<Scalars['String']>;
-
minimum?: Maybe<Scalars['String']>;
};
-
/** Elemental property associated with either a Pokémon or one of their moves. */
-
export type PokemonType =
-
| 'Bug'
-
| 'Dark'
-
| 'Dragon'
-
| 'Electric'
-
| 'Fairy'
-
| 'Fighting'
-
| 'Fire'
-
| 'Flying'
-
| 'Ghost'
-
| 'Grass'
-
| 'Ground'
-
| 'Ice'
-
| 'Normal'
-
| 'Poison'
-
| 'Psychic'
-
| 'Rock'
-
| 'Steel'
-
| 'Water';
-
export type Query = {
__typename?: 'Query';
/** Get a single Pokémon by its ID, a three character long identifier padded with zeroes */
pokemon?: Maybe<Pokemon>;
-
/** List out all Pokémon, optionally in pages */
-
pokemons?: Maybe<Array<Maybe<Pokemon>>>;
};
export type QueryPokemonArgs = {
id: Scalars['ID'];
};
-
export type QueryPokemonsArgs = {
-
limit?: InputMaybe<Scalars['Int']>;
-
skip?: InputMaybe<Scalars['Int']>;
};
-
export type PokemonsQueryVariables = Exact<{ [key: string]: never; }>;
-
-
-
export type PokemonsQuery = { __typename?: 'Query', pokemons?: Array<{ __typename: 'Pokemon', id: string, name: string } | null> | null };
-
-
export type PokemonFieldsFragment = { __typename?: 'Pokemon', id: string, name: string };
export type PokemonQueryVariables = Exact<{
id: Scalars['ID'];
}>;
-
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"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"pokemonFields"}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}}]}},...PokemonFieldsFragmentDoc.definitions]} as unknown as DocumentNode<PokemonsQuery, PokemonsQueryVariables>;
-
export const PokemonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Pokemon"},"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"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}}]}}]} as unknown as DocumentNode<PokemonQuery, PokemonQueryVariables>;
···
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
+
export type Exact<T extends { [key: string]: unknown }> = {
+
[K in keyof T]: T[K];
+
};
+
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
+
[SubKey in K]?: Maybe<T[SubKey]>;
+
};
+
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
+
[SubKey in K]: Maybe<T[SubKey]>;
+
};
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
···
Float: number;
};
+
/** Elemental property associated with either a Pokémon or one of their moves. */
+
export type PokemonType =
+
| 'Grass'
+
| 'Poison'
+
| 'Fire'
+
| 'Flying'
+
| 'Water'
+
| 'Bug'
+
| 'Normal'
+
| 'Electric'
+
| 'Ground'
+
| 'Fairy'
+
| 'Fighting'
+
| 'Psychic'
+
| 'Rock'
+
| 'Steel'
+
| 'Ice'
+
| 'Ghost'
+
| 'Dragon'
+
| 'Dark';
+
/** Move a Pokémon can perform with the associated damage and type. */
export type Attack = {
__typename?: 'Attack';
name?: Maybe<Scalars['String']>;
type?: Maybe<PokemonType>;
+
damage?: Maybe<Scalars['Int']>;
};
/** Requirement that prevents an evolution through regular means of levelling up. */
···
name?: Maybe<Scalars['String']>;
};
+
export type PokemonDimension = {
+
__typename?: 'PokemonDimension';
+
minimum?: Maybe<Scalars['String']>;
+
maximum?: Maybe<Scalars['String']>;
+
};
+
+
export type AttacksConnection = {
+
__typename?: 'AttacksConnection';
+
fast?: Maybe<Array<Maybe<Attack>>>;
+
special?: Maybe<Array<Maybe<Attack>>>;
+
};
+
export type Pokemon = {
__typename?: 'Pokemon';
+
id: Scalars['ID'];
+
name: Scalars['String'];
classification?: Maybe<Scalars['String']>;
+
types?: Maybe<Array<Maybe<PokemonType>>>;
+
resistant?: Maybe<Array<Maybe<PokemonType>>>;
+
weaknesses?: Maybe<Array<Maybe<PokemonType>>>;
evolutionRequirements?: Maybe<Array<Maybe<EvolutionRequirement>>>;
+
weight?: Maybe<PokemonDimension>;
+
height?: Maybe<PokemonDimension>;
+
attacks?: Maybe<AttacksConnection>;
/** Likelihood of an attempt to catch a Pokémon to fail. */
fleeRate?: Maybe<Scalars['Float']>;
/** Maximum combat power a Pokémon may achieve at max level. */
maxCP?: Maybe<Scalars['Int']>;
/** Maximum health points a Pokémon may achieve at max level. */
maxHP?: Maybe<Scalars['Int']>;
+
evolutions?: Maybe<Array<Maybe<Pokemon>>>;
};
export type Query = {
__typename?: 'Query';
+
/** List out all Pokémon, optionally in pages */
+
pokemons?: Maybe<Array<Maybe<Pokemon>>>;
/** Get a single Pokémon by its ID, a three character long identifier padded with zeroes */
pokemon?: Maybe<Pokemon>;
};
+
export type QueryPokemonsArgs = {
+
limit?: InputMaybe<Scalars['Int']>;
+
skip?: InputMaybe<Scalars['Int']>;
+
};
export type QueryPokemonArgs = {
id: Scalars['ID'];
};
+
export type PokemonsQueryVariables = Exact<{ [key: string]: never }>;
+
export type PokemonsQuery = {
+
__typename?: 'Query';
+
pokemons?: Array<{
+
__typename: 'Pokemon';
+
id: string;
+
name: string;
+
} | null> | null;
};
+
export type PokemonFieldsFragment = {
+
__typename?: 'Pokemon';
+
id: string;
+
name: string;
+
};
export type PokemonQueryVariables = Exact<{
id: Scalars['ID'];
}>;
+
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' } },
+
{
+
kind: 'FragmentSpread',
+
name: { kind: 'Name', value: 'pokemonFields' },
+
},
+
{ kind: 'Field', name: { kind: 'Name', value: '__typename' } },
+
],
+
},
+
},
+
],
+
},
+
},
+
...PokemonFieldsFragmentDoc.definitions,
+
],
+
} as unknown as DocumentNode<PokemonsQuery, PokemonsQueryVariables>;
+
export const PokemonDocument = {
+
kind: 'Document',
+
definitions: [
+
{
+
kind: 'OperationDefinition',
+
operation: 'query',
+
name: { kind: 'Name', value: 'Pokemon' },
+
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' } },
+
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
+
{ kind: 'Field', name: { kind: 'Name', value: '__typename' } },
+
],
+
},
+
},
+
],
+
},
+
},
+
],
+
} as unknown as DocumentNode<PokemonQuery, PokemonQueryVariables>;
+12 -10
example/tsconfig.json
···
{
"compilerOptions": {
-
"plugins": [{
-
"name": "plugin",
-
"schema": "./schema.graphql"
-
}],
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
···
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
-
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
···
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
-
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
···
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
-
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
-
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
-
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
···
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
-
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
···
{
"compilerOptions": {
+
"plugins": [
+
{
+
"name": "plugin",
+
"schema": "https://trygql.formidable.dev/graphql/basic-pokedex"
+
}
+
],
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
···
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
+
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
···
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
+
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
···
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
+
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
···
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
+9 -1
example/yarn.lock
···
dependencies:
whatwg-url "^5.0.0"
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
···
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
"plugin@file:..":
-
version "1.0.0"
dependencies:
"@graphql-codegen/core" "^2.6.8"
"@graphql-codegen/typed-document-node" "^2.3.10"
"@graphql-codegen/typescript" "^2.8.5"
"@graphql-codegen/typescript-operations" "^2.5.10"
graphql-language-service "^5.0.6"
promise@^7.1.1:
version "7.3.1"
···
dependencies:
whatwg-url "^5.0.0"
+
node-fetch@^2.0.0:
+
version "2.6.9"
+
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6"
+
integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==
+
dependencies:
+
whatwg-url "^5.0.0"
+
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
···
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
"plugin@file:..":
+
version "0.1.0"
dependencies:
"@graphql-codegen/core" "^2.6.8"
"@graphql-codegen/typed-document-node" "^2.3.10"
"@graphql-codegen/typescript" "^2.8.5"
"@graphql-codegen/typescript-operations" "^2.5.10"
graphql-language-service "^5.0.6"
+
node-fetch "^2.0.0"
promise@^7.1.1:
version "7.3.1"
+3 -1
package.json
···
"@rollup/plugin-terser": "^0.4.1",
"@rollup/plugin-typescript": "^11.1.0",
"@types/node": "^18.15.11",
"dotenv": "^16.0.3",
"graphql": "^16.5.0",
"husky": "^8.0.3",
···
"@graphql-codegen/typed-document-node": "^2.3.10",
"@graphql-codegen/typescript": "^2.8.5",
"@graphql-codegen/typescript-operations": "^2.5.10",
-
"graphql-language-service": "^5.0.6"
}
}
···
"@rollup/plugin-terser": "^0.4.1",
"@rollup/plugin-typescript": "^11.1.0",
"@types/node": "^18.15.11",
+
"@types/node-fetch": "^2.6.3",
"dotenv": "^16.0.3",
"graphql": "^16.5.0",
"husky": "^8.0.3",
···
"@graphql-codegen/typed-document-node": "^2.3.10",
"@graphql-codegen/typescript": "^2.8.5",
"@graphql-codegen/typescript-operations": "^2.5.10",
+
"graphql-language-service": "^5.0.6",
+
"node-fetch": "^2.0.0"
}
}
+50
pnpm-lock.yaml
···
graphql-language-service:
specifier: ^5.0.6
version: 5.0.6(graphql@16.5.0)
devDependencies:
'@changesets/cli':
···
'@types/node':
specifier: ^18.15.11
version: 18.15.11
dotenv:
specifier: ^16.0.3
version: 16.0.3
···
resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==}
dev: true
/@types/node@12.20.55:
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
dev: true
···
/astral-regex@2.0.0:
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
engines: {node: '>=8'}
dev: true
/auto-bind@4.0.0:
···
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
dev: true
/commander@10.0.0:
resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
engines: {node: '>=14'}
···
object-keys: 1.1.1
dev: true
/dependency-graph@0.11.0:
resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==}
engines: {node: '>= 0.6.0'}
···
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies:
is-callable: 1.2.7
dev: true
/fs-extra@7.0.1:
···
dependencies:
braces: 3.0.2
picomatch: 2.3.1
dev: true
/mimic-fn@2.1.0:
···
graphql-language-service:
specifier: ^5.0.6
version: 5.0.6(graphql@16.5.0)
+
node-fetch:
+
specifier: ^2.0.0
+
version: 2.6.7
devDependencies:
'@changesets/cli':
···
'@types/node':
specifier: ^18.15.11
version: 18.15.11
+
'@types/node-fetch':
+
specifier: ^2.6.3
+
version: 2.6.3
dotenv:
specifier: ^16.0.3
version: 16.0.3
···
resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==}
dev: true
+
/@types/node-fetch@2.6.3:
+
resolution: {integrity: sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==}
+
dependencies:
+
'@types/node': 18.15.11
+
form-data: 3.0.1
+
dev: true
+
/@types/node@12.20.55:
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
dev: true
···
/astral-regex@2.0.0:
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
engines: {node: '>=8'}
+
dev: true
+
+
/asynckit@0.4.0:
+
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: true
/auto-bind@4.0.0:
···
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
dev: true
+
/combined-stream@1.0.8:
+
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+
engines: {node: '>= 0.8'}
+
dependencies:
+
delayed-stream: 1.0.0
+
dev: true
+
/commander@10.0.0:
resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
engines: {node: '>=14'}
···
object-keys: 1.1.1
dev: true
+
/delayed-stream@1.0.0:
+
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+
engines: {node: '>=0.4.0'}
+
dev: true
+
/dependency-graph@0.11.0:
resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==}
engines: {node: '>= 0.6.0'}
···
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies:
is-callable: 1.2.7
+
dev: true
+
+
/form-data@3.0.1:
+
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
+
engines: {node: '>= 6'}
+
dependencies:
+
asynckit: 0.4.0
+
combined-stream: 1.0.8
+
mime-types: 2.1.35
dev: true
/fs-extra@7.0.1:
···
dependencies:
braces: 3.0.2
picomatch: 2.3.1
+
dev: true
+
+
/mime-db@1.52.0:
+
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+
engines: {node: '>= 0.6'}
+
dev: true
+
+
/mime-types@2.1.35:
+
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+
engines: {node: '>= 0.6'}
+
dependencies:
+
mime-db: 1.52.0
dev: true
/mimic-fn@2.1.0:
+67 -11
src/getSchema.ts
···
-
import { GraphQLSchema, buildSchema, buildClientSchema } from 'graphql';
import path from 'path';
import fs from 'fs';
export const loadSchema = (
root: string,
-
schema: string
): { current: GraphQLSchema | null } => {
const ref: { current: GraphQLSchema | null } = { current: null };
-
const isJson = schema.endsWith('json');
-
const resolvedPath = path.resolve(path.dirname(root), schema);
-
const contents = fs.readFileSync(resolvedPath, 'utf-8');
-
fs.watchFile(resolvedPath, () => {
const contents = fs.readFileSync(resolvedPath, 'utf-8');
ref.current = isJson
? buildClientSchema(JSON.parse(contents))
: buildSchema(contents);
-
});
-
-
ref.current = isJson
-
? buildClientSchema(JSON.parse(contents))
-
: buildSchema(contents);
return ref;
};
···
+
import {
+
GraphQLSchema,
+
buildSchema,
+
buildClientSchema,
+
getIntrospectionQuery,
+
IntrospectionQuery,
+
} from 'graphql';
+
import fetch from 'node-fetch';
import path from 'path';
import fs from 'fs';
+
import { Logger } from './index';
+
export const loadSchema = (
root: string,
+
schema: string,
+
logger: Logger
): { current: GraphQLSchema | null } => {
const ref: { current: GraphQLSchema | null } = { current: null };
+
let url: URL | undefined;
+
+
try {
+
url = new URL(schema);
+
} catch (e) {}
+
if (url) {
+
logger(`Fetching introspection from ${url.toString()}`);
+
fetch(url.toString(), {
+
method: 'POST',
+
headers: {
+
'Content-Type': 'application/json',
+
},
+
body: JSON.stringify({
+
query: getIntrospectionQuery({
+
descriptions: true,
+
schemaDescription: true,
+
inputValueDeprecation: false,
+
directiveIsRepeatable: false,
+
specifiedByUrl: false,
+
}),
+
}),
+
})
+
.then(response => {
+
logger(`Got response ${response.statusText} ${response.status}`);
+
if (response.ok) return response.json();
+
else return response.text();
+
})
+
.then(result => {
+
if (typeof result === 'string') {
+
logger(`Got error while fetching introspection ${result}`);
+
} else {
+
try {
+
ref.current = buildClientSchema(
+
(result as { data: IntrospectionQuery }).data
+
);
+
logger(`Got schema for ${url!.toString()}`);
+
} catch (e: any) {
+
logger(`Got schema error for ${e.message}`);
+
}
+
}
+
});
+
} else {
+
const isJson = schema.endsWith('json');
+
const resolvedPath = path.resolve(path.dirname(root), schema);
+
logger(`Getting schema from ${resolvedPath}`);
const contents = fs.readFileSync(resolvedPath, 'utf-8');
+
+
fs.watchFile(resolvedPath, () => {
+
const contents = fs.readFileSync(resolvedPath, 'utf-8');
+
ref.current = isJson
+
? buildClientSchema(JSON.parse(contents))
+
: buildSchema(contents);
+
});
+
ref.current = isJson
? buildClientSchema(JSON.parse(contents))
: buildSchema(contents);
+
logger(`Got schema and initialized watcher for ${schema}`);
+
}
return ref;
};
+9 -5
src/index.ts
···
return proxy;
}
function create(info: ts.server.PluginCreateInfo) {
-
const logger = (msg: string) =>
info.project.projectService.logger.info(`[ts-graphql-plugin] ${msg}`);
logger('config: ' + JSON.stringify(info.config));
if (!info.config.schema) {
throw new Error('Please provide a GraphQL Schema!');
}
-
info.project.projectService.logger.info('Setting up the GraphQL Plugin');
const tagTemplate = info.config.template || 'gql';
const scalars = info.config.scalars || {};
const proxy = createBasicDecorator(info);
-
// TODO: check out interesting stuff on ts.factory
-
-
const schema = loadSchema(info.project.getProjectName(), info.config.schema);
proxy.getSemanticDiagnostics = (filename: string): ts.Diagnostic[] => {
const originalDiagnostics =
···
return proxy;
}
+
export type Logger = (msg: string) => void;
+
function create(info: ts.server.PluginCreateInfo) {
+
const logger: Logger = (msg: string) =>
info.project.projectService.logger.info(`[ts-graphql-plugin] ${msg}`);
logger('config: ' + JSON.stringify(info.config));
if (!info.config.schema) {
throw new Error('Please provide a GraphQL Schema!');
}
+
logger('Setting up the GraphQL Plugin');
const tagTemplate = info.config.template || 'gql';
const scalars = info.config.scalars || {};
const proxy = createBasicDecorator(info);
+
const schema = loadSchema(
+
info.project.getProjectName(),
+
info.config.schema,
+
logger
+
);
proxy.getSemanticDiagnostics = (filename: string): ts.Diagnostic[] => {
const originalDiagnostics =