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

fix: avoid diagnostics for fragment imports and field-usage (#174)

* avoid diagnostics for fragment imports and field-usage

* debug

Changed files
+56 -59
.changeset
packages
graphqlsp
test
e2e
+5
.changeset/wild-lizards-fry.md
···
+
---
+
'@0no-co/graphqlsp': patch
+
---
+
+
Stop caching diagnostics for fragment imports and field-usage as these can be controlled externally
+51 -57
packages/graphqlsp/src/diagnostics.ts
···
export const MISSING_OPERATION_NAME_CODE = 52002;
export const USING_DEPRECATED_FIELD_CODE = 52004;
-
let isGeneratingTypes = false;
-
const cache = new LRUCache<number, ts.Diagnostic[]>({
// how long to live in ms
ttl: 1000 * 60 * 15,
···
: texts.join('-') + schema.version
);
+
let tsDiagnostics;
if (cache.has(cacheKey)) {
-
return cache.get(cacheKey)!;
+
tsDiagnostics = cache.get(cacheKey)!;
} else {
-
const tsDiagnostics = runDiagnostics(
-
source,
-
{ nodes, fragments },
-
schema,
-
info
-
);
+
tsDiagnostics = runDiagnostics(source, { nodes, fragments }, schema, info);
cache.set(cacheKey, tsDiagnostics);
+
}
+
+
const shouldCheckForColocatedFragments =
+
info.config.shouldCheckForColocatedFragments ?? true;
+
let fragmentDiagnostics: ts.Diagnostic[] = [];
+
if (isCallExpression && shouldCheckForColocatedFragments) {
+
const moduleSpecifierToFragments = getColocatedFragmentNames(source, info);
+
+
const usedFragments = new Set();
+
nodes.forEach(node => {
+
try {
+
const parsed = parse(node.getText().slice(1, -1), {
+
noLocation: true,
+
});
+
visit(parsed, {
+
FragmentSpread: node => {
+
usedFragments.add(node.name.value);
+
},
+
});
+
} catch (e) {}
+
});
+
+
Object.keys(moduleSpecifierToFragments).forEach(moduleSpecifier => {
+
const {
+
fragments: fragmentNames,
+
start,
+
length,
+
} = moduleSpecifierToFragments[moduleSpecifier];
+
const missingFragments = Array.from(
+
new Set(fragmentNames.filter(x => !usedFragments.has(x)))
+
);
+
if (missingFragments.length) {
+
fragmentDiagnostics.push({
+
file: source,
+
length,
+
start,
+
category: ts.DiagnosticCategory.Warning,
+
code: MISSING_FRAGMENT_CODE,
+
messageText: `Unused co-located fragment definition(s) "${missingFragments.join(
+
', '
+
)}" in ${moduleSpecifier}`,
+
});
+
}
+
});
+
+
return [...tsDiagnostics, ...fragmentDiagnostics];
+
} else {
return tsDiagnostics;
}
}
···
info
);
-
const shouldCheckForColocatedFragments =
-
info.config.shouldCheckForColocatedFragments ?? true;
-
let fragmentDiagnostics: ts.Diagnostic[] = [];
-
if (shouldCheckForColocatedFragments) {
-
const moduleSpecifierToFragments = getColocatedFragmentNames(
-
source,
-
info
-
);
-
-
const usedFragments = new Set();
-
nodes.forEach(node => {
-
try {
-
const parsed = parse(node.getText().slice(1, -1), {
-
noLocation: true,
-
});
-
visit(parsed, {
-
FragmentSpread: node => {
-
usedFragments.add(node.name.value);
-
},
-
});
-
} catch (e) {}
-
});
-
-
Object.keys(moduleSpecifierToFragments).forEach(moduleSpecifier => {
-
const {
-
fragments: fragmentNames,
-
start,
-
length,
-
} = moduleSpecifierToFragments[moduleSpecifier];
-
const missingFragments = Array.from(
-
new Set(fragmentNames.filter(x => !usedFragments.has(x)))
-
);
-
if (missingFragments.length) {
-
fragmentDiagnostics.push({
-
file: source,
-
length,
-
start,
-
category: ts.DiagnosticCategory.Warning,
-
code: MISSING_FRAGMENT_CODE,
-
messageText: `Unused co-located fragment definition(s) "${missingFragments.join(
-
', '
-
)}" in ${moduleSpecifier}`,
-
});
-
}
-
});
-
}
-
-
return [...tsDiagnostics, ...usageDiagnostics, ...fragmentDiagnostics];
+
return [...tsDiagnostics, ...usageDiagnostics];
} else {
return tsDiagnostics;
}
-2
test/e2e/server.ts
···
import { ChildProcess, fork } from 'node:child_process';
-
import path from 'node:path';
import readline from 'node:readline';
-
import fs from 'node:fs';
import ts from 'typescript/lib/tsserverlibrary';
type Command = `${ts.server.protocol.CommandTypes}`;