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

chore(workspaace): add multi schema tests (#307)

+34 -12
pnpm-lock.yaml
···
specifier: ^5.3.3
version: 5.3.3
+
test/e2e/fixture-project-tada-multi-schema:
+
dependencies:
+
'@0no-co/graphqlsp':
+
specifier: workspace:*
+
version: link:../../../packages/graphqlsp
+
'@graphql-typed-document-node/core':
+
specifier: ^3.0.0
+
version: 3.2.0(graphql@16.8.1)
+
'@urql/core':
+
specifier: ^4.0.4
+
version: 4.2.2(graphql@16.8.1)
+
gql.tada:
+
specifier: 1.5.9-canary-8711af177005f46fa3e06d990b6ba28e353e7f9b
+
version: 1.5.9-canary-8711af177005f46fa3e06d990b6ba28e353e7f9b(graphql@16.8.1)(svelte@4.2.15)(typescript@5.3.3)
+
graphql:
+
specifier: ^16.0.0
+
version: 16.8.1
+
devDependencies:
+
typescript:
+
specifier: ^5.3.3
+
version: 5.3.3
+
test/e2e/fixture-project-unused-fields:
dependencies:
'@0no-co/graphqlsp':
···
/@0no-co/graphqlsp@1.11.0(typescript@5.3.3):
resolution: {integrity: sha512-P8DRsT+pRgXXZ+8szO1ISUXLxtaL9ukKddjLqSh+oBvWVCzUDyUM4Une0Co0Y7XC017wI4pdcrR/3hWqw9uuDg==}
peerDependencies:
-
typescript: ^5.0.0
+
typescript: ^5.3.3
dependencies:
'@gql.tada/internal': 0.1.3(graphql@16.8.1)(typescript@5.3.3)
graphql: 16.8.1
···
/@gql.tada/cli-utils@1.2.3-canary-8711af177005f46fa3e06d990b6ba28e353e7f9b(svelte@4.2.15)(typescript@5.3.3):
resolution: {integrity: sha512-tCKCGD4VmdSrnt7wvvzuJ1dtDf0nB6rBlYkOAvgmwDzDQgkWnwP97Xv6t6i1HAWiE9f9uzMl8y1eAr4IbySPrQ==}
peerDependencies:
-
typescript: ^5.0.0
+
typescript: ^5.3.3
dependencies:
'@0no-co/graphqlsp': 1.11.0(typescript@5.3.3)
'@gql.tada/internal': 0.3.0-canary-8711af177005f46fa3e06d990b6ba28e353e7f9b(graphql@16.8.1)(typescript@5.3.3)
···
resolution: {integrity: sha512-wIvykBId7O0gaizmSl5n5AhbQsgJVLTUsFBm3RsfQ9dVfpmT+Fhy2yHX+yNgiVECg2EimXMhs4ltcE4EuZ2WOA==}
peerDependencies:
graphql: ^16.8.1
-
typescript: ^5.0.0
+
typescript: ^5.3.3
dependencies:
'@0no-co/graphql.web': 1.0.6(graphql@16.8.1)
graphql: 16.8.1
···
resolution: {integrity: sha512-CPx00hSOue+XK1KUZhovuN0sZ3sCcUx99Y/iv9obL8ea2rLJ7HbehJ1uI8zwLVtfPoX/tLz+Cc34TFF1znSR7Q==}
peerDependencies:
graphql: ^16.8.1
-
typescript: ^5.0.0
+
typescript: ^5.3.3
dependencies:
'@0no-co/graphql.web': 1.0.6(graphql@16.8.1)
graphql: 16.8.1
···
peerDependencies:
rollup: ^2.14.0||^3.0.0||^4.0.0
tslib: '*'
-
typescript: '>=3.7.0'
+
typescript: ^5.3.3
peerDependenciesMeta:
rollup:
optional: true
···
/@vue/language-core@2.0.14(typescript@5.3.3):
resolution: {integrity: sha512-3q8mHSNcGTR7sfp2X6jZdcb4yt8AjBXAfKk0qkZIh7GAJxOnoZ10h5HToZglw4ToFvAnq+xu/Z2FFbglh9Icag==}
peerDependencies:
-
typescript: '*'
+
typescript: ^5.3.3
peerDependenciesMeta:
typescript:
optional: true
···
resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==}
engines: {node: '>=14'}
peerDependencies:
-
typescript: '>=4.9.5'
+
typescript: ^5.3.3
peerDependenciesMeta:
typescript:
optional: true
···
resolution: {integrity: sha512-3tglGLiGY1zyMyZAow2kpy8GBi35xGAGiEDiPAIPXOUyU35B0HpY0lmMZhf5jEs0fv2FTMgWvFXO6Z3378B6FA==}
hasBin: true
peerDependencies:
-
typescript: ^5.0.0
+
typescript: ^5.3.3
dependencies:
'@0no-co/graphql.web': 1.0.6(graphql@16.8.1)
'@gql.tada/cli-utils': 1.2.3-canary-8711af177005f46fa3e06d990b6ba28e353e7f9b(svelte@4.2.15)(typescript@5.3.3)
···
engines: {node: '>=16'}
peerDependencies:
rollup: ^3.29.4 || ^4
-
typescript: ^4.5 || ^5.0
+
typescript: ^5.3.3
dependencies:
magic-string: 0.30.5
rollup: 4.9.5
···
resolution: {integrity: sha512-HAIxtk5TUHXvCRKApKfxoh1BGT85S/17lS3DvbfxRKFd+Ghr5YScqBvd+sU+p7vJFw48LNkzdFk+ooNVk3e4kA==}
peerDependencies:
svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0
-
typescript: ^4.9.4 || ^5.0.0
+
typescript: ^5.3.3
dependencies:
dedent-js: 1.0.1
pascal-case: 3.1.2
···
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': '*'
-
typescript: '>=2.7'
+
typescript: ^5.3.3
peerDependenciesMeta:
'@swc/core':
optional: true
···
id: file:packages/graphqlsp
name: '@0no-co/graphqlsp'
peerDependencies:
-
typescript: ^5.0.0
+
typescript: ^5.3.3
dependencies:
'@gql.tada/internal': 0.3.0-canary-8711af177005f46fa3e06d990b6ba28e353e7f9b(graphql@16.8.1)(typescript@5.3.3)
graphql: 16.8.1
+3
test/e2e/fixture-project-tada-multi-schema/.vscode/settings.json
···
+
{
+
"typescript.tsdk": "node_modules/typescript/lib"
+
}
+9
test/e2e/fixture-project-tada-multi-schema/fixtures/pokemon.ts
···
+
import { initGraphQLTada } from 'gql.tada';
+
import type { introspection } from '../pokemons';
+
+
export const graphql = initGraphQLTada<{
+
introspection: introspection;
+
}>();
+
+
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';
+
export { readFragment } from 'gql.tada';
+15
test/e2e/fixture-project-tada-multi-schema/fixtures/simple-pokemon.ts
···
+
import { graphql } from './pokemon';
+
+
// prettier-ignore
+
const x = graphql(`
+
query Pokemons($limit: Int!) {
+
pokemons(limit: $limit) {
+
id
+
name
+
+
fleeRate
+
classification
+
__typename
+
}
+
}
+
`);
+11
test/e2e/fixture-project-tada-multi-schema/fixtures/simple-todo.ts
···
+
import { graphql } from './todo';
+
+
// prettier-ignore
+
const x = graphql(`
+
query Todo($id: ID!) {
+
todo(id: $id) {
+
id
+
+
}
+
}
+
`);
+9
test/e2e/fixture-project-tada-multi-schema/fixtures/todo.ts
···
+
import { initGraphQLTada } from 'gql.tada';
+
import type { introspection } from '../todos';
+
+
export const graphql = initGraphQLTada<{
+
introspection: introspection;
+
}>();
+
+
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';
+
export { readFragment } from 'gql.tada';
+14
test/e2e/fixture-project-tada-multi-schema/package.json
···
+
{
+
"name": "fixtures",
+
"private": true,
+
"dependencies": {
+
"graphql": "^16.0.0",
+
"gql.tada": "1.5.9-canary-8711af177005f46fa3e06d990b6ba28e353e7f9b",
+
"@graphql-typed-document-node/core": "^3.0.0",
+
"@0no-co/graphqlsp": "workspace:*",
+
"@urql/core": "^4.0.4"
+
},
+
"devDependencies": {
+
"typescript": "^5.3.3"
+
}
+
}
+9
test/e2e/fixture-project-tada-multi-schema/pokemon.ts
···
+
import { initGraphQLTada } from 'gql.tada';
+
import type { introspection } from './pokemons';
+
+
export const graphql = initGraphQLTada<{
+
introspection: introspection;
+
}>();
+
+
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';
+
export { readFragment } from 'gql.tada';
+33
test/e2e/fixture-project-tada-multi-schema/pokemons.d.ts
···
+
/* eslint-disable */
+
/* prettier-ignore */
+
+
/** An IntrospectionQuery representation of your schema.
+
*
+
* @remarks
+
* This is an introspection of your schema saved as a file by GraphQLSP.
+
* It will automatically be used by `gql.tada` to infer the types of your GraphQL documents.
+
* If you need to reuse this data or update your `scalars`, update `tadaOutputLocation` to
+
* instead save to a .ts instead of a .d.ts file.
+
*/
+
export type introspection = {
+
name: 'pokemons';
+
query: 'Query';
+
mutation: never;
+
subscription: never;
+
types: {
+
'Attack': { kind: 'OBJECT'; name: 'Attack'; fields: { 'damage': { name: 'damage'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'type': { name: 'type'; type: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; } }; }; };
+
'AttacksConnection': { kind: 'OBJECT'; name: 'AttacksConnection'; fields: { 'fast': { name: 'fast'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Attack'; ofType: null; }; } }; 'special': { name: 'special'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Attack'; ofType: null; }; } }; }; };
+
'Boolean': unknown;
+
'EvolutionRequirement': { kind: 'OBJECT'; name: 'EvolutionRequirement'; fields: { 'amount': { name: 'amount'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; };
+
'Float': unknown;
+
'ID': unknown;
+
'Int': unknown;
+
'Pokemon': { kind: 'OBJECT'; name: 'Pokemon'; fields: { 'attacks': { name: 'attacks'; type: { kind: 'OBJECT'; name: 'AttacksConnection'; ofType: null; } }; 'classification': { name: 'classification'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'evolutionRequirements': { name: 'evolutionRequirements'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'EvolutionRequirement'; ofType: null; }; } }; 'evolutions': { name: 'evolutions'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Pokemon'; ofType: null; }; } }; 'fleeRate': { name: 'fleeRate'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'height': { name: 'height'; type: { kind: 'OBJECT'; name: 'PokemonDimension'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'maxCP': { name: 'maxCP'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'maxHP': { name: 'maxHP'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'resistant': { name: 'resistant'; type: { kind: 'LIST'; name: never; ofType: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; }; } }; 'types': { name: 'types'; type: { kind: 'LIST'; name: never; ofType: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; }; } }; 'weaknesses': { name: 'weaknesses'; type: { kind: 'LIST'; name: never; ofType: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; }; } }; 'weight': { name: 'weight'; type: { kind: 'OBJECT'; name: 'PokemonDimension'; ofType: null; } }; }; };
+
'PokemonDimension': { kind: 'OBJECT'; name: 'PokemonDimension'; fields: { 'maximum': { name: 'maximum'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'minimum': { name: 'minimum'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; };
+
'PokemonType': { name: 'PokemonType'; enumValues: 'Bug' | 'Dark' | 'Dragon' | 'Electric' | 'Fairy' | 'Fighting' | 'Fire' | 'Flying' | 'Ghost' | 'Grass' | 'Ground' | 'Ice' | 'Normal' | 'Poison' | 'Psychic' | 'Rock' | 'Steel' | 'Water'; };
+
'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'pokemon': { name: 'pokemon'; type: { kind: 'OBJECT'; name: 'Pokemon'; ofType: null; } }; 'pokemons': { name: 'pokemons'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Pokemon'; ofType: null; }; } }; }; };
+
'String': unknown;
+
};
+
};
+
+
import * as gqlTada from 'gql.tada';
+94
test/e2e/fixture-project-tada-multi-schema/pokemons.graphql
···
+
### This file was generated by Nexus Schema
+
### Do not make changes to this file directly
+
+
"""
+
Move a Pokémon can perform with the associated damage and type.
+
"""
+
type Attack {
+
damage: Int
+
name: String
+
type: PokemonType
+
}
+
+
type AttacksConnection {
+
fast: [Attack]
+
special: [Attack]
+
}
+
+
"""
+
Requirement that prevents an evolution through regular means of levelling up.
+
"""
+
type EvolutionRequirement {
+
amount: Int
+
name: String
+
}
+
+
type Pokemon {
+
attacks: AttacksConnection
+
classification: String @deprecated(reason: "And this is the reason why")
+
evolutionRequirements: [EvolutionRequirement]
+
evolutions: [Pokemon]
+
+
"""
+
Likelihood of an attempt to catch a Pokémon to fail.
+
"""
+
fleeRate: Float
+
height: PokemonDimension
+
id: ID!
+
+
"""
+
Maximum combat power a Pokémon may achieve at max level.
+
"""
+
maxCP: Int
+
+
"""
+
Maximum health points a Pokémon may achieve at max level.
+
"""
+
maxHP: Int
+
name: String!
+
resistant: [PokemonType]
+
types: [PokemonType]
+
weaknesses: [PokemonType]
+
weight: PokemonDimension
+
}
+
+
type PokemonDimension {
+
maximum: String
+
minimum: String
+
}
+
+
"""
+
Elemental property associated with either a Pokémon or one of their moves.
+
"""
+
enum PokemonType {
+
Bug
+
Dark
+
Dragon
+
Electric
+
Fairy
+
Fighting
+
Fire
+
Flying
+
Ghost
+
Grass
+
Ground
+
Ice
+
Normal
+
Poison
+
Psychic
+
Rock
+
Steel
+
Water
+
}
+
+
type Query {
+
"""
+
Get a single Pokémon by its ID, a three character long identifier padded with zeroes
+
"""
+
pokemon(id: ID!): Pokemon
+
+
"""
+
List out all Pokémon, optionally in pages
+
"""
+
pokemons(limit: Int, skip: Int): [Pokemon]
+
}
+10
test/e2e/fixture-project-tada-multi-schema/todo.ts
···
+
import { initGraphQLTada } from 'gql.tada';
+
// @ts-ignore
+
import type { introspection } from './todos';
+
+
export const graphql = initGraphQLTada<{
+
introspection: introspection;
+
}>();
+
+
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';
+
export { readFragment } from 'gql.tada';
+26
test/e2e/fixture-project-tada-multi-schema/todos.d.ts
···
+
/* eslint-disable */
+
/* prettier-ignore */
+
+
/** An IntrospectionQuery representation of your schema.
+
*
+
* @remarks
+
* This is an introspection of your schema saved as a file by GraphQLSP.
+
* It will automatically be used by `gql.tada` to infer the types of your GraphQL documents.
+
* If you need to reuse this data or update your `scalars`, update `tadaOutputLocation` to
+
* instead save to a .ts instead of a .d.ts file.
+
*/
+
export type introspection = {
+
name: 'todos';
+
query: 'Query';
+
mutation: never;
+
subscription: never;
+
types: {
+
'Boolean': unknown;
+
'ID': unknown;
+
'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'todo': { name: 'todo'; type: { kind: 'OBJECT'; name: 'Todo'; ofType: null; } }; }; };
+
'String': unknown;
+
'Todo': { kind: 'OBJECT'; name: 'Todo'; fields: { 'completed': { name: 'completed'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'text': { name: 'text'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; };
+
};
+
};
+
+
import * as gqlTada from 'gql.tada';
+12
test/e2e/fixture-project-tada-multi-schema/todos.graphql
···
+
type Todo {
+
id: ID!
+
text: String!
+
completed: Boolean!
+
}
+
+
type Query {
+
"""
+
Get a single Todo by its ID
+
"""
+
todo(id: ID!): Todo
+
}
+28
test/e2e/fixture-project-tada-multi-schema/tsconfig.json
···
+
{
+
"compilerOptions": {
+
"plugins": [
+
{
+
"name": "@0no-co/graphqlsp",
+
"schemas": [
+
{
+
"name": "pokemons",
+
"schema": "./pokemons.graphql",
+
"tadaOutputLocation": "./pokemons.d.ts"
+
},
+
{
+
"name": "todos",
+
"schema": "./todos.graphql",
+
"tadaOutputLocation": "./todos.d.ts"
+
}
+
]
+
}
+
],
+
"target": "es2016",
+
"esModuleInterop": true,
+
"moduleResolution": "node",
+
"forceConsistentCasingInFileNames": true,
+
"strict": true,
+
"skipLibCheck": true
+
},
+
"exclude": ["node_modules", "fixtures"]
+
}
+18 -422
test/e2e/fixture-project-tada/introspection.d.ts
···
* instead save to a .ts instead of a .d.ts file.
*/
export type introspection = {
-
"__schema": {
-
"queryType": {
-
"name": "Query"
-
},
-
"mutationType": null,
-
"subscriptionType": null,
-
"types": [
-
{
-
"kind": "OBJECT",
-
"name": "Attack",
-
"fields": [
-
{
-
"name": "damage",
-
"type": {
-
"kind": "SCALAR",
-
"name": "Int",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "name",
-
"type": {
-
"kind": "SCALAR",
-
"name": "String",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "type",
-
"type": {
-
"kind": "ENUM",
-
"name": "PokemonType",
-
"ofType": null
-
},
-
"args": []
-
}
-
],
-
"interfaces": []
-
},
-
{
-
"kind": "SCALAR",
-
"name": "Int"
-
},
-
{
-
"kind": "SCALAR",
-
"name": "String"
-
},
-
{
-
"kind": "OBJECT",
-
"name": "AttacksConnection",
-
"fields": [
-
{
-
"name": "fast",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "OBJECT",
-
"name": "Attack",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "special",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "OBJECT",
-
"name": "Attack",
-
"ofType": null
-
}
-
},
-
"args": []
-
}
-
],
-
"interfaces": []
-
},
-
{
-
"kind": "OBJECT",
-
"name": "EvolutionRequirement",
-
"fields": [
-
{
-
"name": "amount",
-
"type": {
-
"kind": "SCALAR",
-
"name": "Int",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "name",
-
"type": {
-
"kind": "SCALAR",
-
"name": "String",
-
"ofType": null
-
},
-
"args": []
-
}
-
],
-
"interfaces": []
-
},
-
{
-
"kind": "OBJECT",
-
"name": "Pokemon",
-
"fields": [
-
{
-
"name": "attacks",
-
"type": {
-
"kind": "OBJECT",
-
"name": "AttacksConnection",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "classification",
-
"type": {
-
"kind": "SCALAR",
-
"name": "String",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "evolutionRequirements",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "OBJECT",
-
"name": "EvolutionRequirement",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "evolutions",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "OBJECT",
-
"name": "Pokemon",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "fleeRate",
-
"type": {
-
"kind": "SCALAR",
-
"name": "Float",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "height",
-
"type": {
-
"kind": "OBJECT",
-
"name": "PokemonDimension",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "id",
-
"type": {
-
"kind": "NON_NULL",
-
"ofType": {
-
"kind": "SCALAR",
-
"name": "ID",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "maxCP",
-
"type": {
-
"kind": "SCALAR",
-
"name": "Int",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "maxHP",
-
"type": {
-
"kind": "SCALAR",
-
"name": "Int",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "name",
-
"type": {
-
"kind": "NON_NULL",
-
"ofType": {
-
"kind": "SCALAR",
-
"name": "String",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "resistant",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "ENUM",
-
"name": "PokemonType",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "types",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "ENUM",
-
"name": "PokemonType",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "weaknesses",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "ENUM",
-
"name": "PokemonType",
-
"ofType": null
-
}
-
},
-
"args": []
-
},
-
{
-
"name": "weight",
-
"type": {
-
"kind": "OBJECT",
-
"name": "PokemonDimension",
-
"ofType": null
-
},
-
"args": []
-
}
-
],
-
"interfaces": []
-
},
-
{
-
"kind": "SCALAR",
-
"name": "Float"
-
},
-
{
-
"kind": "SCALAR",
-
"name": "ID"
-
},
-
{
-
"kind": "OBJECT",
-
"name": "PokemonDimension",
-
"fields": [
-
{
-
"name": "maximum",
-
"type": {
-
"kind": "SCALAR",
-
"name": "String",
-
"ofType": null
-
},
-
"args": []
-
},
-
{
-
"name": "minimum",
-
"type": {
-
"kind": "SCALAR",
-
"name": "String",
-
"ofType": null
-
},
-
"args": []
-
}
-
],
-
"interfaces": []
-
},
-
{
-
"kind": "ENUM",
-
"name": "PokemonType",
-
"enumValues": [
-
{
-
"name": "Bug"
-
},
-
{
-
"name": "Dark"
-
},
-
{
-
"name": "Dragon"
-
},
-
{
-
"name": "Electric"
-
},
-
{
-
"name": "Fairy"
-
},
-
{
-
"name": "Fighting"
-
},
-
{
-
"name": "Fire"
-
},
-
{
-
"name": "Flying"
-
},
-
{
-
"name": "Ghost"
-
},
-
{
-
"name": "Grass"
-
},
-
{
-
"name": "Ground"
-
},
-
{
-
"name": "Ice"
-
},
-
{
-
"name": "Normal"
-
},
-
{
-
"name": "Poison"
-
},
-
{
-
"name": "Psychic"
-
},
-
{
-
"name": "Rock"
-
},
-
{
-
"name": "Steel"
-
},
-
{
-
"name": "Water"
-
}
-
]
-
},
-
{
-
"kind": "OBJECT",
-
"name": "Query",
-
"fields": [
-
{
-
"name": "pokemon",
-
"type": {
-
"kind": "OBJECT",
-
"name": "Pokemon",
-
"ofType": null
-
},
-
"args": [
-
{
-
"name": "id",
-
"type": {
-
"kind": "NON_NULL",
-
"ofType": {
-
"kind": "SCALAR",
-
"name": "ID",
-
"ofType": null
-
}
-
}
-
}
-
]
-
},
-
{
-
"name": "pokemons",
-
"type": {
-
"kind": "LIST",
-
"ofType": {
-
"kind": "OBJECT",
-
"name": "Pokemon",
-
"ofType": null
-
}
-
},
-
"args": [
-
{
-
"name": "limit",
-
"type": {
-
"kind": "SCALAR",
-
"name": "Int",
-
"ofType": null
-
}
-
},
-
{
-
"name": "skip",
-
"type": {
-
"kind": "SCALAR",
-
"name": "Int",
-
"ofType": null
-
}
-
}
-
]
-
}
-
],
-
"interfaces": []
-
},
-
{
-
"kind": "SCALAR",
-
"name": "Boolean"
-
}
-
],
-
"directives": []
-
}
+
name: never;
+
query: 'Query';
+
mutation: never;
+
subscription: never;
+
types: {
+
'Attack': { kind: 'OBJECT'; name: 'Attack'; fields: { 'damage': { name: 'damage'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'type': { name: 'type'; type: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; } }; }; };
+
'AttacksConnection': { kind: 'OBJECT'; name: 'AttacksConnection'; fields: { 'fast': { name: 'fast'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Attack'; ofType: null; }; } }; 'special': { name: 'special'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Attack'; ofType: null; }; } }; }; };
+
'Boolean': unknown;
+
'EvolutionRequirement': { kind: 'OBJECT'; name: 'EvolutionRequirement'; fields: { 'amount': { name: 'amount'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; };
+
'Float': unknown;
+
'ID': unknown;
+
'Int': unknown;
+
'Pokemon': { kind: 'OBJECT'; name: 'Pokemon'; fields: { 'attacks': { name: 'attacks'; type: { kind: 'OBJECT'; name: 'AttacksConnection'; ofType: null; } }; 'classification': { name: 'classification'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'evolutionRequirements': { name: 'evolutionRequirements'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'EvolutionRequirement'; ofType: null; }; } }; 'evolutions': { name: 'evolutions'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Pokemon'; ofType: null; }; } }; 'fleeRate': { name: 'fleeRate'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'height': { name: 'height'; type: { kind: 'OBJECT'; name: 'PokemonDimension'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'maxCP': { name: 'maxCP'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'maxHP': { name: 'maxHP'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'resistant': { name: 'resistant'; type: { kind: 'LIST'; name: never; ofType: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; }; } }; 'types': { name: 'types'; type: { kind: 'LIST'; name: never; ofType: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; }; } }; 'weaknesses': { name: 'weaknesses'; type: { kind: 'LIST'; name: never; ofType: { kind: 'ENUM'; name: 'PokemonType'; ofType: null; }; } }; 'weight': { name: 'weight'; type: { kind: 'OBJECT'; name: 'PokemonDimension'; ofType: null; } }; }; };
+
'PokemonDimension': { kind: 'OBJECT'; name: 'PokemonDimension'; fields: { 'maximum': { name: 'maximum'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'minimum': { name: 'minimum'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; };
+
'PokemonType': { name: 'PokemonType'; enumValues: 'Bug' | 'Dark' | 'Dragon' | 'Electric' | 'Fairy' | 'Fighting' | 'Fire' | 'Flying' | 'Ghost' | 'Grass' | 'Ground' | 'Ice' | 'Normal' | 'Poison' | 'Psychic' | 'Rock' | 'Steel' | 'Water'; };
+
'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'pokemon': { name: 'pokemon'; type: { kind: 'OBJECT'; name: 'Pokemon'; ofType: null; } }; 'pokemons': { name: 'pokemons'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Pokemon'; ofType: null; }; } }; }; };
+
'String': unknown;
+
};
};
import * as gqlTada from 'gql.tada';
-
-
declare module 'gql.tada' {
-
interface setupSchema {
-
introspection: introspection;
-
}
-
}
+364
test/e2e/multi-schema-tada.test.ts
···
+
import { expect, afterAll, beforeAll, it, describe } from 'vitest';
+
import { TSServer } from './server';
+
import path from 'node:path';
+
import fs from 'node:fs';
+
import url from 'node:url';
+
import ts from 'typescript/lib/tsserverlibrary';
+
+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
+
+
const projectPath = path.resolve(
+
__dirname,
+
'fixture-project-tada-multi-schema'
+
);
+
describe('Multiple schemas', () => {
+
const outfilePokemonTest = path.join(projectPath, 'simple-pokemon.ts');
+
const outfileTodoTest = path.join(projectPath, 'simple-todo.ts');
+
+
let server: TSServer;
+
beforeAll(async () => {
+
server = new TSServer(projectPath, { debugLog: false });
+
+
server.sendCommand('open', {
+
file: outfilePokemonTest,
+
fileContent: '// empty',
+
scriptKindName: 'TS',
+
} satisfies ts.server.protocol.OpenRequestArgs);
+
server.sendCommand('open', {
+
file: outfileTodoTest,
+
fileContent: '// empty',
+
scriptKindName: 'TS',
+
} satisfies ts.server.protocol.OpenRequestArgs);
+
+
server.sendCommand('updateOpen', {
+
openFiles: [
+
{
+
file: outfilePokemonTest,
+
fileContent: fs.readFileSync(
+
path.join(projectPath, 'fixtures/simple-pokemon.ts'),
+
'utf-8'
+
),
+
},
+
{
+
file: outfileTodoTest,
+
fileContent: fs.readFileSync(
+
path.join(projectPath, 'fixtures/simple-todo.ts'),
+
'utf-8'
+
),
+
},
+
],
+
} satisfies ts.server.protocol.UpdateOpenRequestArgs);
+
+
server.sendCommand('saveto', {
+
file: outfilePokemonTest,
+
tmpfile: outfilePokemonTest,
+
} satisfies ts.server.protocol.SavetoRequestArgs);
+
server.sendCommand('saveto', {
+
file: outfileTodoTest,
+
tmpfile: outfileTodoTest,
+
} satisfies ts.server.protocol.SavetoRequestArgs);
+
+
// Give TS some time to figure this out...
+
await new Promise(resolve => setTimeout(resolve, 1000));
+
});
+
+
afterAll(() => {
+
try {
+
fs.unlinkSync(outfilePokemonTest);
+
fs.unlinkSync(outfileTodoTest);
+
} catch {}
+
});
+
+
it('gives diagnostics about unused fields', async () => {
+
await server.waitForResponse(
+
e => e.type === 'event' && e.event === 'semanticDiag'
+
);
+
const res = server.responses.filter(
+
resp =>
+
resp.type === 'event' &&
+
resp.event === 'semanticDiag' &&
+
resp.body?.file === outfilePokemonTest
+
);
+
+
expect(res).toBeDefined();
+
expect(res).toHaveLength(1);
+
expect(res[0].body.diagnostics).toHaveLength(1);
+
expect(res[0].body.diagnostics[0]).toMatchInlineSnapshot(`
+
{
+
"category": "warning",
+
"code": 52004,
+
"end": {
+
"line": 12,
+
"offset": 1,
+
},
+
"start": {
+
"line": 11,
+
"offset": 7,
+
},
+
"text": "The field Pokemon.classification is deprecated. And this is the reason why",
+
}
+
`);
+
}, 30000);
+
+
it('gives quick-info for the pokemon document', async () => {
+
server.send({
+
seq: 9,
+
type: 'request',
+
command: 'quickinfo',
+
arguments: {
+
file: outfilePokemonTest,
+
line: 8,
+
offset: 8,
+
},
+
});
+
+
await server.waitForResponse(
+
response =>
+
response.type === 'response' && response.command === 'quickinfo'
+
);
+
+
const res = server.responses
+
.reverse()
+
.find(resp => resp.type === 'response' && resp.command === 'quickinfo');
+
+
expect(res).toBeDefined();
+
expect(typeof res?.body).toEqual('object');
+
expect(res?.body.documentation).toEqual(`Pokemon.name: String!`);
+
}, 30000);
+
+
it('gives quick-info for the todo document', async () => {
+
server.send({
+
seq: 10,
+
type: 'request',
+
command: 'quickinfo',
+
arguments: {
+
file: outfileTodoTest,
+
line: 7,
+
offset: 8,
+
},
+
});
+
+
await server.waitForResponse(
+
response =>
+
response.type === 'response' && response.command === 'quickinfo'
+
);
+
+
const res = server.responses
+
.reverse()
+
.find(resp => resp.type === 'response' && resp.command === 'quickinfo');
+
+
expect(res).toBeDefined();
+
expect(typeof res?.body).toEqual('object');
+
expect(res?.body.documentation).toEqual(`Todo.id: ID!`);
+
}, 30000);
+
+
it('gives completion-info for the pokemon document', async () => {
+
server.send({
+
seq: 11,
+
type: 'request',
+
command: 'completionInfo',
+
arguments: {
+
file: outfilePokemonTest,
+
line: 9,
+
offset: 7,
+
includeExternalModuleExports: true,
+
includeInsertTextCompletions: true,
+
triggerKind: 1,
+
},
+
});
+
+
await server.waitForResponse(
+
response =>
+
response.type === 'response' && response.command === 'completionInfo'
+
);
+
+
const res = server.responses
+
.reverse()
+
.find(
+
resp => resp.type === 'response' && resp.command === 'completionInfo'
+
);
+
+
expect(res).toBeDefined();
+
expect(res).toMatchInlineSnapshot(`
+
{
+
"body": {
+
"entries": [
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " AttacksConnection",
+
},
+
"name": "attacks",
+
"sortText": "0attacks",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " [EvolutionRequirement]",
+
},
+
"name": "evolutionRequirements",
+
"sortText": "2evolutionRequirements",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " [Pokemon]",
+
},
+
"name": "evolutions",
+
"sortText": "3evolutions",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " PokemonDimension",
+
},
+
"name": "height",
+
"sortText": "5height",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"description": "Maximum combat power a Pokémon may achieve at max level.",
+
"detail": " Int",
+
},
+
"name": "maxCP",
+
"sortText": "7maxCP",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"description": "Maximum health points a Pokémon may achieve at max level.",
+
"detail": " Int",
+
},
+
"name": "maxHP",
+
"sortText": "8maxHP",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " [PokemonType]",
+
},
+
"name": "resistant",
+
"sortText": "10resistant",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " [PokemonType]",
+
},
+
"name": "types",
+
"sortText": "11types",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " [PokemonType]",
+
},
+
"name": "weaknesses",
+
"sortText": "12weaknesses",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " PokemonDimension",
+
},
+
"name": "weight",
+
"sortText": "13weight",
+
},
+
],
+
"isGlobalCompletion": false,
+
"isMemberCompletion": false,
+
"isNewIdentifierLocation": false,
+
},
+
"command": "completionInfo",
+
"request_seq": 11,
+
"seq": 0,
+
"success": true,
+
"type": "response",
+
}
+
`);
+
}, 30000);
+
+
it('gives completion-info for the todo document', async () => {
+
server.send({
+
seq: 11,
+
type: 'request',
+
command: 'completionInfo',
+
arguments: {
+
file: outfileTodoTest,
+
line: 8,
+
offset: 7,
+
includeExternalModuleExports: true,
+
includeInsertTextCompletions: true,
+
triggerKind: 1,
+
},
+
});
+
+
await server.waitForResponse(
+
response =>
+
response.type === 'response' && response.command === 'completionInfo'
+
);
+
+
const res = server.responses
+
.reverse()
+
.find(
+
resp => resp.type === 'response' && resp.command === 'completionInfo'
+
);
+
+
expect(res).toBeDefined();
+
expect(res).toMatchInlineSnapshot(`
+
{
+
"body": {
+
"entries": [
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " String!",
+
},
+
"name": "text",
+
"sortText": "1text",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"detail": " Boolean!",
+
},
+
"name": "completed",
+
"sortText": "2completed",
+
},
+
{
+
"kind": "var",
+
"kindModifiers": "declare",
+
"labelDetails": {
+
"description": "The name of the current Object type at runtime.",
+
"detail": " String!",
+
},
+
"name": "__typename",
+
"sortText": "3__typename",
+
},
+
],
+
"isGlobalCompletion": false,
+
"isMemberCompletion": false,
+
"isNewIdentifierLocation": false,
+
},
+
"command": "completionInfo",
+
"request_seq": 11,
+
"seq": 0,
+
"success": true,
+
"type": "response",
+
}
+
`);
+
}, 30000);
+
});