graphql-language-service-server
Version:
Server process backing the GraphQL Language Service
174 lines • 6.42 kB
JavaScript
import { Position, Range } from 'graphql-language-service';
import { TAG_MAP } from './constants';
import { parserMap } from './parsers';
export async function findGraphQLTags(text, ext, uri, logger) {
const result = [];
let rangeMapper = (range) => range;
const parser = parserMap[ext];
const parserResult = await parser(text, uri, logger);
if (!parserResult) {
return [];
}
if (parserResult === null || parserResult === void 0 ? void 0 : parserResult.rangeMapper) {
rangeMapper = parserResult.rangeMapper;
}
const { asts } = parserResult;
if (!(asts === null || asts === void 0 ? void 0 : asts.length)) {
return [];
}
const visitors = {
CallExpression(node) {
if (!('callee' in node)) {
return;
}
const { callee } = node;
if (callee.type === 'Identifier' &&
getGraphQLTagName(callee) &&
'arguments' in node) {
const templateLiteral = node.arguments[0];
if (templateLiteral &&
(templateLiteral.type === 'TemplateLiteral' ||
templateLiteral.type === 'TaggedTemplateExpression')) {
const parsed = parseTemplateLiteral(templateLiteral, rangeMapper);
if (parsed) {
result.push(parsed);
}
}
}
traverse(node, visitors);
},
TaggedTemplateExpression(node) {
var _a;
const tagName = getGraphQLTagName(node.tag);
if (tagName) {
const { loc } = node.quasi.quasis[0];
const template = node.quasi.quasis.length > 1
? node.quasi.quasis
.map((quasi, i) => {
var _a;
return i === ((_a = node.quasi.quasis) === null || _a === void 0 ? void 0 : _a.length) - 1
? quasi.value.raw
: getReplacementString(quasi.value.raw, node.quasi.quasis[i + 1].value.raw);
})
.join('')
: node.quasi.quasis[0].value.raw;
if (loc && node.quasi.quasis.length > 1) {
const last = node.quasi.quasis.pop();
if ((_a = last === null || last === void 0 ? void 0 : last.loc) === null || _a === void 0 ? void 0 : _a.end) {
loc.end = last.loc.end;
}
}
if (loc) {
const range = rangeMapper(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: template.endsWith('\n')
? template.slice(0, template.length - 1)
: template,
range,
});
}
}
},
TemplateLiteral(node) {
var _a, _b;
const hasGraphQLPrefix = node.quasis[0].value.raw.startsWith('#graphql\n');
const hasGraphQLComment = Boolean((_b = (_a = node.leadingComments) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value.match(/^\s*GraphQL\s*$/));
if (hasGraphQLPrefix || hasGraphQLComment) {
const parsed = parseTemplateLiteral(node, rangeMapper);
if (parsed) {
result.push(parsed);
}
}
},
};
for (const ast of asts) {
visit(ast, visitors);
}
return result;
}
const getReplacementString = (quasi, nextQuasi) => {
const trimmed = quasi.trimEnd();
const trimmedNext = nextQuasi.trimStart();
if (trimmed.endsWith('{') && trimmedNext.startsWith('}')) {
return quasi + '__typename';
}
return quasi;
};
function parseTemplateLiteral(node, rangeMapper) {
var _a;
const { loc } = node.quasis[0];
if (loc) {
if (node.quasis.length > 1) {
const quasis = [...node.quasis];
const last = quasis.pop();
if ((_a = last === null || last === void 0 ? void 0 : last.loc) === null || _a === void 0 ? void 0 : _a.end) {
loc.end = last.loc.end;
}
}
const template = node.quasis
.map((quasi, i) => {
var _a;
return i === ((_a = node.quasis) === null || _a === void 0 ? void 0 : _a.length) - 1
? quasi.value.raw
: getReplacementString(quasi.value.raw, node.quasis[i + 1].value.raw);
})
.join('');
const range = rangeMapper(new Range(new Position(loc.start.line - 1, loc.start.column), new Position(loc.end.line - 1, loc.end.column)));
return {
tag: '',
template,
range,
};
}
}
function getGraphQLTagName(tag) {
if (tag.type === 'Identifier' && TAG_MAP[tag.name]) {
return tag.name;
}
if (tag.type === 'MemberExpression' &&
tag.object.type === 'Identifier' &&
tag.object.name === 'graphql' &&
tag.property.type === 'Identifier' &&
tag.property.name === 'experimental') {
return 'graphql.experimental';
}
return null;
}
function visit(node, visitors) {
const fn = visitors[node.type];
if (fn && fn != null) {
fn(node);
return;
}
traverse(node, visitors);
}
const IGNORED_KEYS = {
comments: true,
end: true,
leadingComments: true,
loc: true,
name: true,
start: true,
trailingComments: true,
type: true,
};
function traverse(node, visitors) {
for (const key in node) {
if (IGNORED_KEYS[key]) {
continue;
}
const prop = node[key];
if (prop && typeof prop === 'object' && typeof prop.type === 'string') {
visit(prop, visitors);
}
else if (Array.isArray(prop)) {
for (const item of prop) {
if (item && typeof item === 'object' && typeof item.type === 'string') {
visit(item, visitors);
}
}
}
}
}
//# sourceMappingURL=findGraphQLTags.js.map