Skip to content

Commit

Permalink
Crawl nested scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Sep 14, 2024
1 parent acede9b commit dd3be9a
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-queens-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@0no-co/graphqlsp': patch
---

Handle chained expressions while crawling scopes
155 changes: 100 additions & 55 deletions packages/graphqlsp/src/fieldUsage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ts } from './ts';
import { parse, visit } from 'graphql';

import { findNode } from './ast';
import { PropertyAccessExpression } from 'typescript';

export const UNUSED_FIELD_CODE = 52005;

Expand Down Expand Up @@ -119,6 +120,81 @@ const arrayMethods = new Set([
'sort',
]);

const crawlChainedExpressions = (
ref: ts.CallExpression,
pathParts: string[],
allFields: string[],
source: ts.SourceFile,
info: ts.server.PluginCreateInfo
): string[] => {
const isChained =
ts.isPropertyAccessExpression(ref.expression) &&
arrayMethods.has(ref.expression.name.text);
if (isChained) {
const foundRef = ref.expression;
const isReduce = foundRef.name.text === 'reduce';
let func: ts.Expression | ts.FunctionDeclaration | undefined =
ref.arguments[0];

const res = [];
if (ts.isCallExpression(ref.expression.expression)) {
const nestedResult = crawlChainedExpressions(
ref.expression.expression,
pathParts,
allFields,
source,
info
);
if (nestedResult.length) {
res.push(...nestedResult);
}
}

if (func && ts.isIdentifier(func)) {
// TODO: Scope utilities in checkFieldUsageInFile to deduplicate
const checker = info.languageService.getProgram()!.getTypeChecker();

const declaration = checker.getSymbolAtLocation(func)?.valueDeclaration;
if (declaration && ts.isFunctionDeclaration(declaration)) {
func = declaration;
} else if (
declaration &&
ts.isVariableDeclaration(declaration) &&
declaration.initializer
) {
func = declaration.initializer;
}
}

if (
func &&
(ts.isFunctionDeclaration(func) ||
ts.isFunctionExpression(func) ||
ts.isArrowFunction(func))
) {
const param = func.parameters[isReduce ? 1 : 0];
if (param) {
const scopedResult = crawlScope(
param.name,
pathParts,
allFields,
source,
info,
true
);

if (scopedResult.length) {
res.push(...scopedResult);
}
}
}

return res;
}

return [];
};

const crawlScope = (
node: ts.BindingName,
originalWip: Array<string>,
Expand Down Expand Up @@ -219,65 +295,34 @@ const crawlScope = (
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;
let func: ts.Expression | ts.FunctionDeclaration | undefined =
callExpression.arguments[0];

if (func && ts.isIdentifier(func)) {
// TODO: Scope utilities in checkFieldUsageInFile to deduplicate
const checker = info.languageService.getProgram()!.getTypeChecker();

const declaration =
checker.getSymbolAtLocation(func)?.valueDeclaration;
if (declaration && ts.isFunctionDeclaration(declaration)) {
func = declaration;
} else if (
declaration &&
ts.isVariableDeclaration(declaration) &&
declaration.initializer
) {
func = declaration.initializer;
}
const res = [];
const isSomeOrEvery =
foundRef.name.text === 'some' || foundRef.name.text === 'every';
const chainedResults = crawlChainedExpressions(
callExpression,
pathParts,
allFields,
source,
info
);
if (chainedResults.length) {
res.push(...chainedResults);
}

if (
func &&
(ts.isFunctionDeclaration(func) ||
ts.isFunctionExpression(func) ||
ts.isArrowFunction(func))
) {
const param = func.parameters[isReduce ? 1 : 0];
if (param) {
const res = crawlScope(
param.name,
pathParts,
allFields,
source,
info,
true
);

if (
ts.isVariableDeclaration(callExpression.parent) &&
!isSomeOrEvery
) {
const varRes = crawlScope(
callExpression.parent.name,
pathParts,
allFields,
source,
info,
true
);
res.push(...varRes);
}

return res;
}
if (ts.isVariableDeclaration(callExpression.parent) && !isSomeOrEvery) {
const varRes = crawlScope(
callExpression.parent.name,
pathParts,
allFields,
source,
info,
true
);
res.push(...varRes);
}

return res;
} else if (
ts.isPropertyAccessExpression(foundRef) &&
!pathParts.includes(foundRef.name.text)
Expand Down

0 comments on commit dd3be9a

Please sign in to comment.