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

Configure Feed

Select the types of activity you want to include in your feed.

fix(persisted): nested persisted operation resolution (#330)

authored by

Jovi De Croock and committed by
GitHub
77682860 adfa3cef

+81 -30
+5
.changeset/fifty-mangos-doubt.md
··· 1 + --- 2 + '@0no-co/graphqlsp': patch 3 + --- 4 + 5 + Fix nested fragment resolution during persisted traversal
+4 -1
packages/graphqlsp/src/diagnostics.ts
··· 260 260 const generatedHash = generateHashForDocument( 261 261 info, 262 262 initializer.arguments[0], 263 - foundFilename 263 + foundFilename, 264 + ts.isArrayLiteralExpression(initializer.arguments[1]) 265 + ? initializer.arguments[1] 266 + : undefined 264 267 ); 265 268 if (!generatedHash) return null; 266 269
+72 -29
packages/graphqlsp/src/persisted.ts
··· 3 3 import { createHash } from 'crypto'; 4 4 5 5 import * as checks from './ast/checks'; 6 - import { findAllCallExpressions, findNode, getSource } from './ast'; 6 + import { 7 + findAllCallExpressions, 8 + findNode, 9 + getSource, 10 + unrollTadaFragments, 11 + } from './ast'; 7 12 import { resolveTemplate } from './ast/resolve'; 8 - import { parse, print, visit } from '@0no-co/graphql.web'; 13 + import { 14 + FragmentDefinitionNode, 15 + parse, 16 + print, 17 + visit, 18 + } from '@0no-co/graphql.web'; 9 19 10 20 type PersistedAction = { 11 21 span: { ··· 113 123 const hash = generateHashForDocument( 114 124 info, 115 125 initializer.arguments[0], 116 - foundFilename 126 + foundFilename, 127 + ts.isArrayLiteralExpression(initializer.arguments[1]) 128 + ? initializer.arguments[1] 129 + : undefined 117 130 ); 118 131 const existingHash = callExpression.arguments[0]; 119 132 // We assume for now that this is either undefined or an existing string literal ··· 156 169 export const generateHashForDocument = ( 157 170 info: ts.server.PluginCreateInfo, 158 171 templateLiteral: ts.StringLiteralLike | ts.TaggedTemplateExpression, 159 - foundFilename: string 172 + foundFilename: string, 173 + referencedFragments: ts.ArrayLiteralExpression | undefined 160 174 ): string | undefined => { 161 - const externalSource = getSource(info, foundFilename)!; 162 - const { fragments } = findAllCallExpressions(externalSource, info); 175 + if (referencedFragments) { 176 + const fragments: Array<FragmentDefinitionNode> = []; 177 + unrollTadaFragments(referencedFragments, fragments, info); 178 + let text = resolveTemplate( 179 + templateLiteral, 180 + foundFilename, 181 + info 182 + ).combinedText; 183 + fragments.forEach(fragmentDefinition => { 184 + text = `${text}\n\n${print(fragmentDefinition)}`; 185 + }); 186 + return createHash('sha256').update(print(parse(text))).digest('hex'); 187 + } else { 188 + const externalSource = getSource(info, foundFilename)!; 189 + const { fragments } = findAllCallExpressions(externalSource, info); 190 + 191 + const text = resolveTemplate( 192 + templateLiteral, 193 + foundFilename, 194 + info 195 + ).combinedText; 196 + 197 + const parsed = parse(text); 198 + const spreads = new Set<string>(); 199 + visit(parsed, { 200 + FragmentSpread: node => { 201 + spreads.add(node.name.value); 202 + }, 203 + }); 163 204 164 - const text = resolveTemplate( 165 - templateLiteral, 166 - foundFilename, 167 - info 168 - ).combinedText; 169 - const parsed = parse(text); 170 - const spreads = new Set(); 171 - visit(parsed, { 172 - FragmentSpread: node => { 173 - spreads.add(node.name.value); 174 - }, 175 - }); 205 + let resolvedText = text; 206 + const visited = new Set(); 207 + const traversedSpreads = [...spreads]; 176 208 177 - let resolvedText = text; 178 - [...spreads].forEach(spreadName => { 179 - const fragmentDefinition = fragments.find(x => x.name.value === spreadName); 180 - if (!fragmentDefinition) { 181 - info.project.projectService.logger.info( 182 - `[GraphQLSP] could not find fragment for spread ${spreadName}!` 209 + let spreadName: string | undefined; 210 + while ((spreadName = traversedSpreads.shift())) { 211 + visited.add(spreadName); 212 + const fragmentDefinition = fragments.find( 213 + x => x.name.value === spreadName 183 214 ); 184 - return; 185 - } 215 + if (!fragmentDefinition) { 216 + info.project.projectService.logger.info( 217 + `[GraphQLSP] could not find fragment for spread ${spreadName}!` 218 + ); 219 + return; 220 + } 221 + 222 + visit(fragmentDefinition, { 223 + FragmentSpread: node => { 224 + if (!visited.has(node.name.value)) 225 + traversedSpreads.push(node.name.value); 226 + }, 227 + }); 186 228 187 - resolvedText = `${resolvedText}\n\n${print(fragmentDefinition)}`; 188 - }); 229 + resolvedText = `${resolvedText}\n\n${print(fragmentDefinition)}`; 230 + } 189 231 190 - return createHash('sha256').update(resolvedText).digest('hex'); 232 + return createHash('sha256').update(print(parse(resolvedText))).digest('hex'); 233 + } 191 234 }; 192 235 193 236 export const getDocumentReferenceFromTypeQuery = (