-
Notifications
You must be signed in to change notification settings - Fork 26
/
validator.ts
86 lines (84 loc) · 3.5 KB
/
validator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import type { GraphQLSchema } from 'graphql';
import { getDiagnostics, getFragmentDependenciesForAST } from 'graphql-language-service';
import { ErrorWithLocation, ERROR_CODES } from '../errors';
import type { ComputePosition } from '../ts-ast-util';
import { getFragmentsInDocument, getFragmentNamesInDocument, cloneFragmentMap } from '../gql-ast-util';
import type { ExtractResult } from './extractor';
import { OutOfRangeError } from '../string-util';
function calcEndPositionSafely(
startPositionOfSource: number,
getSourcePosition: ComputePosition,
innerPosition: number,
) {
let endPositionOfSource: number = 0;
try {
endPositionOfSource = getSourcePosition(innerPosition).pos;
} catch (error) {
endPositionOfSource = startPositionOfSource + 1;
}
return endPositionOfSource;
}
export function validate({ fileEntries: extractedResults, globalFragments }: ExtractResult, schema: GraphQLSchema) {
const errors: ErrorWithLocation[] = [];
extractedResults.forEach(r => {
if (!r.resolevedTemplateInfo) return;
const { combinedText, getSourcePosition, convertInnerLocation2InnerPosition } = r.resolevedTemplateInfo;
const fragmentNamesInText = getFragmentNamesInDocument(r.documentNode);
errors.push(
...getFragmentsInDocument(r.documentNode)
.map(fragmentDef => [fragmentDef, getSourcePosition(fragmentDef.name.loc!.start)] as const)
.filter(
([fragmentDef, { isInOtherExpression }]) =>
!isInOtherExpression && globalFragments.duplicatedDefinitions.has(fragmentDef.name.value),
)
.map(
([fragmentDef, { pos: startPositionOfSource }]) =>
new ErrorWithLocation(ERROR_CODES.duplicatedFragmentDefinitions.message, {
fileName: r.fileName,
severity: 'Error',
content: r.templateNode.getSourceFile().getText(),
start: startPositionOfSource,
end: calcEndPositionSafely(startPositionOfSource, getSourcePosition, fragmentDef.name.loc!.end),
}),
),
);
const externalFragments = r.documentNode
? getFragmentDependenciesForAST(
r.documentNode,
cloneFragmentMap(globalFragments.definitionMap, fragmentNamesInText),
)
: [];
const diagnostics = getDiagnostics(combinedText, schema, undefined, undefined, externalFragments);
diagnostics.forEach(diagnositc => {
try {
const { pos: startPositionOfSource, isInOtherExpression } = getSourcePosition(
convertInnerLocation2InnerPosition(diagnositc.range.start, true),
);
if (isInOtherExpression) return;
const endPositionOfSource = calcEndPositionSafely(
startPositionOfSource,
getSourcePosition,
convertInnerLocation2InnerPosition(diagnositc.range.end),
);
errors.push(
new ErrorWithLocation(diagnositc.message, {
fileName: r.fileName,
severity: diagnositc.severity === 2 ? 'Warn' : 'Error',
content: r.templateNode.getSourceFile().getText(),
start: startPositionOfSource,
end: endPositionOfSource,
}),
);
} catch (e) {
if (e instanceof OutOfRangeError) {
// Note:
// We can not convertInnerLocation2InnerPosition if semantics diagnostics are located in externalFragments.
// In other words, there is no error in the original sanitized template text, so nothing to do.
return;
}
throw e;
}
});
});
return errors;
}