Giter VIP home page Giter VIP logo

graphqlsp's Introduction

GraphQLSP

This is a TypeScript LSP Plugin that will recognise documents in your TypeScript code and help you out with hover-information, diagnostics and auto-complete.

Features

  • Hover information showing the decriptions of fields
  • Diagnostics for adding fields that don't exist, are deprecated, missmatched argument types, ...
  • Auto-complete inside your editor for fields
  • Will warn you when you are importing from a file that is exporting fragments that you're not using

Note that this plugin does not do syntax highlighting, for that you still need something like the VSCode/... plugin

Installation

npm install -D @0no-co/graphqlsp

Usage

Go to your tsconfig.json and add

{
  "compilerOptions": {
    "plugins": [
      {
        "name": "@0no-co/graphqlsp",
        "schema": "./schema.graphql"
      }
    ]
  }
}

now restart your TS-server and you should be good to go, ensure you are using the workspace version of TypeScript. In VSCode you can do so by clicking the bottom right when on a TypeScript file or adding a file like this.

If you are using VSCode ensure that your editor is using the Workspace Version of TypeScript this can be done by manually selecting it or adding a .vscode/config.json with the contents of

{
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true
}

Configuration

Required

  • schema allows you to specify a url, .json or .graphql file as your schema. If you need to specify headers for your introspection you can opt into the object notation i.e. { "schema": { "url": "x", "headers": { "Authorization": "y" } }}

Optional

  • template add an additional template to the defaults gql and graphql
  • templateIsCallExpression this tells our client that you are using graphql('doc') (default: true) when using false it will look for tagged template literals
  • shouldCheckForColocatedFragments when turned on, this will scan your imports to find unused fragments and provide a message notifying you about them (only works with call-expressions, default: true)
  • trackFieldUsage this only works with the client-preset, when turned on it will warn you about unused fields within the same file. (only works with call-expressions, default: true)
  • tadaOutputLocation when using gql.tada this can be convenient as it automatically generates an introspection.ts file for you, just give it the directory to output to and you're done
  • tadaDisablePreprocessing this setting disables the optimisation of tadaOutput to a pre-processed TypeScript type, this is off by default.

Tracking unused fields

Currently the tracking unused fields feature has a few caveats with regards to tracking, first and foremost it will only track the result and the accessed properties in the same file to encourage fragment co-location.

Secondly, we don't track mutations/subscriptions as some folks will add additional fields to properly support normalised cache updates.

Fragment masking

When we use a useQuery that supports TypedDocumentNode it will automatically pick up the typings from the query you provide it. However for fragments this could become a bit more troublesome, the minimal way of providing typings for a fragment would be the following:

import { TypedDocumentNode } from '@graphql-typed-document-node/core';

export const PokemonFields = gql`
  fragment pokemonFields on Pokemon {
    id
    name
  }
` as typeof import('./Pokemon.generated').PokemonFieldsFragmentDoc;

export const Pokemon = props => {
  const pokemon = useFragment(props.pokemon, PokemonFields);
};

export function useFragment<Type>(
  data: any,
  _fragment: TypedDocumentNode<Type>
): Type {
  return data;
}

This is mainly needed in cases where this isn't supported out of the box and mainly serves as a way for you to case your types.

BigCommerce
BigCommerce
WunderGraph
WunderGraph
The Guild
The Guild
BeatGig
BeatGig

Local development

Run pnpm i at the root. Open packages/example by running code packages/example or if you want to leverage breakpoints do it with the TSS_DEBUG_BRK=9559 prefix. When you make changes in packages/graphqlsp all you need to do is run pnpm i in your other editor and restart the TypeScript server for the changes to apply.

Ensure that both instances of your editor are using the Workspace Version of TypeScript

graphqlsp's People

Contributors

andheiberg avatar dan-lee avatar github-actions[bot] avatar jovidecroock avatar kitten avatar llllvvuu avatar nandorojo avatar themightypenguin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

graphqlsp's Issues

Throw for unused variables

Currently, there are no errors thrown / displayed if you have unused variables in a named query. However, this breaks the codegen, so it would be nice if it would show red lines for unused variables the way that the GraphQL VSCode plugin did.

Thanks!

Fragments not getting picked up

image
  1. The fragment sometimes doesn't get picked up. Restarting the TS server fixes it.
  2. Once it's fixed, I can't right click a fragment to jump to it (the VSCode extension allows this, it's nice). I can open a separate issue here if you want.

Setup information and conveying errors

We need to figure out whether there are better ways to convey errors/... to our users as currently this involves enabling TS Server Logging which isn't the best DX and the setup error could be minimal

Throw for incorrect variable names

I accidentally did this:

query Artist($slug: String!) {
  artist($slug: $slug) # notice the incorrect dollar sign $slug on the left

  # it should be artist(slug: $slug)
}

There's an unnecessary $ in the variable name. I'd expect this to turn red, as the $slug variable name doesn't exist for artist(slug)

Improve local debug experience

Since switching to pnpm breakpoints have stopped working when running in example/, I would love to take a look here. We also might need to introduce a build:watch command that produces a non-minified bundled piece of code to add breakpoints to.

Add component fragment spread diagnostic

Add optional-diagnostic where we look at the imports of a file and warn when the file has exported fragments that aren't imported/used in the file.

example Todo.ts

export const TodoFields = gql`
  fragment todoFields on Todo { name }
`

export default function Todos(props) {
  return <p>{props.name}</p>
}

example Todos.ts

// Would warn if this isn't imported
import Todo, { TodoFields } from './Todo'

export const TodosQuery = gql`
  query todos {
    todos {
      id
      ...todoFields
    }
  }

  ${TodoFields}
`

Investigate bug where fragment-renaming gets wrong name

I copy pasted the following

export const PokemonFields = gql`
  fragment pokemonFields on Pokemon {
    id
    name
    attacks {
      fast {
        damage
        name
      }
    }
  }
` as typeof import('./Pokemon.generated').PokemonFieldsFragmentDoc;

to create a second query

export const WeaknessFields = gql`
  fragment weakFields on Pokemon {
    weaknesses
  }
` as typeof import('./Pokemon.generated').PokemonFieldsFragmentDoc;

and rather than renaming the latter to leverage WeaknessFieldsFragmentDoc it renamed the first which is very incorrect. This most likely happens because we just do a substring replace which leverages the index of the first occurrence.

A potential solution here would be to perform the replace on a substring of the source-text that starts at node.getStart()

Can't type `...` for fragments

I'm trying to type ... but it appears to automatically insert a field when I type a second period.

Code.-.bookingWithDrawDataListItemFragment.tsx.bg-hmr.-.30.November.2023.mp4

Is this because it doesn't find a matching fragment?

Hoist the `typescript` types

Currently every .generated file contains the metadata from typescript, we probably want to hoist this to a top-level __generated__ folder so we reduce the file-size for the typed-document nodes.

Only write to the source-file when it's saved

Currently we always write to the source-file which could get annoying for our user when they are editing the file because as soon as the diagnostics are cleared we'll snapshot and edit that snapshot so further edits are ignored until they reload the file from disk.

There are several options here but the easiest to me feels like finding a way to see whether we are looking at a saved version and if so then only write to it as TS will mark the file as dirty and reload it when there are no local pending edits. This most likely could also be done by moving the generation out of getSemanticDiagnostics and using some watch on the project and edit that way.

Another option would be to find a way to edit the local-edit cache of the user but this feels a lot more complex.

Figure out a way how to handle multiple fragments

We need to find a way how to handle multiple fragments in a document i.e.

fragment x on Y { id }
fragment y on X { id }

This might lead to us stubbing in the wrong export-name for the document, we should investigate whether GQL-code-generator can handle this and whether we provide the correct export name.

Add session caching

I have been dogfooding this lib quite thoroughly as of late and it seems like we cause some major slowdowns due to how often TS calls the diagnostics hook. We need to perform some caching where the key could be a combo of the schema and the tagged template.

Missbehaving type-generation

When we have a document like

const x = gql `query { todos { id } }`

const y = gql `query { authors { id } }`

The TypeScript LSP will start missbehaving as we are performing two separate updates rather than one large one.

Support additional headers for introspection

Some API's protect their introspection behind an Authorization header, we want to support these however it would be quite... odd to allow folks to pass this in through .json as they wouldn't be able to use environment variables/...

In graphql-code-generator this is already supported through their js-config https://t.co/4WeKtQzeww

Automatically type the queries

  • automatically generated TypedDocumentNode for found queries and fragments
  • add diagnostic for untyped gql invocations
  • add code-hint to add the type to untyped gql invocations

Support nested-fragment resolution errors

When we have a nested-fragment that i.e. spreads an unknown fragment or has an unknown field we potentially surface this error as part of the main operation

The following

export const x = gql`
  fragment someFields on Smth {
     someUnknownField
  }
`

const query = gql`
  query x { ...someFields }
  ${x}
`

Will surface the unknown field both as part of query as well as of x, while this is correct it gives a lot of noise.

Allow duplicate fragments

Currently if we have duplicated fragment use then we report an error, this could be because a component is used multiple times in different sub-components leading to a fragment being used multiple times. We should give people the option to filter out these warnings.

not seeing any syntax highlighting

I updated the vs code settings as recommended and this is my tsconfig. I don't see any syntax highlighting or autocomplete. How can i debug this?

{
"compilerOptions": 
{
    "strict": true,
    "target": "es5",
    "baseUrl": "src",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "checkJs": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "noEmit": true,
    "noUnusedLocals": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": false,
    "plugins": [
      {
        "name": "@0no-co/graphqlsp",
        "schema": "http://127.0.0.1:8000/graphql/",
        "disableTypegen": true,
        "templateIsCallExpression": true,
        "template": "graphql"
      }
    ]
  },
  "include": ["src", "eslint-plugin-canal", "*.js", "next-env.d.ts", "codegen.ts"],
  "exclude": ["node_modules"]
}
Screenshot 2023-12-06 at 12 01 58 PM

Port the existing field-suggestion algorithm to the client-preset suggestions

In the normal non-client-preset route of the LSP we handle field-suggestions slightly different, we add in fragment spread suggestions and filter out fields that are already present in the selection-set (unless you are typing an alias).

The Fragment suggestions do not seem feasible as we have all fragments of the application in memory, this might just be a bit too costly to compute... not even mentioning the noise of all fragments that can be spread on a certain type if it's coming from the whole application.

We however could provide value by filtering out duplicate fields, the logic for that can be found here and the code to alter is found here.

A solution we'll need to provide is similar to this where we can define externalFragments which in this case would be the global-fragments

Doesn’t work for `/* GraphQL */` commented template strings

Template strings containing GraphQL queries that have a leading comment /* GraphQL */ do not have any sort of IntelliSense you would expect this plugin to provide. If you replace the comment with a qgl template string tag function, the IntelliSense starts working. The GraphQL client we're using, graphql-react, (like several other GraphQL clients) doesn't use template string tag functions for GraphQL; it just uses a string for the query.

Enable periodic refetch for URL-based introspections

Currently we execute the introspection once when we have a URL or URL-object for the schema. When we use file-based we leverage an FS.watch, we should have some periodic refetch atleast configurable for the URL-based as well.

Support endpoints as schema

Currently we only support .json introspections as well as normal text which we assume is a GraphQLSchema, however it's most likely worth supporting a URL format as well so we fetch the introspection and treat it as JSON.

add `enforceKeyfields` option

One of the things that can turn out to be difficult in normalized caching is when people aren't querying the key-fields, we could write a heuristic that looks at the selection-sets and warns when we see the schema offers an ID! field but that field isn't queried. I would also add a few defaults like key, id, uid and _id

We'd put this behind an option so people can opt-in to this behavior.

Resolve Fragments

Currently when there's an interpolated fragment the LSP stops working

  • look at scopes
  • alternatively resolve manually

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.