@tinacms/app 0.0.0-003e348-20251023020516

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,273 @@
1
+ import * as G from 'graphql';
2
+
3
+ export const expandQuery = ({
4
+ schema,
5
+ documentNode,
6
+ }: {
7
+ schema: G.GraphQLSchema;
8
+ documentNode: G.DocumentNode;
9
+ }): G.DocumentNode => {
10
+ const documentNodeWithTypenames = addTypenameToDocument(documentNode);
11
+ return addMetaFieldsToQuery(
12
+ documentNodeWithTypenames,
13
+ new G.TypeInfo(schema)
14
+ );
15
+ };
16
+
17
+ const addTypenameToDocument = (doc: G.DocumentNode) => {
18
+ function isField(selection: G.SelectionNode): selection is G.FieldNode {
19
+ return selection.kind === 'Field';
20
+ }
21
+ return G.visit(doc, {
22
+ SelectionSet: {
23
+ enter(node, _key, parent) {
24
+ // Don't add __typename to OperationDefinitions.
25
+ if (
26
+ parent &&
27
+ (parent as G.OperationDefinitionNode).kind ===
28
+ G.Kind.OPERATION_DEFINITION
29
+ ) {
30
+ return;
31
+ }
32
+
33
+ // No changes if no selections.
34
+ const { selections } = node;
35
+ if (!selections) {
36
+ return;
37
+ }
38
+
39
+ // If selections already have a __typename, or are part of an
40
+ // introspection query, do nothing.
41
+ const skip = selections.some((selection) => {
42
+ return (
43
+ isField(selection) &&
44
+ (selection.name.value === '__typename' ||
45
+ selection.name.value.lastIndexOf('__', 0) === 0)
46
+ );
47
+ });
48
+ if (skip) {
49
+ return;
50
+ }
51
+
52
+ // If this SelectionSet is @export-ed as an input variable, it should
53
+ // not have a __typename field (see issue #4691).
54
+ const field = parent as G.FieldNode;
55
+ if (
56
+ isField(field) &&
57
+ field.directives &&
58
+ field.directives.some((d) => d.name.value === 'export')
59
+ ) {
60
+ return;
61
+ }
62
+
63
+ // Create and return a new SelectionSet with a __typename Field.
64
+ return {
65
+ ...node,
66
+ selections: [...selections, TYPENAME_FIELD],
67
+ };
68
+ },
69
+ },
70
+ });
71
+ };
72
+
73
+ const CONTENT_SOURCE_FIELD: G.FieldNode = {
74
+ kind: G.Kind.FIELD,
75
+ name: {
76
+ kind: G.Kind.NAME,
77
+ value: '_content_source',
78
+ },
79
+ };
80
+ const METADATA_FIELD: G.FieldNode = {
81
+ kind: G.Kind.FIELD,
82
+ name: {
83
+ kind: G.Kind.NAME,
84
+ value: '_tina_metadata',
85
+ },
86
+ };
87
+
88
+ const TYPENAME_FIELD: G.FieldNode = {
89
+ kind: G.Kind.FIELD,
90
+ name: {
91
+ kind: G.Kind.NAME,
92
+ value: '__typename',
93
+ },
94
+ };
95
+
96
+ const addMetadataField = (
97
+ node: G.FieldNode | G.InlineFragmentNode | G.FragmentDefinitionNode
98
+ ): G.ASTNode => {
99
+ return {
100
+ ...node,
101
+ selectionSet: {
102
+ ...(node.selectionSet || {
103
+ kind: 'SelectionSet',
104
+ selections: [],
105
+ }),
106
+ selections: [
107
+ ...(node.selectionSet?.selections || []),
108
+ METADATA_FIELD,
109
+ CONTENT_SOURCE_FIELD,
110
+ ],
111
+ },
112
+ };
113
+ };
114
+
115
+ const addMetaFieldsToQuery = (
116
+ documentNode: G.DocumentNode,
117
+ typeInfo: G.TypeInfo
118
+ ) => {
119
+ const addMetaFields: G.VisitFn<G.ASTNode, G.FieldNode> = (
120
+ node: G.FieldNode
121
+ ): G.ASTNode => {
122
+ return {
123
+ ...node,
124
+ selectionSet: {
125
+ ...(node.selectionSet || {
126
+ kind: 'SelectionSet',
127
+ selections: [],
128
+ }),
129
+ selections: [...(node.selectionSet?.selections || []), ...metaFields],
130
+ },
131
+ };
132
+ };
133
+
134
+ const formifyVisitor: G.Visitor<G.ASTKindToNode, G.ASTNode> = {
135
+ FragmentDefinition: {
136
+ enter: (node, key, parent, path, ancestors) => {
137
+ typeInfo.enter(node);
138
+ const type = typeInfo.getType();
139
+ if (type) {
140
+ const namedType = G.getNamedType(type);
141
+ if (G.isObjectType(namedType)) {
142
+ if (namedType.getFields()['_tina_metadata']) {
143
+ return addMetadataField(node);
144
+ }
145
+ }
146
+ return node;
147
+ }
148
+ },
149
+ },
150
+ InlineFragment: {
151
+ enter: (node, key, parent, path, ancestors) => {
152
+ typeInfo.enter(node);
153
+ const type = typeInfo.getType();
154
+ if (type) {
155
+ const namedType = G.getNamedType(type);
156
+ if (G.isObjectType(namedType)) {
157
+ if (namedType.getFields()['_tina_metadata']) {
158
+ return addMetadataField(node);
159
+ }
160
+ }
161
+ return node;
162
+ }
163
+ },
164
+ },
165
+ Field: {
166
+ enter: (node, key, parent, path, ancestors) => {
167
+ typeInfo.enter(node);
168
+ const type = typeInfo.getType();
169
+ if (type) {
170
+ if (isNodeType(type)) {
171
+ return addMetaFields(node, key, parent, path, ancestors);
172
+ }
173
+ const namedType = G.getNamedType(type);
174
+ if (G.isObjectType(namedType)) {
175
+ if (namedType.getFields()['_tina_metadata']) {
176
+ return addMetadataField(node);
177
+ }
178
+ return node;
179
+ }
180
+ }
181
+ return node;
182
+ },
183
+ },
184
+ };
185
+ return G.visit(documentNode, G.visitWithTypeInfo(typeInfo, formifyVisitor));
186
+ };
187
+ /**
188
+ * This is a dummy query which we pull apart and spread
189
+ * back into the the selectionSet for all "Node" fields
190
+ */
191
+ const node = G.parse(`
192
+ query Sample {
193
+ ...on Document {
194
+ _internalValues: _values
195
+ _internalSys: _sys {
196
+ breadcrumbs
197
+ basename
198
+ filename
199
+ path
200
+ extension
201
+ relativePath
202
+ title
203
+ template
204
+ collection {
205
+ name
206
+ slug
207
+ label
208
+ path
209
+ format
210
+ matches
211
+ templates
212
+ fields
213
+ }
214
+ }
215
+ }
216
+ }`);
217
+ const metaFields: G.SelectionNode[] =
218
+ // @ts-ignore
219
+ node.definitions[0].selectionSet.selections;
220
+
221
+ export const isNodeType = (type: G.GraphQLOutputType) => {
222
+ const namedType = G.getNamedType(type);
223
+ if (G.isInterfaceType(namedType)) {
224
+ if (namedType.name === 'Node') {
225
+ return true;
226
+ }
227
+ }
228
+ if (G.isUnionType(namedType)) {
229
+ const types = namedType.getTypes();
230
+ if (
231
+ types.every((type) => {
232
+ return type.getInterfaces().some((intfc) => intfc.name === 'Node');
233
+ })
234
+ ) {
235
+ return true;
236
+ }
237
+ }
238
+ if (G.isObjectType(namedType)) {
239
+ if (namedType.getInterfaces().some((intfc) => intfc.name === 'Node')) {
240
+ return true;
241
+ }
242
+ }
243
+ return false;
244
+ };
245
+
246
+ export const isConnectionType = (type: G.GraphQLOutputType) => {
247
+ const namedType = G.getNamedType(type);
248
+ if (G.isInterfaceType(namedType)) {
249
+ if (namedType.name === 'Connection') {
250
+ return true;
251
+ }
252
+ }
253
+ if (G.isUnionType(namedType)) {
254
+ const types = namedType.getTypes();
255
+ if (
256
+ types.every((type) => {
257
+ return type
258
+ .getInterfaces()
259
+ .some((intfc) => intfc.name === 'Connection');
260
+ })
261
+ ) {
262
+ return true;
263
+ }
264
+ }
265
+ if (G.isObjectType(namedType)) {
266
+ if (
267
+ namedType.getInterfaces().some((intfc) => intfc.name === 'Connection')
268
+ ) {
269
+ return true;
270
+ }
271
+ }
272
+ return false;
273
+ };