···
!pathParts.includes(foundRef.text)
const joined = [...pathParts, foundRef.text].join('.');
166
-
console.log('joined', JSON.stringify(joined, null, 2));
167
-
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)
182
-
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];
188
-
console.log('found func', func.getText());
if (ts.isFunctionExpression(func) || ts.isArrowFunction(func)) {
const param = func.parameters[isReduce ? 1 : 0];
···
198
-
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('.');
225
-
console.log('joined', JSON.stringify(joined, null, 2));
226
-
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(
238
-
console.log('joined', JSON.stringify(joined, null, 2));
239
-
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;
267
-
nodes.forEach(node => {
268
-
const nodeText = node.getText();
269
-
// Bailing for mutations/subscriptions as these could have small details
270
-
// for normalised cache interactions
271
-
if (nodeText.includes('mutation') || nodeText.includes('subscription'))
259
+
nodes.forEach(node => {
260
+
const nodeText = node.getText();
261
+
// Bailing for mutations/subscriptions as these could have small details
262
+
// for normalised cache interactions
263
+
if (nodeText.includes('mutation') || nodeText.includes('subscription'))
274
-
const variableDeclaration = getVariableDeclaration(node);
275
-
if (!ts.isVariableDeclaration(variableDeclaration)) return;
266
+
const variableDeclaration = getVariableDeclaration(node);
267
+
if (!ts.isVariableDeclaration(variableDeclaration)) return;
269
+
const references = info.languageService.getReferencesAtPosition(
271
+
variableDeclaration.name.getStart()
273
+
if (!references) return;
277
-
const references = info.languageService.getReferencesAtPosition(
279
-
variableDeclaration.name.getStart()
281
-
if (!references) return;
275
+
const allAccess: string[] = [];
276
+
const inProgress: string[] = [];
277
+
const allPaths: string[] = [];
278
+
const reserved = ['id', '__typename'];
279
+
const fieldToLoc = new Map<string, { start: number; length: number }>();
280
+
// This visitor gets all the leaf-paths in the document
281
+
// as well as all fields that are part of the document
282
+
// We need the leaf-paths to check usage and we need the
283
+
// fields to validate whether an access on a given reference
284
+
// is valid given the current document...
285
+
visit(parse(node.getText().slice(1, -1)), {
288
+
if (!node.selectionSet && !reserved.includes(node.name.value)) {
290
+
if (inProgress.length) {
291
+
p = inProgress.join('.') + '.' + node.name.value;
293
+
p = node.name.value;
283
-
const allAccess: string[] = [];
284
-
const inProgress: string[] = [];
285
-
const allPaths: string[] = [];
286
-
const reserved = ['id', '__typename'];
287
-
const fieldToLoc = new Map<string, { start: number; length: number }>();
288
-
// This visitor gets all the leaf-paths in the document
289
-
// as well as all fields that are part of the document
290
-
// We need the leaf-paths to check usage and we need the
291
-
// fields to validate whether an access on a given reference
292
-
// is valid given the current document...
293
-
visit(parse(node.getText().slice(1, -1)), {
296
-
if (!node.selectionSet && !reserved.includes(node.name.value)) {
298
-
if (inProgress.length) {
299
-
p = inProgress.join('.') + '.' + node.name.value;
301
-
p = node.name.value;
297
+
fieldToLoc.set(p, {
298
+
start: node.name.loc!.start,
299
+
length: node.name.loc!.end - node.name.loc!.start,
301
+
} else if (node.selectionSet) {
302
+
inProgress.push(node.name.value);
305
-
fieldToLoc.set(p, {
306
-
start: node.name.loc!.start,
307
-
length: node.name.loc!.end - node.name.loc!.start,
309
-
} else if (node.selectionSet) {
310
-
inProgress.push(node.name.value);
314
-
if (node.selectionSet) {
306
+
if (node.selectionSet) {
321
-
references.forEach(ref => {
322
-
if (ref.fileName !== source.fileName) return;
313
+
references.forEach(ref => {
314
+
if (ref.fileName !== source.fileName) return;
324
-
let found = findNode(source, ref.textSpan.start);
325
-
while (found && !ts.isVariableStatement(found)) {
326
-
found = found.parent;
316
+
let found = findNode(source, ref.textSpan.start);
317
+
while (found && !ts.isVariableStatement(found)) {
318
+
found = found.parent;
329
-
if (!found || !ts.isVariableStatement(found)) return;
321
+
if (!found || !ts.isVariableStatement(found)) return;
331
-
const [output] = found.declarationList.declarations;
323
+
const [output] = found.declarationList.declarations;
333
-
if (output.name.getText() === variableDeclaration.name.getText()) return;
325
+
if (output.name.getText() === variableDeclaration.name.getText())
335
-
let temp = output.name;
336
-
// Supported cases:
337
-
// - const result = await client.query() || useFragment()
338
-
// - const [result] = useQuery() --> urql
339
-
// - const { data } = useQuery() --> Apollo
340
-
// - const { field } = useFragment()
341
-
// - const [{ data }] = useQuery()
342
-
// - const { data: { pokemon } } = useQuery()
344
-
ts.isArrayBindingPattern(temp) &&
345
-
ts.isBindingElement(temp.elements[0])
347
-
temp = temp.elements[0].name;
328
+
let temp = output.name;
329
+
// Supported cases:
330
+
// - const result = await client.query() || useFragment()
331
+
// - const [result] = useQuery() --> urql
332
+
// - const { data } = useQuery() --> Apollo
333
+
// - const { field } = useFragment()
334
+
// - const [{ data }] = useQuery()
335
+
// - const { data: { pokemon } } = useQuery()
337
+
ts.isArrayBindingPattern(temp) &&
338
+
ts.isBindingElement(temp.elements[0])
340
+
temp = temp.elements[0].name;
350
-
if (ts.isObjectBindingPattern(temp)) {
351
-
const result = traverseDestructuring(temp, [], allPaths, source, info);
352
-
allAccess.push(...result);
354
-
const result = crawlScope(temp, [], allPaths, source, info);
355
-
allAccess.push(...result);
343
+
if (ts.isObjectBindingPattern(temp)) {
344
+
const result = traverseDestructuring(
351
+
allAccess.push(...result);
353
+
const result = crawlScope(temp, [], allPaths, source, info);
354
+
allAccess.push(...result);
359
-
const unused = allPaths.filter(x => !allAccess.includes(x));
358
+
const unused = allPaths.filter(x => !allAccess.includes(x));
361
-
unused.forEach(unusedField => {
362
-
const loc = fieldToLoc.get(unusedField);
360
+
unused.forEach(unusedField => {
361
+
const loc = fieldToLoc.get(unusedField);
367
-
length: loc.length,
368
-
start: node.getStart() + loc.start + 1,
369
-
category: ts.DiagnosticCategory.Warning,
370
-
code: UNUSED_FIELD_CODE,
371
-
messageText: `Field '${unusedField}' is not used.`,
366
+
length: loc.length,
367
+
start: node.getStart() + loc.start + 1,
368
+
category: ts.DiagnosticCategory.Warning,
369
+
code: UNUSED_FIELD_CODE,
370
+
messageText: `Field '${unusedField}' is not used.`,
375
+
console.error('[GraphQLSP]: ', e.message, e.stack);