@graphql-tools/documents
Version:
Utilities for GraphQL documents.
106 lines (105 loc) • 3.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.sortExecutableNodes = sortExecutableNodes;
const graphql_1 = require("graphql");
const normalize_whitespace_js_1 = require("./normalize-whitespace.js");
// Cache the sorted nodes to avoid sorting the same nodes multiple times
const nodeSortCache = new WeakMap();
function sortExecutableNodes(nodes) {
if (nodes) {
const shortcutNodes = nodeSortCache.get(nodes);
if (shortcutNodes) {
return shortcutNodes;
}
const cacheResult = (resultNodes) => {
nodeSortCache.set(nodes, resultNodes);
return resultNodes;
};
if (nodes.length === 0) {
return [];
}
if (isOfKindList(nodes, graphql_1.Kind.DIRECTIVE)) {
return cacheResult(sortNodesByStringKey(nodes, node => node.name.value));
}
if (isOfKindList(nodes, graphql_1.Kind.VARIABLE_DEFINITION)) {
return cacheResult(sortNodesByStringKey(nodes, node => node.variable.name.value));
}
if (isOfKindList(nodes, graphql_1.Kind.ARGUMENT)) {
return cacheResult(sortNodesByStringKey(nodes, node => node.name.value));
}
if (isOfKindList(nodes, [graphql_1.Kind.FIELD, graphql_1.Kind.FRAGMENT_SPREAD, graphql_1.Kind.INLINE_FRAGMENT])) {
return cacheResult(sortNodesByStringKey(nodes, node => {
if (node.kind === graphql_1.Kind.FIELD) {
return sortPrefixField + node.name.value;
}
else if (node.kind === graphql_1.Kind.FRAGMENT_SPREAD) {
return sortPrefixFragmentSpread + node.name.value;
}
else {
const typeCondition = node.typeCondition?.name.value ?? '';
// if you have a better idea, send a PR :)
const sortedNodes = buildInlineFragmentSelectionSetKey(cacheResult(sortExecutableNodes(node.selectionSet.selections)));
return sortPrefixInlineFragmentNode + typeCondition + sortedNodes;
}
}));
}
return cacheResult(nodes
.map((node, index) => ({
node,
index,
kind: node.kind,
name: getNodeNameValue(node),
}))
.sort((a, b) => {
const kindComparison = compareKeys(a.kind, b.kind);
if (kindComparison !== 0) {
return kindComparison;
}
const nameComparison = compareKeys(a.name, b.name);
if (nameComparison !== 0) {
return nameComparison;
}
return a.index - b.index;
})
.map(item => item.node));
}
}
const sortPrefixField = '0';
const sortPrefixFragmentSpread = '1';
const sortPrefixInlineFragmentNode = '2';
function isOfKindList(nodes, kind) {
return typeof kind === 'string' ? nodes[0].kind === kind : kind.includes(nodes[0].kind);
}
function buildInlineFragmentSelectionSetKey(nodes) {
return (0, normalize_whitespace_js_1.normalizeWhiteSpace)(nodes.map(node => (0, graphql_1.print)(node)).join(' '));
}
function sortNodesByStringKey(nodes, getKey) {
return nodes
.map((node, index) => ({ node, index, key: getKey(node) }))
.sort((a, b) => {
const keyComparison = compareKeys(a.key, b.key);
if (keyComparison !== 0) {
return keyComparison;
}
return a.index - b.index;
})
.map(item => item.node);
}
function compareKeys(a, b) {
if (a == null) {
return b == null ? 0 : 1;
}
if (b == null) {
return -1;
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
function getNodeNameValue(node) {
return node.name?.value;
}