1 | import { __assign, __spreadArray } from "tslib";
|
2 | import { invariant } from "../globals/index.js";
|
3 | import { visit, Kind, } from 'graphql';
|
4 | import { checkDocument, getOperationDefinition, getFragmentDefinition, getFragmentDefinitions, getMainDefinition, } from "./getFromAST.js";
|
5 | import { isField } from "./storeUtils.js";
|
6 | import { createFragmentMap, } from "./fragments.js";
|
7 | import { isArray } from "../common/arrays.js";
|
8 | var TYPENAME_FIELD = {
|
9 | kind: Kind.FIELD,
|
10 | name: {
|
11 | kind: Kind.NAME,
|
12 | value: '__typename',
|
13 | },
|
14 | };
|
15 | function isEmpty(op, fragmentMap) {
|
16 | return !op || op.selectionSet.selections.every(function (selection) { return selection.kind === Kind.FRAGMENT_SPREAD &&
|
17 | isEmpty(fragmentMap[selection.name.value], fragmentMap); });
|
18 | }
|
19 | function nullIfDocIsEmpty(doc) {
|
20 | return isEmpty(getOperationDefinition(doc) || getFragmentDefinition(doc), createFragmentMap(getFragmentDefinitions(doc)))
|
21 | ? null
|
22 | : doc;
|
23 | }
|
24 | function getDirectiveMatcher(directives) {
|
25 | var nameSet = new Set();
|
26 | var tests = [];
|
27 | directives.forEach(function (directive) {
|
28 | if (directive.name) {
|
29 | nameSet.add(directive.name);
|
30 | }
|
31 | else if (directive.test) {
|
32 | tests.push(directive.test);
|
33 | }
|
34 | });
|
35 | return function (directive) { return (nameSet.has(directive.name.value) ||
|
36 | tests.some(function (test) { return test(directive); })); };
|
37 | }
|
38 | function makeInUseGetterFunction(defaultKey) {
|
39 | var map = new Map();
|
40 | return function inUseGetterFunction(key) {
|
41 | if (key === void 0) { key = defaultKey; }
|
42 | var inUse = map.get(key);
|
43 | if (!inUse) {
|
44 | map.set(key, inUse = {
|
45 | variables: new Set,
|
46 | fragmentSpreads: new Set,
|
47 | });
|
48 | }
|
49 | return inUse;
|
50 | };
|
51 | }
|
52 | export function removeDirectivesFromDocument(directives, doc) {
|
53 | var getInUseByOperationName = makeInUseGetterFunction("");
|
54 | var getInUseByFragmentName = makeInUseGetterFunction("");
|
55 | var getInUse = function (ancestors) {
|
56 | for (var p = 0, ancestor = void 0; p < ancestors.length && (ancestor = ancestors[p]); ++p) {
|
57 | if (isArray(ancestor))
|
58 | continue;
|
59 | if (ancestor.kind === Kind.OPERATION_DEFINITION) {
|
60 | return getInUseByOperationName(ancestor.name && ancestor.name.value);
|
61 | }
|
62 | if (ancestor.kind === Kind.FRAGMENT_DEFINITION) {
|
63 | return getInUseByFragmentName(ancestor.name.value);
|
64 | }
|
65 | }
|
66 | __DEV__ && invariant.error("Could not find operation or fragment");
|
67 | return null;
|
68 | };
|
69 | var operationCount = 0;
|
70 | for (var i = doc.definitions.length - 1; i >= 0; --i) {
|
71 | if (doc.definitions[i].kind === Kind.OPERATION_DEFINITION) {
|
72 | ++operationCount;
|
73 | }
|
74 | }
|
75 | var directiveMatcher = getDirectiveMatcher(directives);
|
76 | var hasRemoveDirective = directives.some(function (directive) { return directive.remove; });
|
77 | var shouldRemoveField = function (nodeDirectives) { return (hasRemoveDirective &&
|
78 | nodeDirectives &&
|
79 | nodeDirectives.some(directiveMatcher)); };
|
80 | var originalFragmentDefsByPath = new Map();
|
81 | var firstVisitMadeChanges = false;
|
82 | var fieldOrInlineFragmentVisitor = {
|
83 | enter: function (node) {
|
84 | if (shouldRemoveField(node.directives)) {
|
85 | firstVisitMadeChanges = true;
|
86 | return null;
|
87 | }
|
88 | },
|
89 | };
|
90 | var docWithoutDirectiveSubtrees = visit(doc, {
|
91 | Field: fieldOrInlineFragmentVisitor,
|
92 | InlineFragment: fieldOrInlineFragmentVisitor,
|
93 | VariableDefinition: {
|
94 | enter: function () {
|
95 | return false;
|
96 | },
|
97 | },
|
98 | Variable: {
|
99 | enter: function (node, _key, _parent, _path, ancestors) {
|
100 | var inUse = getInUse(ancestors);
|
101 | if (inUse) {
|
102 | inUse.variables.add(node.name.value);
|
103 | }
|
104 | },
|
105 | },
|
106 | FragmentSpread: {
|
107 | enter: function (node, _key, _parent, _path, ancestors) {
|
108 | if (shouldRemoveField(node.directives)) {
|
109 | firstVisitMadeChanges = true;
|
110 | return null;
|
111 | }
|
112 | var inUse = getInUse(ancestors);
|
113 | if (inUse) {
|
114 | inUse.fragmentSpreads.add(node.name.value);
|
115 | }
|
116 | },
|
117 | },
|
118 | FragmentDefinition: {
|
119 | enter: function (node, _key, _parent, path) {
|
120 | originalFragmentDefsByPath.set(JSON.stringify(path), node);
|
121 | },
|
122 | leave: function (node, _key, _parent, path) {
|
123 | var originalNode = originalFragmentDefsByPath.get(JSON.stringify(path));
|
124 | if (node === originalNode) {
|
125 | return node;
|
126 | }
|
127 | if (operationCount > 0 &&
|
128 | node.selectionSet.selections.every(function (selection) { return (selection.kind === Kind.FIELD &&
|
129 | selection.name.value === '__typename'); })) {
|
130 | getInUseByFragmentName(node.name.value).removed = true;
|
131 | firstVisitMadeChanges = true;
|
132 | return null;
|
133 | }
|
134 | },
|
135 | },
|
136 | Directive: {
|
137 | leave: function (node) {
|
138 | if (directiveMatcher(node)) {
|
139 | firstVisitMadeChanges = true;
|
140 | return null;
|
141 | }
|
142 | },
|
143 | },
|
144 | });
|
145 | if (!firstVisitMadeChanges) {
|
146 | return doc;
|
147 | }
|
148 | var populateTransitiveVars = function (inUse) {
|
149 | if (!inUse.transitiveVars) {
|
150 | inUse.transitiveVars = new Set(inUse.variables);
|
151 | if (!inUse.removed) {
|
152 | inUse.fragmentSpreads.forEach(function (childFragmentName) {
|
153 | populateTransitiveVars(getInUseByFragmentName(childFragmentName)).transitiveVars.forEach(function (varName) {
|
154 | inUse.transitiveVars.add(varName);
|
155 | });
|
156 | });
|
157 | }
|
158 | }
|
159 | return inUse;
|
160 | };
|
161 | var allFragmentNamesUsed = new Set();
|
162 | docWithoutDirectiveSubtrees.definitions.forEach(function (def) {
|
163 | if (def.kind === Kind.OPERATION_DEFINITION) {
|
164 | populateTransitiveVars(getInUseByOperationName(def.name && def.name.value)).fragmentSpreads.forEach(function (childFragmentName) {
|
165 | allFragmentNamesUsed.add(childFragmentName);
|
166 | });
|
167 | }
|
168 | else if (def.kind === Kind.FRAGMENT_DEFINITION &&
|
169 | operationCount === 0 &&
|
170 | !getInUseByFragmentName(def.name.value).removed) {
|
171 | allFragmentNamesUsed.add(def.name.value);
|
172 | }
|
173 | });
|
174 | allFragmentNamesUsed.forEach(function (fragmentName) {
|
175 | populateTransitiveVars(getInUseByFragmentName(fragmentName)).fragmentSpreads.forEach(function (childFragmentName) {
|
176 | allFragmentNamesUsed.add(childFragmentName);
|
177 | });
|
178 | });
|
179 | var fragmentWillBeRemoved = function (fragmentName) { return !!(!allFragmentNamesUsed.has(fragmentName) ||
|
180 | getInUseByFragmentName(fragmentName).removed); };
|
181 | var enterVisitor = {
|
182 | enter: function (node) {
|
183 | if (fragmentWillBeRemoved(node.name.value)) {
|
184 | return null;
|
185 | }
|
186 | },
|
187 | };
|
188 | return nullIfDocIsEmpty(visit(docWithoutDirectiveSubtrees, {
|
189 | FragmentSpread: enterVisitor,
|
190 | FragmentDefinition: enterVisitor,
|
191 | OperationDefinition: {
|
192 | leave: function (node) {
|
193 | if (node.variableDefinitions) {
|
194 | var usedVariableNames_1 = populateTransitiveVars(getInUseByOperationName(node.name && node.name.value)).transitiveVars;
|
195 | if (usedVariableNames_1.size < node.variableDefinitions.length) {
|
196 | return __assign(__assign({}, node), { variableDefinitions: node.variableDefinitions.filter(function (varDef) { return usedVariableNames_1.has(varDef.variable.name.value); }) });
|
197 | }
|
198 | }
|
199 | },
|
200 | },
|
201 | }));
|
202 | }
|
203 | export var addTypenameToDocument = Object.assign(function (doc) {
|
204 | return visit(doc, {
|
205 | SelectionSet: {
|
206 | enter: function (node, _key, parent) {
|
207 | if (parent &&
|
208 | parent.kind === Kind.OPERATION_DEFINITION) {
|
209 | return;
|
210 | }
|
211 | var selections = node.selections;
|
212 | if (!selections) {
|
213 | return;
|
214 | }
|
215 | var skip = selections.some(function (selection) {
|
216 | return (isField(selection) &&
|
217 | (selection.name.value === '__typename' ||
|
218 | selection.name.value.lastIndexOf('__', 0) === 0));
|
219 | });
|
220 | if (skip) {
|
221 | return;
|
222 | }
|
223 | var field = parent;
|
224 | if (isField(field) &&
|
225 | field.directives &&
|
226 | field.directives.some(function (d) { return d.name.value === 'export'; })) {
|
227 | return;
|
228 | }
|
229 | return __assign(__assign({}, node), { selections: __spreadArray(__spreadArray([], selections, true), [TYPENAME_FIELD], false) });
|
230 | },
|
231 | },
|
232 | });
|
233 | }, {
|
234 | added: function (field) {
|
235 | return field === TYPENAME_FIELD;
|
236 | },
|
237 | });
|
238 | var connectionRemoveConfig = {
|
239 | test: function (directive) {
|
240 | var willRemove = directive.name.value === 'connection';
|
241 | if (willRemove) {
|
242 | if (!directive.arguments ||
|
243 | !directive.arguments.some(function (arg) { return arg.name.value === 'key'; })) {
|
244 | __DEV__ && invariant.warn('Removing an @connection directive even though it does not have a key. ' +
|
245 | 'You may want to use the key parameter to specify a store key.');
|
246 | }
|
247 | }
|
248 | return willRemove;
|
249 | },
|
250 | };
|
251 | export function removeConnectionDirectiveFromDocument(doc) {
|
252 | return removeDirectivesFromDocument([connectionRemoveConfig], checkDocument(doc));
|
253 | }
|
254 | function hasDirectivesInSelectionSet(directives, selectionSet, nestedCheck) {
|
255 | if (nestedCheck === void 0) { nestedCheck = true; }
|
256 | return (!!selectionSet &&
|
257 | selectionSet.selections &&
|
258 | selectionSet.selections.some(function (selection) {
|
259 | return hasDirectivesInSelection(directives, selection, nestedCheck);
|
260 | }));
|
261 | }
|
262 | function hasDirectivesInSelection(directives, selection, nestedCheck) {
|
263 | if (nestedCheck === void 0) { nestedCheck = true; }
|
264 | if (!isField(selection)) {
|
265 | return true;
|
266 | }
|
267 | if (!selection.directives) {
|
268 | return false;
|
269 | }
|
270 | return (selection.directives.some(getDirectiveMatcher(directives)) ||
|
271 | (nestedCheck &&
|
272 | hasDirectivesInSelectionSet(directives, selection.selectionSet, nestedCheck)));
|
273 | }
|
274 | function getArgumentMatcher(config) {
|
275 | return function argumentMatcher(argument) {
|
276 | return config.some(function (aConfig) {
|
277 | return argument.value &&
|
278 | argument.value.kind === Kind.VARIABLE &&
|
279 | argument.value.name &&
|
280 | (aConfig.name === argument.value.name.value ||
|
281 | (aConfig.test && aConfig.test(argument)));
|
282 | });
|
283 | };
|
284 | }
|
285 | export function removeArgumentsFromDocument(config, doc) {
|
286 | var argMatcher = getArgumentMatcher(config);
|
287 | return nullIfDocIsEmpty(visit(doc, {
|
288 | OperationDefinition: {
|
289 | enter: function (node) {
|
290 | return __assign(__assign({}, node), { variableDefinitions: node.variableDefinitions ? node.variableDefinitions.filter(function (varDef) {
|
291 | return !config.some(function (arg) { return arg.name === varDef.variable.name.value; });
|
292 | }) : [] });
|
293 | },
|
294 | },
|
295 | Field: {
|
296 | enter: function (node) {
|
297 | var shouldRemoveField = config.some(function (argConfig) { return argConfig.remove; });
|
298 | if (shouldRemoveField) {
|
299 | var argMatchCount_1 = 0;
|
300 | if (node.arguments) {
|
301 | node.arguments.forEach(function (arg) {
|
302 | if (argMatcher(arg)) {
|
303 | argMatchCount_1 += 1;
|
304 | }
|
305 | });
|
306 | }
|
307 | if (argMatchCount_1 === 1) {
|
308 | return null;
|
309 | }
|
310 | }
|
311 | },
|
312 | },
|
313 | Argument: {
|
314 | enter: function (node) {
|
315 | if (argMatcher(node)) {
|
316 | return null;
|
317 | }
|
318 | },
|
319 | },
|
320 | }));
|
321 | }
|
322 | export function removeFragmentSpreadFromDocument(config, doc) {
|
323 | function enter(node) {
|
324 | if (config.some(function (def) { return def.name === node.name.value; })) {
|
325 | return null;
|
326 | }
|
327 | }
|
328 | return nullIfDocIsEmpty(visit(doc, {
|
329 | FragmentSpread: { enter: enter },
|
330 | FragmentDefinition: { enter: enter },
|
331 | }));
|
332 | }
|
333 | export function buildQueryFromSelectionSet(document) {
|
334 | var definition = getMainDefinition(document);
|
335 | var definitionOperation = definition.operation;
|
336 | if (definitionOperation === 'query') {
|
337 | return document;
|
338 | }
|
339 | var modifiedDoc = visit(document, {
|
340 | OperationDefinition: {
|
341 | enter: function (node) {
|
342 | return __assign(__assign({}, node), { operation: 'query' });
|
343 | },
|
344 | },
|
345 | });
|
346 | return modifiedDoc;
|
347 | }
|
348 | export function removeClientSetsFromDocument(document) {
|
349 | checkDocument(document);
|
350 | var modifiedDoc = removeDirectivesFromDocument([
|
351 | {
|
352 | test: function (directive) { return directive.name.value === 'client'; },
|
353 | remove: true,
|
354 | },
|
355 | ], document);
|
356 | return modifiedDoc;
|
357 | }
|
358 |
|
\ | No newline at end of file |