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

feat: make tada work with the LSP (#160)

* make tada work

* minor ifxes

* Create many-pandas-relax.md

* minor ifxes

* Update diagnostics.ts

+5
.changeset/many-pandas-relax.md
···
···
+
---
+
"@0no-co/graphqlsp": minor
+
---
+
+
Make the LSP work with [`gql.tada`](https://github.com/0no-co/gql.tada)
+3
packages/example-tada/.vscode/settings.json
···
···
+
{
+
"typescript.tsdk": "node_modules/typescript/lib"
+
}
+418
packages/example-tada/introspection.ts
···
···
+
export const 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: [],
+
},
+
} as const;
+27
packages/example-tada/package.json
···
···
+
{
+
"name": "example",
+
"private": true,
+
"version": "1.0.0",
+
"description": "",
+
"main": "index.js",
+
"scripts": {
+
"test": "echo \"Error: no test specified\" && exit 1"
+
},
+
"author": "",
+
"license": "ISC",
+
"dependencies": {
+
"@graphql-typed-document-node/core": "^3.2.0",
+
"gql.tada": "*",
+
"@urql/core": "^3.0.0",
+
"graphql": "^16.8.1",
+
"urql": "^4.0.6"
+
},
+
"devDependencies": {
+
"@0no-co/graphqlsp": "file:../graphqlsp",
+
"@graphql-codegen/cli": "^5.0.0",
+
"@graphql-codegen/client-preset": "^4.1.0",
+
"@types/react": "^18.2.45",
+
"ts-node": "^10.9.1",
+
"typescript": "^5.3.3"
+
}
+
}
+94
packages/example-tada/schema.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]
+
}
+27
packages/example-tada/src/Pokemon.tsx
···
···
+
import { FragmentOf, graphql, readFragment } from './graphql';
+
+
export const PokemonFields = graphql(`
+
fragment pokemonFields on Pokemon {
+
name
+
weight {
+
minimum
+
}
+
}
+
`);
+
+
interface Props {
+
data: FragmentOf<typeof PokemonFields> | null;
+
}
+
+
export const Pokemon = ({ data }: Props) => {
+
const pokemon = readFragment(PokemonFields, data);
+
if (!pokemon) {
+
return null;
+
}
+
+
return (
+
<li>
+
{pokemon.name}
+
</li>
+
);
+
};
+9
packages/example-tada/src/graphql.ts
···
···
+
import { initGraphQLTada } from 'gql.tada';
+
import type { introspection } from '../introspection';
+
+
export const graphql = initGraphQLTada<{
+
introspection: typeof introspection;
+
}>();
+
+
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';
+
export { readFragment } from 'gql.tada';
+51
packages/example-tada/src/index.tsx
···
···
+
import { createClient, useQuery } from 'urql';
+
import { graphql } from './graphql';
+
import { Pokemon, PokemonFields } from './Pokemon';
+
+
const PokemonQuery = graphql(`
+
query Po($id: ID!) {
+
pokemon(id: $id) {
+
id
+
fleeRate
+
...pokemonFields
+
attacks {
+
special {
+
name
+
damage
+
}
+
}
+
weight {
+
minimum
+
maximum
+
}
+
name
+
__typename
+
}
+
}
+
`, [PokemonFields]);
+
+
const Pokemons = () => {
+
const [result] = useQuery({
+
query: PokemonQuery,
+
variables: { id: '' }
+
});
+
+
// Works
+
console.log(result.data?.pokemon?.attacks && result.data?.pokemon?.attacks.special && result.data?.pokemon?.attacks.special[0] && result.data?.pokemon?.attacks.special[0].name)
+
+
// Works
+
const { fleeRate } = result.data?.pokemon || {};
+
console.log(fleeRate)
+
// Works
+
const po = result.data?.pokemon;
+
// @ts-expect-error
+
const { pokemon: { weight: { minimum } } } = result.data || {};
+
console.log(po?.name, minimum)
+
+
// Works
+
const { pokemon } = result.data || {};
+
console.log(pokemon?.weight?.maximum)
+
+
return <Pokemon data={result.data!.pokemon} />;
+
}
+
+25
packages/example-tada/tsconfig.json
···
···
+
{
+
"compilerOptions": {
+
"plugins": [
+
{
+
"name": "@0no-co/graphqlsp",
+
"schema": "./schema.graphql",
+
"disableTypegen": true,
+
"shouldCheckForColocatedFragments": true,
+
"template": "graphql",
+
"templateIsCallExpression": true,
+
"trackFieldUsage": true
+
}
+
],
+
"jsx": "react-jsx",
+
/* Language and Environment */
+
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
+
/* Modules */
+
"module": "commonjs" /* Specify what module code is generated. */,
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
+
/* Type Checking */
+
"strict": true /* Enable all strict type-checking options. */,
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
+
}
+
}
+74 -2
packages/graphqlsp/src/ast/index.ts
···
import ts from 'typescript/lib/tsserverlibrary';
import fs from 'fs';
import { FragmentDefinitionNode, parse } from 'graphql';
export function isFileDirty(fileName: string, source: ts.SourceFile) {
const contents = fs.readFileSync(fileName, 'utf-8');
···
return result;
}
export function findAllCallExpressions(
sourceFile: ts.SourceFile,
template: string,
···
let hasTriedToFindFragments = shouldSearchFragments ? false : true;
function find(node: ts.Node) {
if (ts.isCallExpression(node) && node.expression.getText() === template) {
-
if (!hasTriedToFindFragments) {
hasTriedToFindFragments = true;
fragments = getAllFragments(sourceFile.fileName, node, info);
}
-
const [arg] = node.arguments;
if (arg && ts.isNoSubstitutionTemplateLiteral(arg)) {
result.push(arg);
}
···
node: ts.CallExpression,
info: ts.server.PluginCreateInfo
) {
let fragments: Array<FragmentDefinitionNode> = [];
const definitions = info.languageService.getDefinitionAtPosition(
···
node.expression.getStart()
);
if (!definitions) return fragments;
const def = definitions[0];
const src = getSource(info, def.fileName);
···
import ts from 'typescript/lib/tsserverlibrary';
import fs from 'fs';
import { FragmentDefinitionNode, parse } from 'graphql';
+
import { Logger } from '..';
export function isFileDirty(fileName: string, source: ts.SourceFile) {
const contents = fs.readFileSync(fileName, 'utf-8');
···
return result;
}
+
function unrollFragment(
+
element: ts.Identifier,
+
template: string,
+
info: ts.server.PluginCreateInfo
+
): Array<FragmentDefinitionNode> {
+
const fragments: Array<FragmentDefinitionNode> = [];
+
const definitions = info.languageService.getDefinitionAtPosition(
+
element.getSourceFile().fileName,
+
element.getStart()
+
);
+
+
if (!definitions) return fragments;
+
+
const [fragment] = definitions;
+
+
const externalSource = getSource(info, fragment.fileName);
+
if (!externalSource) return fragments;
+
+
let found = findNode(externalSource, fragment.textSpan.start);
+
if (!found) return fragments;
+
+
if (
+
ts.isVariableDeclaration(found.parent) &&
+
found.parent.initializer &&
+
ts.isCallExpression(found.parent.initializer)
+
) {
+
found = found.parent.initializer;
+
}
+
+
if (ts.isCallExpression(found) && found.expression.getText() === template) {
+
const [arg, arg2] = found.arguments;
+
if (arg2 && ts.isArrayLiteralExpression(arg2)) {
+
arg2.elements.forEach(element => {
+
if (ts.isIdentifier(element)) {
+
fragments.push(...unrollFragment(element, template, info));
+
}
+
});
+
}
+
+
try {
+
const parsed = parse(arg.getText().slice(1, -1), { noLocation: true });
+
parsed.definitions.forEach(definition => {
+
if (definition.kind === 'FragmentDefinition') {
+
fragments.push(definition);
+
}
+
});
+
} catch (e) {}
+
}
+
+
return fragments;
+
}
+
export function findAllCallExpressions(
sourceFile: ts.SourceFile,
template: string,
···
let hasTriedToFindFragments = shouldSearchFragments ? false : true;
function find(node: ts.Node) {
if (ts.isCallExpression(node) && node.expression.getText() === template) {
+
const [arg, arg2] = node.arguments;
+
+
if (!hasTriedToFindFragments && !arg2) {
hasTriedToFindFragments = true;
fragments = getAllFragments(sourceFile.fileName, node, info);
+
} else if (arg2 && ts.isArrayLiteralExpression(arg2)) {
+
arg2.elements.forEach(element => {
+
if (ts.isIdentifier(element)) {
+
fragments.push(...unrollFragment(element, template, info));
+
}
+
});
}
+
if (arg && ts.isNoSubstitutionTemplateLiteral(arg)) {
result.push(arg);
}
···
node: ts.CallExpression,
info: ts.server.PluginCreateInfo
) {
+
const template = info.config.template || 'gql';
let fragments: Array<FragmentDefinitionNode> = [];
const definitions = info.languageService.getDefinitionAtPosition(
···
node.expression.getStart()
);
if (!definitions) return fragments;
+
+
if (node.arguments[1] && ts.isArrayLiteralExpression(node.arguments[1])) {
+
const arg2 = node.arguments[1] as ts.ArrayLiteralExpression;
+
arg2.elements.forEach(element => {
+
if (ts.isIdentifier(element)) {
+
fragments.push(...unrollFragment(element, template, info));
+
}
+
});
+
return fragments;
+
}
const def = definitions[0];
const src = getSource(info, def.fileName);
-3
packages/graphqlsp/src/autoComplete.ts
···
schema: { current: GraphQLSchema | null },
info: ts.server.PluginCreateInfo
): ts.WithMetadata<ts.CompletionInfo> | undefined {
-
const logger: any = (msg: string) =>
-
info.project.projectService.logger.info(`[GraphQLSP] ${msg}`);
-
const tagTemplate = info.config.template || 'gql';
const isCallExpression = info.config.templateIsCallExpression ?? false;
···
schema: { current: GraphQLSchema | null },
info: ts.server.PluginCreateInfo
): ts.WithMetadata<ts.CompletionInfo> | undefined {
const tagTemplate = info.config.template || 'gql';
const isCallExpression = info.config.templateIsCallExpression ?? false;
+42 -47
packages/graphqlsp/src/checkImports.ts
···
const moduleExports = typeChecker?.getExportsOfModule(symbol);
if (!moduleExports) return;
-
const missingImports = moduleExports
-
.map(exp => {
-
if (importedNames.includes(exp.name)) {
-
return;
-
}
-
const declarations = exp.getDeclarations();
-
const declaration = declarations?.find(x => {
-
// TODO: check whether the sourceFile.fileName resembles the module
-
// specifier
-
return true;
-
});
-
if (!declaration) return;
-
const [template] = findAllTaggedTemplateNodes(
-
declaration,
-
tagTemplate
-
);
-
if (template) {
-
let node = template;
if (
-
ts.isNoSubstitutionTemplateLiteral(node) ||
-
ts.isTemplateExpression(node)
) {
-
if (ts.isTaggedTemplateExpression(node.parent)) {
-
node = node.parent;
-
} else {
-
return;
-
}
-
}
-
-
const text = resolveTemplate(
-
node,
-
node.getSourceFile().fileName,
-
info
-
).combinedText;
-
try {
-
const parsed = parse(text, { noLocation: true });
-
if (
-
parsed.definitions.every(
-
x => x.kind === Kind.FRAGMENT_DEFINITION
-
)
-
) {
-
return `'${exp.name}'`;
-
}
-
} catch (e) {
-
return;
}
}
-
})
-
.filter(Boolean);
-
if (missingImports.length) {
tsDiagnostics.push({
file: source,
length: imp.getText().length,
start: imp.getStart(),
category: ts.DiagnosticCategory.Message,
code: MISSING_FRAGMENT_CODE,
-
messageText: `Missing Fragment import(s) ${missingImports.join(
', '
)} from ${imp.moduleSpecifier.getText()}.`,
});
···
const moduleExports = typeChecker?.getExportsOfModule(symbol);
if (!moduleExports) return;
+
const missingImports = new Set<string>();
+
moduleExports.forEach(exp => {
+
if (importedNames.includes(exp.name)) {
+
return;
+
}
+
+
const declarations = exp.getDeclarations();
+
const declaration = declarations?.find(x => {
+
// TODO: check whether the sourceFile.fileName resembles the module
+
// specifier
+
return true;
+
});
+
if (!declaration) return;
+
const [template] = findAllTaggedTemplateNodes(declaration, tagTemplate);
+
if (template) {
+
let node = template;
+
if (
+
ts.isNoSubstitutionTemplateLiteral(node) ||
+
ts.isTemplateExpression(node)
+
) {
+
if (ts.isTaggedTemplateExpression(node.parent)) {
+
node = node.parent;
+
} else {
+
return;
+
}
+
}
+
const text = resolveTemplate(
+
node,
+
node.getSourceFile().fileName,
+
info
+
).combinedText;
+
try {
+
const parsed = parse(text, { noLocation: true });
if (
+
parsed.definitions.every(x => x.kind === Kind.FRAGMENT_DEFINITION)
) {
+
missingImports.add(`'${exp.name}'`);
}
+
} catch (e) {
+
return;
}
+
}
+
});
+
const missing = Array.from(missingImports);
+
if (missing.length) {
tsDiagnostics.push({
file: source,
length: imp.getText().length,
start: imp.getStart(),
category: ts.DiagnosticCategory.Message,
code: MISSING_FRAGMENT_CODE,
+
messageText: `Missing Fragment import(s) ${missing.join(
', '
)} from ${imp.moduleSpecifier.getText()}.`,
});
+2 -6
packages/graphqlsp/src/diagnostics.ts
···
source,
info
);
-
console.log(
-
'[GraphhQLSP] Checking for colocated fragments ',
-
JSON.stringify(moduleSpecifierToFragments, null, 2)
-
);
const usedFragments = new Set();
nodes.forEach(node => {
···
start,
length,
} = moduleSpecifierToFragments[moduleSpecifier];
-
const missingFragments = fragmentNames.filter(
x => !usedFragments.has(x)
-
);
if (missingFragments.length) {
fragmentDiagnostics.push({
file: source,
···
source,
info
);
const usedFragments = new Set();
nodes.forEach(node => {
···
start,
length,
} = moduleSpecifierToFragments[moduleSpecifier];
+
const missingFragments = Array.from(new Set(fragmentNames.filter(
x => !usedFragments.has(x)
+
)));
if (missingFragments.length) {
fragmentDiagnostics.push({
file: source,
+47 -2
pnpm-lock.yaml
···
specifier: ^5.3.3
version: 5.3.3
packages/graphqlsp:
dependencies:
'@graphql-codegen/add':
···
'@graphql-tools/executor-graphql-ws': 1.1.0(graphql@16.8.1)
'@graphql-tools/executor-http': 1.0.3(@types/node@18.15.11)(graphql@16.8.1)
'@graphql-tools/executor-legacy-ws': 1.0.4(graphql@16.8.1)
-
'@graphql-tools/utils': 10.0.1(graphql@16.8.1)
'@graphql-tools/wrap': 10.0.1(graphql@16.8.1)
'@types/ws': 8.5.10
'@whatwg-node/fetch': 0.9.14
graphql: 16.8.1
isomorphic-ws: 5.0.0(ws@8.14.2)
-
tslib: 2.5.0
value-or-promise: 1.0.12
ws: 8.14.2
transitivePeerDependencies:
···
dependencies:
get-intrinsic: 1.2.0
dev: true
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
···
specifier: ^5.3.3
version: 5.3.3
+
packages/example-tada:
+
dependencies:
+
'@graphql-typed-document-node/core':
+
specifier: ^3.2.0
+
version: 3.2.0(graphql@16.8.1)
+
'@urql/core':
+
specifier: ^3.0.0
+
version: 3.2.2(graphql@16.8.1)
+
gql.tada:
+
specifier: '*'
+
version: 1.0.0-beta.1(graphql@16.8.1)
+
graphql:
+
specifier: ^16.8.1
+
version: 16.8.1
+
urql:
+
specifier: ^4.0.6
+
version: 4.0.6(graphql@16.8.1)(react@18.2.0)
+
devDependencies:
+
'@0no-co/graphqlsp':
+
specifier: file:../graphqlsp
+
version: file:packages/graphqlsp(graphql@16.8.1)
+
'@graphql-codegen/cli':
+
specifier: ^5.0.0
+
version: 5.0.0(@types/node@18.15.11)(graphql@16.8.1)(typescript@5.3.3)
+
'@graphql-codegen/client-preset':
+
specifier: ^4.1.0
+
version: 4.1.0(graphql@16.8.1)
+
'@types/react':
+
specifier: ^18.2.45
+
version: 18.2.45
+
ts-node:
+
specifier: ^10.9.1
+
version: 10.9.1(@types/node@18.15.11)(typescript@5.3.3)
+
typescript:
+
specifier: ^5.3.3
+
version: 5.3.3
+
packages/graphqlsp:
dependencies:
'@graphql-codegen/add':
···
'@graphql-tools/executor-graphql-ws': 1.1.0(graphql@16.8.1)
'@graphql-tools/executor-http': 1.0.3(@types/node@18.15.11)(graphql@16.8.1)
'@graphql-tools/executor-legacy-ws': 1.0.4(graphql@16.8.1)
+
'@graphql-tools/utils': 10.0.11(graphql@16.8.1)
'@graphql-tools/wrap': 10.0.1(graphql@16.8.1)
'@types/ws': 8.5.10
'@whatwg-node/fetch': 0.9.14
graphql: 16.8.1
isomorphic-ws: 5.0.0(ws@8.14.2)
+
tslib: 2.6.2
value-or-promise: 1.0.12
ws: 8.14.2
transitivePeerDependencies:
···
dependencies:
get-intrinsic: 1.2.0
dev: true
+
+
/gql.tada@1.0.0-beta.1(graphql@16.8.1):
+
resolution: {integrity: sha512-jodWt1sV8ORYp8FyY8+bGXAalRoyAqjk6kAZfewZgTuFZ8v/F70yujqxQ0KSdajzFK2SLsOSPEI+QXv+Nln9EQ==}
+
dependencies:
+
'@0no-co/graphql.web': 1.0.4(graphql@16.8.1)
+
transitivePeerDependencies:
+
- graphql
+
dev: false
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}