···
52
-
function unrollFragment(
53
-
element: ts.Identifier,
52
+
function resolveIdentifierToGraphQLCall(
53
+
input: ts.Identifier,
info: ts.server.PluginCreateInfo,
55
-
typeChecker: ts.TypeChecker | undefined
56
-
): Array<FragmentDefinitionNode> {
57
-
const fragments: Array<FragmentDefinitionNode> = [];
58
-
const definitions = info.languageService.getDefinitionAtPosition(
59
-
element.getSourceFile().fileName,
55
+
checker: ts.TypeChecker | undefined
56
+
): checks.GraphQLCallNode | null {
57
+
let prevElement: ts.Node | undefined;
58
+
let element: ts.Node | undefined = input;
59
+
// NOTE: Under certain circumstances, resolving an identifier can loop
60
+
while (ts.isIdentifier(element) && element !== prevElement) {
61
+
prevElement = element;
63
-
const fragment = definitions && definitions[0];
64
-
if (!fragment) return fragments;
63
+
const definitions = info.languageService.getDefinitionAtPosition(
64
+
element.getSourceFile().fileName,
66
-
const externalSource = getSource(info, fragment.fileName);
67
-
if (!externalSource) return fragments;
68
+
const fragment = definitions && definitions[0];
69
+
const externalSource = fragment && getSource(info, fragment.fileName);
70
+
if (!fragment || !externalSource) return null;
69
-
let found = findNode(externalSource, fragment.textSpan.start);
70
-
if (!found) return fragments;
72
+
element = findNode(externalSource, fragment.textSpan.start);
73
+
if (!element) return null;
72
-
while (ts.isPropertyAccessExpression(found.parent)) found = found.parent;
75
+
while (ts.isPropertyAccessExpression(element.parent))
76
+
element = element.parent;
75
-
ts.isVariableDeclaration(found.parent) &&
76
-
found.parent.initializer &&
77
-
ts.isCallExpression(found.parent.initializer)
79
-
found = found.parent.initializer;
80
-
} else if (ts.isPropertyAssignment(found.parent)) {
81
-
found = found.parent.initializer;
82
-
} else if (ts.isBinaryExpression(found.parent)) {
83
-
if (ts.isPropertyAccessExpression(found.parent.right)) {
84
-
found = found.parent.right.name as ts.Identifier;
86
-
found = found.parent.right;
79
+
ts.isVariableDeclaration(element.parent) &&
80
+
element.parent.initializer &&
81
+
ts.isCallExpression(element.parent.initializer)
83
+
element = element.parent.initializer;
84
+
} else if (ts.isPropertyAssignment(element.parent)) {
85
+
element = element.parent.initializer;
86
+
} else if (ts.isBinaryExpression(element.parent)) {
87
+
element = ts.isPropertyAccessExpression(element.parent.right)
88
+
? element.parent.right.name
89
+
: element.parent.right;
90
-
// If we found another identifier, we repeat trying to find the original
91
-
// fragment definition
92
-
if (ts.isIdentifier(found)) {
93
-
return unrollFragment(found, info, typeChecker);
91
+
// If we find another Identifier, we continue resolving it
// Check whether we've got a `graphql()` or `gql()` call, by the
// call expression's identifier
98
-
if (!checks.isGraphQLCall(found, typeChecker)) {
95
+
return checks.isGraphQLCall(element, checker) ? element : null;
103
-
const text = found.arguments[0];
104
-
const fragmentRefs = resolveTadaFragmentArray(found.arguments[1]);
105
-
if (fragmentRefs) {
106
-
for (const identifier of fragmentRefs) {
107
-
fragments.push(...unrollFragment(identifier, info, typeChecker));
98
+
function unrollFragment(
99
+
element: ts.Identifier,
100
+
info: ts.server.PluginCreateInfo,
101
+
checker: ts.TypeChecker | undefined
102
+
): Array<FragmentDefinitionNode> {
103
+
const fragments: FragmentDefinitionNode[] = [];
104
+
const elements: ts.Identifier[] = [element];
105
+
const seen = new WeakSet<ts.Identifier>();
107
+
const _unrollElement = (element: ts.Identifier): void => {
108
+
if (seen.has(element)) return;
111
+
const node = resolveIdentifierToGraphQLCall(element, info, checker);
114
+
const fragmentRefs = resolveTadaFragmentArray(node.arguments[1]);
115
+
if (fragmentRefs) elements.push(...fragmentRefs);
118
+
const text = node.arguments[0];
119
+
const parsed = parse(text.getText().slice(1, -1), { noLocation: true });
120
+
parsed.definitions.forEach(definition => {
121
+
if (definition.kind === 'FragmentDefinition') {
122
+
fragments.push(definition);
126
+
// NOTE: Assume graphql.parse errors can be ignored
110
-
const parsed = parse(text.getText().slice(1, -1), { noLocation: true });
111
-
parsed.definitions.forEach(definition => {
112
-
if (definition.kind === 'FragmentDefinition') {
113
-
fragments.push(definition);
130
+
let nextElement: ts.Identifier | undefined;
131
+
while ((nextElement = elements.shift()) !== undefined)
132
+
_unrollElement(nextElement);