From af052d7381cbd7568ed10946f3488bf9e2b63224 Mon Sep 17 00:00:00 2001 From: Hironao OTSUBO Date: Wed, 5 Feb 2025 13:28:13 +0900 Subject: [PATCH 1/3] fix unknown fragment error --- .../src/MessageProcessor.ts | 20 +++--- .../src/__tests__/MessageProcessor.spec.ts | 68 ++++++++++++++++++- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/packages/graphql-language-service-server/src/MessageProcessor.ts b/packages/graphql-language-service-server/src/MessageProcessor.ts index 817e178c237..1328987b4bf 100644 --- a/packages/graphql-language-service-server/src/MessageProcessor.ts +++ b/packages/graphql-language-service-server/src/MessageProcessor.ts @@ -1223,25 +1223,29 @@ export class MessageProcessor { private async _cacheDocumentFilesforProject(project: GraphQLProjectConfig) { try { const documents = await project.getDocuments(); - return Promise.all( - documents.map(async document => { - if (!document.location || !document.rawSDL) { - return; - } + const documentLocations = new Set( + documents + .filter(doc => doc.location && doc.rawSDL) + .map(doc => doc.location!), + ); - let filePath = document.location; + return Promise.all( + Array.from(documentLocations).map(async loc => { + let filePath = loc; if (!path.isAbsolute(filePath)) { - filePath = path.join(project.dirpath, document.location); + filePath = path.join(project.dirpath, loc); } // build full system URI path with protocol const uri = URI.file(filePath).toString(); + const fileContent = await readFile(filePath, 'utf-8'); // I would use the already existing graphql-config AST, but there are a few reasons we can't yet - const contents = await this._parser(document.rawSDL, uri); + const contents = await parseDocument(fileContent, uri); if (!contents[0]?.query) { return; } + await this._updateObjectTypeDefinition(uri, contents); await this._updateFragmentDefinition(uri, contents); await this._invalidateCache({ version: 1, uri }, uri, contents); diff --git a/packages/graphql-language-service-server/src/__tests__/MessageProcessor.spec.ts b/packages/graphql-language-service-server/src/__tests__/MessageProcessor.spec.ts index ffbe0dc6f36..33dffaabd89 100644 --- a/packages/graphql-language-service-server/src/__tests__/MessageProcessor.spec.ts +++ b/packages/graphql-language-service-server/src/__tests__/MessageProcessor.spec.ts @@ -218,8 +218,8 @@ describe('MessageProcessor with config', () => { character: 0, }, end: { - line: 2, - character: 1, + line: 0, + character: 25, }, }); @@ -542,6 +542,9 @@ describe('MessageProcessor with config', () => { expect(project.lsp._logger.error).not.toHaveBeenCalled(); expect(await project.lsp._graphQLCache.getSchema('a')).toBeDefined(); + expect(project.lsp._logger.info).not.toHaveBeenCalledWith( + expect.stringMatching(/SyntaxError: Unexpected token/), + ); fetchMock.restore(); mockSchema( @@ -636,4 +639,65 @@ describe('MessageProcessor with config', () => { expect(project.lsp._logger.error).not.toHaveBeenCalled(); project.lsp.handleShutdownRequest(); }); + + it('correctly handles a fragment inside a TypeScript file', async () => { + const project = new MockProject({ + files: [ + [ + 'schema.graphql', + ` +type Item { + foo: String + bar: Int +} + +type Query { + items: [Item] +} + `, + ], + [ + 'query.ts', + ` +import gql from 'graphql-tag' + +const query = gql\` + query { + items { + ...ItemFragment + } + } +\` + `, + ], + [ + 'fragments.ts', + ` +import gql from 'graphql-tag' + +export const ItemFragment = gql\` + fragment ItemFragment on Item { + foo + bar + } +\` + `, + ], + [ + 'graphql.config.json', + '{ "schema": "./schema.graphql", "documents": "./**.{graphql,ts}" }', + ], + ], + }); + + const initParams = await project.init('query.ts'); + expect(initParams.diagnostics).toEqual([]); + + const fragmentDefinition = await project.lsp.handleDefinitionRequest({ + textDocument: { uri: project.uri('query.ts') }, + position: { character: 10, line: 6 }, + }); + + expect(fragmentDefinition[0]?.uri).toEqual(project.uri('fragments.ts')); + }); }); From faaa01d6beadb17597e5d8f1d114dfdc5eedf828 Mon Sep 17 00:00:00 2001 From: Hironao OTSUBO Date: Wed, 5 Feb 2025 14:17:03 +0900 Subject: [PATCH 2/3] use MessageProcessor._parser --- .../graphql-language-service-server/src/MessageProcessor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphql-language-service-server/src/MessageProcessor.ts b/packages/graphql-language-service-server/src/MessageProcessor.ts index 1328987b4bf..45a1923d3d3 100644 --- a/packages/graphql-language-service-server/src/MessageProcessor.ts +++ b/packages/graphql-language-service-server/src/MessageProcessor.ts @@ -1241,7 +1241,7 @@ export class MessageProcessor { const fileContent = await readFile(filePath, 'utf-8'); // I would use the already existing graphql-config AST, but there are a few reasons we can't yet - const contents = await parseDocument(fileContent, uri); + const contents = await this._parser(fileContent, uri); if (!contents[0]?.query) { return; } From 84d0eb61b93e50163387acf9ebb7470c4613ee7a Mon Sep 17 00:00:00 2001 From: Hironao OTSUBO Date: Wed, 5 Feb 2025 15:55:07 +0900 Subject: [PATCH 3/3] add changeset --- .changeset/warm-sheep-brake.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/warm-sheep-brake.md diff --git a/.changeset/warm-sheep-brake.md b/.changeset/warm-sheep-brake.md new file mode 100644 index 00000000000..a79964b24b0 --- /dev/null +++ b/.changeset/warm-sheep-brake.md @@ -0,0 +1,5 @@ +--- +'graphql-language-service-server': patch +--- + +fix parsing non-graphql documents