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

fix(field-usage): support Array.at and non-null-expressions (#177)

Changed files
+94 -10
.changeset
packages
example-tada
graphqlsp
+5
.changeset/wise-mangos-kick.md
···
+
---
+
'@0no-co/graphqlsp': patch
+
---
+
+
Add fix for nonNullAssertion and using Array.at
+14 -1
packages/example-tada/src/index.tsx
···
name
__typename
}
+
pokemons {
+
name
+
maxCP
+
maxHP
+
types
+
}
}
`, [PokemonFields]);
···
variables: { id: '' }
});
+
const selected = result.data?.pokemons?.at(0)!
+
console.log(result.data?.pokemons?.at(0)?.maxCP)
+
console.log(selected.maxHP)
+
const names = result.data?.pokemons?.map(x => x?.name)
+
console.log(names)
+
const pos = result.data?.pokemons?.map(x => ({ ...x }))
+
console.log(pos && pos[0].types)
+
// 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)
···
return <Pokemon data={result.data!.pokemon} />;
}
-
-3
packages/example-tada/tsconfig.json
···
{
"name": "@0no-co/graphqlsp",
"schema": "./schema.graphql",
-
"disableTypegen": true,
-
"shouldCheckForColocatedFragments": true,
-
"trackFieldUsage": true,
"tadaOutputLocation": "./introspection.ts"
}
],
+1
packages/graphqlsp/package.json
···
},
"homepage": "https://github.com/0no-co/GraphQLSP#readme",
"devDependencies": {
+
"@0no-co/graphql.web": "^1.0.4",
"@sindresorhus/fnv1a": "^2.0.0",
"@types/node": "^18.15.11",
"@types/node-fetch": "^2.6.3",
+62 -1
packages/graphqlsp/src/fieldUsage.ts
···
return results;
};
+
const arrayMethods = new Set([
+
'map',
+
'filter',
+
'forEach',
+
'reduce',
+
'every',
+
'some',
+
'find',
+
'flatMap',
+
'sort',
+
]);
const crawlScope = (
node: ts.Identifier | ts.BindingName,
originalWip: Array<string>,
···
pathParts.push(foundRef.text);
} else if (
ts.isPropertyAccessExpression(foundRef) &&
+
foundRef.name.text === 'at' &&
+
ts.isCallExpression(foundRef.parent)
+
) {
+
foundRef = foundRef.parent;
+
} else if (
+
ts.isPropertyAccessExpression(foundRef) &&
+
arrayMethods.has(foundRef.name.text) &&
+
ts.isCallExpression(foundRef.parent)
+
) {
+
const isReduce = foundRef.name.text === 'reduce';
+
const isSomeOrEvery =
+
foundRef.name.text === 'every' || foundRef.name.text === 'some';
+
const callExpression = foundRef.parent;
+
const func = callExpression.arguments[0];
+
if (ts.isFunctionExpression(func) || ts.isArrowFunction(func)) {
+
const param = func.parameters[isReduce ? 1 : 0];
+
const res = crawlScope(
+
param.name,
+
pathParts,
+
allFields,
+
source,
+
info
+
);
+
+
// TODO: do we need to support variable destructuring here like
+
// .map being used in const [x] = list.map()?
+
if (
+
ts.isVariableDeclaration(callExpression.parent) &&
+
!isSomeOrEvery
+
) {
+
const varRes = crawlScope(
+
callExpression.parent.name,
+
pathParts,
+
allFields,
+
source,
+
info
+
);
+
res.push(...varRes);
+
}
+
+
return res;
+
} else if (ts.isIdentifier(func)) {
+
// TODO: get the function and do the same as the above
+
}
+
} else if (
+
ts.isPropertyAccessExpression(foundRef) &&
allFields.includes(foundRef.name.text) &&
!pathParts.includes(foundRef.name.text)
) {
···
pathParts.push(foundRef.argumentExpression.text);
}
-
foundRef = foundRef.parent;
+
if (ts.isNonNullExpression(foundRef.parent)) {
+
foundRef = foundRef.parent.parent;
+
} else {
+
foundRef = foundRef.parent;
+
}
}
return pathParts.join('.');
+12 -5
pnpm-lock.yaml
···
specifier: ^2.0.0
version: 2.6.7
devDependencies:
+
'@0no-co/graphql.web':
+
specifier: ^1.0.4
+
version: 1.0.4(graphql@16.8.1)
'@sindresorhus/fnv1a':
specifier: ^2.0.0
version: 2.0.1
···
optional: true
dependencies:
graphql: 16.8.1
-
dev: false
/@ampproject/remapping@2.2.1:
resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
···
object-assign: 4.1.1
promise: 7.3.1
setimmediate: 1.0.5
-
ua-parser-js: 1.0.37
+
ua-parser-js: 0.7.37
transitivePeerDependencies:
- encoding
dev: true
···
dependencies:
hosted-git-info: 2.8.9
resolve: 1.22.2
-
semver: 7.5.4
+
semver: 5.7.2
validate-npm-package-license: 3.0.4
dev: true
···
resolution: {integrity: sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==}
dev: true
+
/semver@5.7.2:
+
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+
hasBin: true
+
dev: true
+
/semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
···
hasBin: true
dev: true
-
/ua-parser-js@1.0.37:
-
resolution: {integrity: sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==}
+
/ua-parser-js@0.7.37:
+
resolution: {integrity: sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==}
dev: true
/ufo@1.3.1: