diff --git a/src/language/__tests__/printLocation-test.js b/src/language/__tests__/printLocation-test.js index 1d325fcb0f..129a872a52 100644 --- a/src/language/__tests__/printLocation-test.js +++ b/src/language/__tests__/printLocation-test.js @@ -7,7 +7,48 @@ import dedent from '../../jsutils/dedent'; import { Source } from '../../language'; import { printSourceLocation } from '../printLocation'; -describe('printLocation', () => { +describe('printSourceLocation', () => { + it('prints minified documents', () => { + const minifiedSource = new Source( + 'query SomeMiniFiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String){someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD...on THIRD_ERROR_HERE}}}', + ); + + const firstLocation = printSourceLocation(minifiedSource, { + line: 1, + column: minifiedSource.body.indexOf('FIRST_ERROR_HERE') + 1, + }); + expect(firstLocation + '\n').to.equal(dedent` + GraphQL request:1:53 + 1 | query SomeMiniFiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String) + | ^ + | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD. + `); + + const secondLocation = printSourceLocation(minifiedSource, { + line: 1, + column: minifiedSource.body.indexOf('SECOND_ERROR_HERE') + 1, + }); + expect(secondLocation + '\n').to.equal(dedent` + GraphQL request:1:114 + 1 | query SomeMiniFiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String) + | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD. + | ^ + | ..on THIRD_ERROR_HERE}}} + `); + + const thirdLocation = printSourceLocation(minifiedSource, { + line: 1, + column: minifiedSource.body.indexOf('THIRD_ERROR_HERE') + 1, + }); + expect(thirdLocation + '\n').to.equal(dedent` + GraphQL request:1:166 + 1 | query SomeMiniFiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String) + | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD. + | ..on THIRD_ERROR_HERE}}} + | ^ + `); + }); + it('prints single digit line number with no padding', () => { const result = printSourceLocation( new Source('*', 'Test', { line: 9, column: 1 }), diff --git a/src/language/printLocation.js b/src/language/printLocation.js index 2bc8ea42f2..7cff1602fb 100644 --- a/src/language/printLocation.js +++ b/src/language/printLocation.js @@ -30,14 +30,37 @@ export function printSourceLocation( const columnOffset = sourceLocation.line === 1 ? firstLineColumnOffset : 0; const columnNum = sourceLocation.column + columnOffset; + const locationStr = `${source.name}:${lineNum}:${columnNum}\n`; const lines = body.split(/\r\n|[\n\r]/g); + const locationLine = lines[lineIndex]; + + // Special case for minified documents + if (locationLine.length > 120) { + const sublineIndex = Math.floor(columnNum / 80); + const sublineColumnNum = columnNum % 80; + const sublines = []; + for (let i = 0; i < locationLine.length; i += 80) { + sublines.push(locationLine.slice(i, i + 80)); + } + + return ( + locationStr + + printPrefixedLines([ + [`${lineNum}`, sublines[0]], + ...sublines.slice(1, sublineIndex + 1).map(subline => ['', subline]), + [' ', whitespace(sublineColumnNum - 1) + '^'], + ['', sublines[sublineIndex + 1]], + ]) + ); + } + return ( - `${source.name}:${lineNum}:${columnNum}\n` + + locationStr + printPrefixedLines([ // Lines specified like this: ["prefix", "string"], [`${lineNum - 1}`, lines[lineIndex - 1]], - [`${lineNum}`, lines[lineIndex]], + [`${lineNum}`, locationLine], ['', whitespace(columnNum - 1) + '^'], [`${lineNum + 1}`, lines[lineIndex + 1]], ])