@apollo/client
Version:
A fully-featured caching GraphQL client.
358 lines • 14 kB
JavaScript
import { __assign, __spreadArray } from "tslib";
import { invariant } from "../globals/index.js";
import { visit, Kind, } from 'graphql';
import { checkDocument, getOperationDefinition, getFragmentDefinition, getFragmentDefinitions, getMainDefinition, } from "./getFromAST.js";
import { isField } from "./storeUtils.js";
import { createFragmentMap, } from "./fragments.js";
import { isArray } from "../common/arrays.js";
var TYPENAME_FIELD = {
kind: Kind.FIELD,
name: {
kind: Kind.NAME,
value: '__typename',
},
};
function isEmpty(op, fragmentMap) {
return !op || op.selectionSet.selections.every(function (selection) { return selection.kind === Kind.FRAGMENT_SPREAD &&
isEmpty(fragmentMap[selection.name.value], fragmentMap); });
}
function nullIfDocIsEmpty(doc) {
return isEmpty(getOperationDefinition(doc) || getFragmentDefinition(doc), createFragmentMap(getFragmentDefinitions(doc)))
? null
: doc;
}
function getDirectiveMatcher(directives) {
var nameSet = new Set();
var tests = [];
directives.forEach(function (directive) {
if (directive.name) {
nameSet.add(directive.name);
}
else if (directive.test) {
tests.push(directive.test);
}
});
return function (directive) { return (nameSet.has(directive.name.value) ||
tests.some(function (test) { return test(directive); })); };
}
function makeInUseGetterFunction(defaultKey) {
var map = new Map();
return function inUseGetterFunction(key) {
if (key === void 0) { key = defaultKey; }
var inUse = map.get(key);
if (!inUse) {
map.set(key, inUse = {
variables: new Set,
fragmentSpreads: new Set,
});
}
return inUse;
};
}
export function removeDirectivesFromDocument(directives, doc) {
var getInUseByOperationName = makeInUseGetterFunction("");
var getInUseByFragmentName = makeInUseGetterFunction("");
var getInUse = function (ancestors) {
for (var p = 0, ancestor = void 0; p < ancestors.length && (ancestor = ancestors[p]); ++p) {
if (isArray(ancestor))
continue;
if (ancestor.kind === Kind.OPERATION_DEFINITION) {
return getInUseByOperationName(ancestor.name && ancestor.name.value);
}
if (ancestor.kind === Kind.FRAGMENT_DEFINITION) {
return getInUseByFragmentName(ancestor.name.value);
}
}
__DEV__ && invariant.error("Could not find operation or fragment");
return null;
};
var operationCount = 0;
for (var i = doc.definitions.length - 1; i >= 0; --i) {
if (doc.definitions[i].kind === Kind.OPERATION_DEFINITION) {
++operationCount;
}
}
var directiveMatcher = getDirectiveMatcher(directives);
var hasRemoveDirective = directives.some(function (directive) { return directive.remove; });
var shouldRemoveField = function (nodeDirectives) { return (hasRemoveDirective &&
nodeDirectives &&
nodeDirectives.some(directiveMatcher)); };
var originalFragmentDefsByPath = new Map();
var firstVisitMadeChanges = false;
var fieldOrInlineFragmentVisitor = {
enter: function (node) {
if (shouldRemoveField(node.directives)) {
firstVisitMadeChanges = true;
return null;
}
},
};
var docWithoutDirectiveSubtrees = visit(doc, {
Field: fieldOrInlineFragmentVisitor,
InlineFragment: fieldOrInlineFragmentVisitor,
VariableDefinition: {
enter: function () {
return false;
},
},
Variable: {
enter: function (node, _key, _parent, _path, ancestors) {
var inUse = getInUse(ancestors);
if (inUse) {
inUse.variables.add(node.name.value);
}
},
},
FragmentSpread: {
enter: function (node, _key, _parent, _path, ancestors) {
if (shouldRemoveField(node.directives)) {
firstVisitMadeChanges = true;
return null;
}
var inUse = getInUse(ancestors);
if (inUse) {
inUse.fragmentSpreads.add(node.name.value);
}
},
},
FragmentDefinition: {
enter: function (node, _key, _parent, path) {
originalFragmentDefsByPath.set(JSON.stringify(path), node);
},
leave: function (node, _key, _parent, path) {
var originalNode = originalFragmentDefsByPath.get(JSON.stringify(path));
if (node === originalNode) {
return node;
}
if (operationCount > 0 &&
node.selectionSet.selections.every(function (selection) { return (selection.kind === Kind.FIELD &&
selection.name.value === '__typename'); })) {
getInUseByFragmentName(node.name.value).removed = true;
firstVisitMadeChanges = true;
return null;
}
},
},
Directive: {
leave: function (node) {
if (directiveMatcher(node)) {
firstVisitMadeChanges = true;
return null;
}
},
},
});
if (!firstVisitMadeChanges) {
return doc;
}
var populateTransitiveVars = function (inUse) {
if (!inUse.transitiveVars) {
inUse.transitiveVars = new Set(inUse.variables);
if (!inUse.removed) {
inUse.fragmentSpreads.forEach(function (childFragmentName) {
populateTransitiveVars(getInUseByFragmentName(childFragmentName)).transitiveVars.forEach(function (varName) {
inUse.transitiveVars.add(varName);
});
});
}
}
return inUse;
};
var allFragmentNamesUsed = new Set();
docWithoutDirectiveSubtrees.definitions.forEach(function (def) {
if (def.kind === Kind.OPERATION_DEFINITION) {
populateTransitiveVars(getInUseByOperationName(def.name && def.name.value)).fragmentSpreads.forEach(function (childFragmentName) {
allFragmentNamesUsed.add(childFragmentName);
});
}
else if (def.kind === Kind.FRAGMENT_DEFINITION &&
operationCount === 0 &&
!getInUseByFragmentName(def.name.value).removed) {
allFragmentNamesUsed.add(def.name.value);
}
});
allFragmentNamesUsed.forEach(function (fragmentName) {
populateTransitiveVars(getInUseByFragmentName(fragmentName)).fragmentSpreads.forEach(function (childFragmentName) {
allFragmentNamesUsed.add(childFragmentName);
});
});
var fragmentWillBeRemoved = function (fragmentName) { return !!(!allFragmentNamesUsed.has(fragmentName) ||
getInUseByFragmentName(fragmentName).removed); };
var enterVisitor = {
enter: function (node) {
if (fragmentWillBeRemoved(node.name.value)) {
return null;
}
},
};
return nullIfDocIsEmpty(visit(docWithoutDirectiveSubtrees, {
FragmentSpread: enterVisitor,
FragmentDefinition: enterVisitor,
OperationDefinition: {
leave: function (node) {
if (node.variableDefinitions) {
var usedVariableNames_1 = populateTransitiveVars(getInUseByOperationName(node.name && node.name.value)).transitiveVars;
if (usedVariableNames_1.size < node.variableDefinitions.length) {
return __assign(__assign({}, node), { variableDefinitions: node.variableDefinitions.filter(function (varDef) { return usedVariableNames_1.has(varDef.variable.name.value); }) });
}
}
},
},
}));
}
export var addTypenameToDocument = Object.assign(function (doc) {
return visit(doc, {
SelectionSet: {
enter: function (node, _key, parent) {
if (parent &&
parent.kind === Kind.OPERATION_DEFINITION) {
return;
}
var selections = node.selections;
if (!selections) {
return;
}
var skip = selections.some(function (selection) {
return (isField(selection) &&
(selection.name.value === '__typename' ||
selection.name.value.lastIndexOf('__', 0) === 0));
});
if (skip) {
return;
}
var field = parent;
if (isField(field) &&
field.directives &&
field.directives.some(function (d) { return d.name.value === 'export'; })) {
return;
}
return __assign(__assign({}, node), { selections: __spreadArray(__spreadArray([], selections, true), [TYPENAME_FIELD], false) });
},
},
});
}, {
added: function (field) {
return field === TYPENAME_FIELD;
},
});
var connectionRemoveConfig = {
test: function (directive) {
var willRemove = directive.name.value === 'connection';
if (willRemove) {
if (!directive.arguments ||
!directive.arguments.some(function (arg) { return arg.name.value === 'key'; })) {
__DEV__ && invariant.warn('Removing an @connection directive even though it does not have a key. ' +
'You may want to use the key parameter to specify a store key.');
}
}
return willRemove;
},
};
export function removeConnectionDirectiveFromDocument(doc) {
return removeDirectivesFromDocument([connectionRemoveConfig], checkDocument(doc));
}
function hasDirectivesInSelectionSet(directives, selectionSet, nestedCheck) {
if (nestedCheck === void 0) { nestedCheck = true; }
return (!!selectionSet &&
selectionSet.selections &&
selectionSet.selections.some(function (selection) {
return hasDirectivesInSelection(directives, selection, nestedCheck);
}));
}
function hasDirectivesInSelection(directives, selection, nestedCheck) {
if (nestedCheck === void 0) { nestedCheck = true; }
if (!isField(selection)) {
return true;
}
if (!selection.directives) {
return false;
}
return (selection.directives.some(getDirectiveMatcher(directives)) ||
(nestedCheck &&
hasDirectivesInSelectionSet(directives, selection.selectionSet, nestedCheck)));
}
function getArgumentMatcher(config) {
return function argumentMatcher(argument) {
return config.some(function (aConfig) {
return argument.value &&
argument.value.kind === Kind.VARIABLE &&
argument.value.name &&
(aConfig.name === argument.value.name.value ||
(aConfig.test && aConfig.test(argument)));
});
};
}
export function removeArgumentsFromDocument(config, doc) {
var argMatcher = getArgumentMatcher(config);
return nullIfDocIsEmpty(visit(doc, {
OperationDefinition: {
enter: function (node) {
return __assign(__assign({}, node), { variableDefinitions: node.variableDefinitions ? node.variableDefinitions.filter(function (varDef) {
return !config.some(function (arg) { return arg.name === varDef.variable.name.value; });
}) : [] });
},
},
Field: {
enter: function (node) {
var shouldRemoveField = config.some(function (argConfig) { return argConfig.remove; });
if (shouldRemoveField) {
var argMatchCount_1 = 0;
if (node.arguments) {
node.arguments.forEach(function (arg) {
if (argMatcher(arg)) {
argMatchCount_1 += 1;
}
});
}
if (argMatchCount_1 === 1) {
return null;
}
}
},
},
Argument: {
enter: function (node) {
if (argMatcher(node)) {
return null;
}
},
},
}));
}
export function removeFragmentSpreadFromDocument(config, doc) {
function enter(node) {
if (config.some(function (def) { return def.name === node.name.value; })) {
return null;
}
}
return nullIfDocIsEmpty(visit(doc, {
FragmentSpread: { enter: enter },
FragmentDefinition: { enter: enter },
}));
}
export function buildQueryFromSelectionSet(document) {
var definition = getMainDefinition(document);
var definitionOperation = definition.operation;
if (definitionOperation === 'query') {
return document;
}
var modifiedDoc = visit(document, {
OperationDefinition: {
enter: function (node) {
return __assign(__assign({}, node), { operation: 'query' });
},
},
});
return modifiedDoc;
}
export function removeClientSetsFromDocument(document) {
checkDocument(document);
var modifiedDoc = removeDirectivesFromDocument([
{
test: function (directive) { return directive.name.value === 'client'; },
remove: true,
},
], document);
return modifiedDoc;
}
//# sourceMappingURL=transform.js.map