···
!pathParts.includes(foundRef.text)
const joined = [...pathParts, foundRef.text].join('.');
-
console.log('joined', JSON.stringify(joined, null, 2));
-
console.log('allFields', JSON.stringify(allFields, null, 2));
if (allFields.find(x => x.startsWith(joined))) {
pathParts.push(foundRef.text);
···
arrayMethods.has(foundRef.name.text) &&
ts.isCallExpression(foundRef.parent)
-
console.log('found array method', foundRef.getText());
const isReduce = foundRef.name.text === 'reduce';
foundRef.name.text === 'every' || foundRef.name.text === 'some';
const callExpression = foundRef.parent;
const func = callExpression.arguments[0];
-
console.log('found func', func.getText());
if (ts.isFunctionExpression(func) || ts.isArrowFunction(func)) {
const param = func.parameters[isReduce ? 1 : 0];
···
-
console.log('res scope', JSON.stringify(res, null, 2));
// TODO: do we need to support variable destructuring here like
// .map being used in const [x] = list.map()?
···
!pathParts.includes(foundRef.name.text)
const joined = [...pathParts, foundRef.name.text].join('.');
-
console.log('joined', JSON.stringify(joined, null, 2));
-
console.log('allFields', JSON.stringify(allFields, null, 2));
if (allFields.find(x => x.startsWith(joined))) {
pathParts.push(foundRef.name.text);
···
const joined = [...pathParts, foundRef.argumentExpression.text].join(
-
console.log('joined', JSON.stringify(joined, null, 2));
-
console.log('allFields', JSON.stringify(allFields, null, 2));
if (allFields.find(x => x.startsWith(joined))) {
pathParts.push(foundRef.argumentExpression.text);
···
const shouldTrackFieldUsage = info.config.trackFieldUsage ?? true;
if (!shouldTrackFieldUsage) return diagnostics;
-
nodes.forEach(node => {
-
const nodeText = node.getText();
-
// Bailing for mutations/subscriptions as these could have small details
-
// for normalised cache interactions
-
if (nodeText.includes('mutation') || nodeText.includes('subscription'))
-
const variableDeclaration = getVariableDeclaration(node);
-
if (!ts.isVariableDeclaration(variableDeclaration)) return;
-
const references = info.languageService.getReferencesAtPosition(
-
variableDeclaration.name.getStart()
-
if (!references) return;
-
const allAccess: string[] = [];
-
const inProgress: string[] = [];
-
const allPaths: string[] = [];
-
const reserved = ['id', '__typename'];
-
const fieldToLoc = new Map<string, { start: number; length: number }>();
-
// This visitor gets all the leaf-paths in the document
-
// as well as all fields that are part of the document
-
// We need the leaf-paths to check usage and we need the
-
// fields to validate whether an access on a given reference
-
// is valid given the current document...
-
visit(parse(node.getText().slice(1, -1)), {
-
if (!node.selectionSet && !reserved.includes(node.name.value)) {
-
if (inProgress.length) {
-
p = inProgress.join('.') + '.' + node.name.value;
-
start: node.name.loc!.start,
-
length: node.name.loc!.end - node.name.loc!.start,
-
} else if (node.selectionSet) {
-
inProgress.push(node.name.value);
-
if (node.selectionSet) {
-
references.forEach(ref => {
-
if (ref.fileName !== source.fileName) return;
-
let found = findNode(source, ref.textSpan.start);
-
while (found && !ts.isVariableStatement(found)) {
-
if (!found || !ts.isVariableStatement(found)) return;
-
const [output] = found.declarationList.declarations;
-
if (output.name.getText() === variableDeclaration.name.getText()) return;
-
let temp = output.name;
-
// - const result = await client.query() || useFragment()
-
// - const [result] = useQuery() --> urql
-
// - const { data } = useQuery() --> Apollo
-
// - const { field } = useFragment()
-
// - const [{ data }] = useQuery()
-
// - const { data: { pokemon } } = useQuery()
-
ts.isArrayBindingPattern(temp) &&
-
ts.isBindingElement(temp.elements[0])
-
temp = temp.elements[0].name;
-
if (ts.isObjectBindingPattern(temp)) {
-
const result = traverseDestructuring(temp, [], allPaths, source, info);
-
allAccess.push(...result);
-
const result = crawlScope(temp, [], allPaths, source, info);
-
allAccess.push(...result);
-
const unused = allPaths.filter(x => !allAccess.includes(x));
-
unused.forEach(unusedField => {
-
const loc = fieldToLoc.get(unusedField);
-
start: node.getStart() + loc.start + 1,
-
category: ts.DiagnosticCategory.Warning,
-
code: UNUSED_FIELD_CODE,
-
messageText: `Field '${unusedField}' is not used.`,