Skip to content

Commit

Permalink
support for template strings with replacement expressions (#2111)
Browse files Browse the repository at this point in the history
this adds some support for template strings with replacement expressions. this will get rid of the EOF parse error in many contexts when using replacements

they will be replaced by a blank whitespace character, newlines will remain intact

characters will be off for the same line

there are still some cases where daringly constructed template strings could break parsing for diagnostics, but in most common cases it should be fine!
  • Loading branch information
acao committed Dec 9, 2021
1 parent d5fca9d commit 08ff6dc
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/nasty-hornets-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'graphql-language-service-server': patch
---

Support template literals and tagged template literals with replacement expressions
1 change: 1 addition & 0 deletions packages/graphql-language-service-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"graphql": "^15.5.0 || ^16.0.0"
},
"dependencies": {
"@babel/types": "^7.16.0",
"@babel/parser": "^7.13.13",
"dotenv": "8.2.0",
"graphql-config": "^4.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,106 @@ query Test {
`);
});

it('parseDocument finds queries in multi-expression tagged templates using tsx', async () => {
const text = `
import {gql} from 'react-apollo';
import {B} from 'B';
import A from './A';
const someValue = 'value'
const QUERY: string = gql\`
query Test {
test {
value
$\{someValue}
...FragmentsComment
}
$\{someValue}
}\`
export function Example(arg: string) {
return <div>{QUERY}</div>
}`;

const contents = parseDocument(text, 'test.tsx');
expect(contents[0].query).toEqual(`
query Test {
test {
value
...FragmentsComment
}
}`);
});
// TODO: why an extra line here?
it('parseDocument finds queries in multi-expression tagged template with declarations with using tsx', async () => {
const text = `
import {gql} from 'react-apollo';
import {B} from 'B';
import A from './A';
const someValue = 'value'
type SomeType = { test: any }
const QUERY: string = gql<SomeType>\`
query Test {
test {
value
$\{someValue}
...FragmentsComment
}
$\{someValue}
}\`
export function Example(arg: string) {
return <div>{QUERY}</div>
}`;

const contents = parseDocument(text, 'test.tsx');
expect(contents[0].query).toEqual(`
query Test {
test {
value
...FragmentsComment
}
}`);
});

it('parseDocument finds queries in multi-expression template strings using tsx', async () => {
const text = `
import {gql} from 'react-apollo';
import {B} from 'B';
import A from './A';
const someValue = 'value'
const QUERY: string =
/* GraphQL */
\`
query Test {
test {
value
\${someValue}
...FragmentsComment
}
}
\${A.fragments.test}
\`
export function Example(arg: string) {
return <div>{QUERY}</div>
}`;

const contents = parseDocument(text, 'test.tsx');
expect(contents[0].query).toEqual(`
query Test {
test {
value
...FragmentsComment
}
}
`);
});

it('parseDocument finds queries in #graphql-annotated templates', async () => {
const text = `
import {gql} from 'react-apollo';
Expand Down
29 changes: 27 additions & 2 deletions packages/graphql-language-service-server/src/findGraphQLTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export function findGraphQLTags(text: string, ext: string): TagResult[] {
new Position(loc.start.line - 1, loc.start.column),
new Position(loc.end.line - 1, loc.end.column),
);

result.push({
tag: tagName,
template,
Expand All @@ -154,14 +155,27 @@ export function findGraphQLTags(text: string, ext: string): TagResult[] {
const tagName = getGraphQLTagName(node.tag);
if (tagName) {
const loc = node.quasi.quasis[0].loc;
const template =
node.quasi.quasis.length > 1
? node.quasi.quasis.map(quasi => quasi.value.raw).join('')
: node.quasi.quasis[0].value.raw;
if (loc && node.quasi.quasis.length > 1) {
const last = node.quasi.quasis.pop();
if (last?.loc?.end) {
loc.end = last.loc.end;
}
}
if (loc) {
const range = new Range(
new Position(loc.start.line - 1, loc.start.column),
new Position(loc.end.line - 1, loc.end.column),
);

result.push({
tag: tagName,
template: node.quasi.quasis[0].value.raw,
template: template.endsWith('\n')
? template.slice(0, template.length - 1)
: template,
range,
});
}
Expand All @@ -177,20 +191,31 @@ export function findGraphQLTags(text: string, ext: string): TagResult[] {
if (hasGraphQLPrefix || hasGraphQLComment) {
const loc = node.quasis[0].loc;
if (loc) {
if (node.quasis.length > 1) {
const last = node.quasis.pop();
if (last?.loc?.end) {
loc.end = last.loc.end;
}
}
const template =
node.quasis.length > 1
? node.quasis.map(quasi => quasi.value.raw).join('')
: node.quasis[0].value.raw;
const range = new Range(
new Position(loc.start.line - 1, loc.start.column),
new Position(loc.end.line - 1, loc.end.column),
);
result.push({
tag: '',
template: node.quasis[0].value.raw,
template,
range,
});
}
}
},
};
visit(ast, visitors);

return result;
}

Expand Down

2 comments on commit 08ff6dc

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.