@tinacms/app 2.2.2 → 2.2.3

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.
@@ -1,10 +1,10 @@
1
- import React from 'react'
2
- import * as G from 'graphql'
3
- import { getIn } from 'final-form'
4
- import { z } from 'zod'
1
+ import React from 'react';
2
+ import * as G from 'graphql';
3
+ import { getIn } from 'final-form';
4
+ import { z } from 'zod';
5
5
  // @ts-expect-error
6
- import schemaJson from 'SCHEMA_IMPORT'
7
- import { expandQuery, isConnectionType, isNodeType } from './expand-query'
6
+ import schemaJson from 'SCHEMA_IMPORT';
7
+ import { expandQuery, isConnectionType, isNodeType } from './expand-query';
8
8
  import {
9
9
  Form,
10
10
  TinaCMS,
@@ -20,17 +20,17 @@ import {
20
20
  GlobalFormPlugin,
21
21
  TinaState,
22
22
  ErrorDialog,
23
- } from 'tinacms'
24
- import { createForm, createGlobalForm, FormifyCallback } from './build-form'
23
+ } from 'tinacms';
24
+ import { createForm, createGlobalForm, FormifyCallback } from './build-form';
25
25
  import type {
26
26
  PostMessage,
27
27
  Payload,
28
28
  SystemInfo,
29
29
  ResolvedDocument,
30
- } from './types'
31
- import { getFormAndFieldNameFromMetadata } from './util'
32
- import { useSearchParams } from 'react-router-dom'
33
- import { showErrorModal } from './errors'
30
+ } from './types';
31
+ import { getFormAndFieldNameFromMetadata } from './util';
32
+ import { useSearchParams } from 'react-router-dom';
33
+ import { showErrorModal } from './errors';
34
34
 
35
35
  const sysSchema = z.object({
36
36
  breadcrumbs: z.array(z.string()),
@@ -50,14 +50,14 @@ const sysSchema = z.object({
50
50
  format: z.string().optional().nullable(),
51
51
  matches: z.string().optional().nullable(),
52
52
  }),
53
- })
53
+ });
54
54
 
55
55
  const documentSchema: z.ZodType<ResolvedDocument> = z.object({
56
56
  _internalValues: z.record(z.unknown()),
57
57
  _internalSys: sysSchema,
58
- })
58
+ });
59
59
 
60
- const astNode = schemaJson as G.DocumentNode
60
+ const astNode = schemaJson as G.DocumentNode;
61
61
  const astNodeWithMeta: G.DocumentNode = {
62
62
  ...astNode,
63
63
  definitions: astNode.definitions.map((def) => {
@@ -103,7 +103,7 @@ const astNodeWithMeta: G.DocumentNode = {
103
103
  },
104
104
  },
105
105
  ],
106
- }
106
+ };
107
107
  }
108
108
  if (def.kind === 'ObjectTypeDefinition') {
109
109
  return {
@@ -147,68 +147,68 @@ const astNodeWithMeta: G.DocumentNode = {
147
147
  },
148
148
  },
149
149
  ],
150
- }
150
+ };
151
151
  }
152
- return def
152
+ return def;
153
153
  }),
154
- }
155
- const schema = G.buildASTSchema(astNode)
156
- const schemaForResolver = G.buildASTSchema(astNodeWithMeta)
154
+ };
155
+ const schema = G.buildASTSchema(astNode);
156
+ const schemaForResolver = G.buildASTSchema(astNodeWithMeta);
157
157
 
158
158
  const isRejected = (
159
159
  input: PromiseSettledResult<unknown>
160
- ): input is PromiseRejectedResult => input.status === 'rejected'
160
+ ): input is PromiseRejectedResult => input.status === 'rejected';
161
161
 
162
162
  const isFulfilled = <T>(
163
163
  input: PromiseSettledResult<T>
164
- ): input is PromiseFulfilledResult<T> => input.status === 'fulfilled'
164
+ ): input is PromiseFulfilledResult<T> => input.status === 'fulfilled';
165
165
 
166
166
  export const useGraphQLReducer = (
167
167
  iframe: React.MutableRefObject<HTMLIFrameElement>,
168
168
  url: string
169
169
  ) => {
170
- const cms = useCMS()
171
- const tinaSchema = cms.api.tina.schema as TinaSchema
172
- const [payloads, setPayloads] = React.useState<Payload[]>([])
173
- const [requestErrors, setRequestErrors] = React.useState<string[]>([])
174
- const [searchParams, setSearchParams] = useSearchParams()
170
+ const cms = useCMS();
171
+ const tinaSchema = cms.api.tina.schema as TinaSchema;
172
+ const [payloads, setPayloads] = React.useState<Payload[]>([]);
173
+ const [requestErrors, setRequestErrors] = React.useState<string[]>([]);
174
+ const [searchParams, setSearchParams] = useSearchParams();
175
175
  const [results, setResults] = React.useState<
176
176
  {
177
- id: string
177
+ id: string;
178
178
  data:
179
179
  | {
180
- [key: string]: any
180
+ [key: string]: any;
181
181
  }
182
182
  | null
183
- | undefined
183
+ | undefined;
184
184
  }[]
185
- >([])
185
+ >([]);
186
186
  const [documentsToResolve, setDocumentsToResolve] = React.useState<string[]>(
187
187
  []
188
- )
188
+ );
189
189
  const [resolvedDocuments, setResolvedDocuments] = React.useState<
190
190
  ResolvedDocument[]
191
- >([])
192
- const [operationIndex, setOperationIndex] = React.useState(0)
191
+ >([]);
192
+ const [operationIndex, setOperationIndex] = React.useState(0);
193
193
 
194
- const activeField = searchParams.get('active-field')
194
+ const activeField = searchParams.get('active-field');
195
195
 
196
196
  React.useEffect(() => {
197
197
  const run = async () => {
198
198
  return Promise.all(
199
199
  documentsToResolve.map(async (documentId) => {
200
- return await getDocument(documentId, cms.api.tina)
200
+ return await getDocument(documentId, cms.api.tina);
201
201
  })
202
- )
203
- }
202
+ );
203
+ };
204
204
  if (documentsToResolve.length) {
205
205
  run().then((docs) => {
206
- setResolvedDocuments((resolvedDocs) => [...resolvedDocs, ...docs])
207
- setDocumentsToResolve([])
208
- setOperationIndex((i) => i + 1)
209
- })
206
+ setResolvedDocuments((resolvedDocs) => [...resolvedDocs, ...docs]);
207
+ setDocumentsToResolve([]);
208
+ setOperationIndex((i) => i + 1);
209
+ });
210
210
  }
211
- }, [documentsToResolve.join('.')])
211
+ }, [documentsToResolve.join('.')]);
212
212
 
213
213
  /**
214
214
  * Note: since React runs effects twice in development this will run twice for a given query
@@ -216,39 +216,41 @@ export const useGraphQLReducer = (
216
216
  */
217
217
  React.useEffect(() => {
218
218
  const run = async () => {
219
- setRequestErrors([])
219
+ setRequestErrors([]);
220
220
  // gather the errors and display an error message containing each error unique message
221
221
  return Promise.allSettled(
222
222
  payloads.map(async (payload) => {
223
223
  // This payload has already been expanded, skip it.
224
224
  if (payload.expandedQuery) {
225
- return payload
225
+ return payload;
226
226
  } else {
227
- const expandedPayload = await expandPayload(payload, cms)
228
- processPayload(expandedPayload)
229
- return expandedPayload
227
+ const expandedPayload = await expandPayload(payload, cms);
228
+ processPayload(expandedPayload);
229
+ return expandedPayload;
230
230
  }
231
231
  })
232
- )
233
- }
232
+ );
233
+ };
234
234
  if (payloads.length) {
235
235
  run().then((updatedPayloads) => {
236
- setPayloads(updatedPayloads.filter(isFulfilled).map((p) => p.value))
236
+ setPayloads(updatedPayloads.filter(isFulfilled).map((p) => p.value));
237
237
  setRequestErrors(
238
238
  updatedPayloads.filter(isRejected).map((p) => String(p.reason))
239
- )
240
- })
239
+ );
240
+ });
241
241
  }
242
- }, [JSON.stringify(payloads), cms])
242
+ }, [JSON.stringify(payloads), cms]);
243
243
 
244
244
  const processPayload = React.useCallback(
245
245
  (payload: Payload) => {
246
- const { expandedQueryForResolver, variables, expandedData } = payload
246
+ const { expandedQueryForResolver, variables, expandedData } = payload;
247
247
  if (!expandedQueryForResolver || !expandedData) {
248
- throw new Error(`Unable to process payload which has not been expanded`)
248
+ throw new Error(
249
+ `Unable to process payload which has not been expanded`
250
+ );
249
251
  }
250
- const formListItems: TinaState['formLists'][number]['items'] = []
251
- const formIds: string[] = []
252
+ const formListItems: TinaState['formLists'][number]['items'] = [];
253
+ const formIds: string[] = [];
252
254
 
253
255
  const result = G.graphqlSync({
254
256
  schema: schemaForResolver,
@@ -256,7 +258,7 @@ export const useGraphQLReducer = (
256
258
  variableValues: variables,
257
259
  rootValue: expandedData,
258
260
  fieldResolver: (source, args, context, info) => {
259
- const fieldName = info.fieldName
261
+ const fieldName = info.fieldName;
260
262
  /**
261
263
  * Since the `source` for this resolver is the query that
262
264
  * ran before passing it into `useTina`, we need to take aliases
@@ -265,35 +267,35 @@ export const useGraphQLReducer = (
265
267
  * solution as the `value` gets overwritten depending on the alias
266
268
  * query.
267
269
  */
268
- const aliases: string[] = []
270
+ const aliases: string[] = [];
269
271
  info.fieldNodes.forEach((fieldNode) => {
270
272
  if (fieldNode.alias) {
271
- aliases.push(fieldNode.alias.value)
273
+ aliases.push(fieldNode.alias.value);
272
274
  }
273
- })
274
- let value = source[fieldName] as unknown
275
+ });
276
+ let value = source[fieldName] as unknown;
275
277
  aliases.forEach((alias) => {
276
- const aliasValue = source[alias]
278
+ const aliasValue = source[alias];
277
279
  if (aliasValue) {
278
- value = aliasValue
280
+ value = aliasValue;
279
281
  }
280
- })
282
+ });
281
283
  if (fieldName === '_sys') {
282
- return source._internalSys
284
+ return source._internalSys;
283
285
  }
284
286
  if (fieldName === '_values') {
285
- return source._internalValues
287
+ return source._internalValues;
286
288
  }
287
289
  if (info.fieldName === '_content_source') {
288
- const pathArray = G.responsePathAsArray(info.path)
290
+ const pathArray = G.responsePathAsArray(info.path);
289
291
  return {
290
292
  queryId: payload.id,
291
293
  path: pathArray.slice(0, pathArray.length - 1),
292
- }
294
+ };
293
295
  }
294
296
  if (info.fieldName === '_tina_metadata') {
295
297
  if (value) {
296
- return value
298
+ return value;
297
299
  }
298
300
  // TODO: ensure all fields that have _tina_metadata
299
301
  // actually need it
@@ -301,77 +303,77 @@ export const useGraphQLReducer = (
301
303
  id: null,
302
304
  fields: [],
303
305
  prefix: '',
304
- }
306
+ };
305
307
  }
306
308
  if (isConnectionType(info.returnType)) {
307
- const name = G.getNamedType(info.returnType).name
309
+ const name = G.getNamedType(info.returnType).name;
308
310
  const connectionCollection = tinaSchema
309
311
  .getCollections()
310
312
  .find((collection) => {
311
313
  const collectionName = NAMER.referenceConnectionType(
312
314
  collection.namespace
313
- )
315
+ );
314
316
  if (collectionName === name) {
315
- return true
317
+ return true;
316
318
  }
317
- return false
318
- })
319
+ return false;
320
+ });
319
321
  if (connectionCollection) {
320
322
  formListItems.push({
321
323
  type: 'list',
322
324
  label: connectionCollection.label || connectionCollection.name,
323
- })
325
+ });
324
326
  }
325
327
  }
326
328
  if (isNodeType(info.returnType)) {
327
329
  if (!value) {
328
- return
330
+ return;
329
331
  }
330
- let resolvedDocument: ResolvedDocument
332
+ let resolvedDocument: ResolvedDocument;
331
333
  // This is a reference from another form
332
334
  if (typeof value === 'string') {
333
335
  const valueFromSetup = getIn(
334
336
  expandedData,
335
337
  G.responsePathAsArray(info.path).join('.')
336
- )
338
+ );
337
339
  const maybeResolvedDocument = resolvedDocuments.find(
338
340
  (doc) => doc._internalSys.path === value
339
- )
341
+ );
340
342
  // If we already have this document, use it.
341
343
  if (maybeResolvedDocument) {
342
- resolvedDocument = maybeResolvedDocument
344
+ resolvedDocument = maybeResolvedDocument;
343
345
  } else if (valueFromSetup) {
344
346
  // Else, even though in this context the value is a string because it's
345
347
  // resolved from a parent form, if the reference hasn't changed
346
348
  // from when we ran the setup query, we can avoid a data fetch
347
349
  // here and just grab it from the response
348
350
  const maybeResolvedDocument =
349
- documentSchema.parse(valueFromSetup)
351
+ documentSchema.parse(valueFromSetup);
350
352
  if (maybeResolvedDocument._internalSys.path === value) {
351
- resolvedDocument = maybeResolvedDocument
353
+ resolvedDocument = maybeResolvedDocument;
352
354
  } else {
353
- throw new NoFormError(`No form found`, value)
355
+ throw new NoFormError(`No form found`, value);
354
356
  }
355
357
  } else {
356
- throw new NoFormError(`No form found`, value)
358
+ throw new NoFormError(`No form found`, value);
357
359
  }
358
360
  } else {
359
- resolvedDocument = documentSchema.parse(value)
361
+ resolvedDocument = documentSchema.parse(value);
360
362
  }
361
- const id = resolvedDocument._internalSys.path
362
- formIds.push(id)
363
+ const id = resolvedDocument._internalSys.path;
364
+ formIds.push(id);
363
365
  const existingForm = cms.state.forms.find(
364
366
  (f) => f.tinaForm.id === id
365
- )
367
+ );
366
368
 
367
- const pathArray = G.responsePathAsArray(info.path)
368
- const pathString = pathArray.join('.')
369
+ const pathArray = G.responsePathAsArray(info.path);
370
+ const pathString = pathArray.join('.');
369
371
  const ancestors = formListItems.filter((item) => {
370
372
  if (item.type === 'document') {
371
- return pathString.startsWith(item.path)
373
+ return pathString.startsWith(item.path);
372
374
  }
373
- })
374
- const parent = ancestors[ancestors.length - 1]
375
+ });
376
+ const parent = ancestors[ancestors.length - 1];
375
377
  if (parent) {
376
378
  if (parent.type === 'document') {
377
379
  parent.subItems.push({
@@ -379,7 +381,7 @@ export const useGraphQLReducer = (
379
381
  path: pathString,
380
382
  formId: id,
381
383
  subItems: [],
382
- })
384
+ });
383
385
  }
384
386
  } else {
385
387
  formListItems.push({
@@ -387,7 +389,7 @@ export const useGraphQLReducer = (
387
389
  path: pathString,
388
390
  formId: id,
389
391
  subItems: [],
390
- })
392
+ });
391
393
  }
392
394
 
393
395
  if (!existingForm) {
@@ -396,65 +398,65 @@ export const useGraphQLReducer = (
396
398
  tinaSchema,
397
399
  payloadId: payload.id,
398
400
  cms,
399
- })
401
+ });
400
402
  form.subscribe(
401
403
  () => {
402
- setOperationIndex((i) => i + 1)
404
+ setOperationIndex((i) => i + 1);
403
405
  },
404
406
  { values: true }
405
- )
407
+ );
406
408
  return resolveDocument(
407
409
  resolvedDocument,
408
410
  template,
409
411
  form,
410
412
  pathString
411
- )
413
+ );
412
414
  } else {
413
- existingForm.tinaForm.addQuery(payload.id)
415
+ existingForm.tinaForm.addQuery(payload.id);
414
416
  const { template } = getTemplateForDocument(
415
417
  resolvedDocument,
416
418
  tinaSchema
417
- )
418
- existingForm.tinaForm.addQuery(payload.id)
419
+ );
420
+ existingForm.tinaForm.addQuery(payload.id);
419
421
  return resolveDocument(
420
422
  resolvedDocument,
421
423
  template,
422
424
  existingForm.tinaForm,
423
425
  pathString
424
- )
426
+ );
425
427
  }
426
428
  }
427
- return value
429
+ return value;
428
430
  },
429
- })
431
+ });
430
432
  if (result.errors) {
431
433
  result.errors.forEach((error) => {
432
434
  if (
433
435
  error instanceof G.GraphQLError &&
434
436
  error.originalError instanceof NoFormError
435
437
  ) {
436
- const id = error.originalError.id
438
+ const id = error.originalError.id;
437
439
  setDocumentsToResolve((docs) => [
438
440
  ...docs.filter((doc) => doc !== id),
439
441
  id,
440
- ])
442
+ ]);
441
443
  } else {
442
- console.log(error)
444
+ console.log(error);
443
445
  // throw new Error(
444
446
  // `Error processing value change, please contact support`
445
447
  // )
446
448
  }
447
- })
449
+ });
448
450
  } else {
449
451
  if (result.data) {
450
452
  setResults((results) => [
451
453
  ...results.filter((result) => result.id !== payload.id),
452
454
  { id: payload.id, data: result.data },
453
- ])
455
+ ]);
454
456
  }
455
457
  if (activeField) {
456
- setSearchParams({})
457
- const [queryId, eventFieldName] = activeField.split('---')
458
+ setSearchParams({});
459
+ const [queryId, eventFieldName] = activeField.split('---');
458
460
  if (queryId === payload.id) {
459
461
  if (result?.data) {
460
462
  cms.dispatch({
@@ -463,19 +465,19 @@ export const useGraphQLReducer = (
463
465
  result.data,
464
466
  eventFieldName
465
467
  ),
466
- })
468
+ });
467
469
  }
468
470
  cms.dispatch({
469
471
  type: 'sidebar:set-display-state',
470
472
  value: 'openOrFull',
471
- })
473
+ });
472
474
  }
473
475
  }
474
476
  iframe.current?.contentWindow?.postMessage({
475
477
  type: 'updateData',
476
478
  id: payload.id,
477
479
  data: result.data,
478
- })
480
+ });
479
481
  }
480
482
  cms.dispatch({
481
483
  type: 'form-lists:add',
@@ -485,13 +487,13 @@ export const useGraphQLReducer = (
485
487
  items: formListItems,
486
488
  formIds,
487
489
  },
488
- })
490
+ });
489
491
  },
490
492
  [
491
493
  resolvedDocuments.map((doc) => doc._internalSys.path).join('.'),
492
494
  activeField,
493
495
  ]
494
- )
496
+ );
495
497
 
496
498
  const handleMessage = React.useCallback(
497
499
  (event: MessageEvent<PostMessage>) => {
@@ -499,50 +501,50 @@ export const useGraphQLReducer = (
499
501
  cms.dispatch({
500
502
  type: 'forms:set-active-form-id',
501
503
  value: event.data.formId,
502
- })
504
+ });
503
505
  }
504
506
 
505
507
  if (event?.data?.type === 'quick-edit') {
506
508
  cms.dispatch({
507
509
  type: 'set-quick-editing-supported',
508
510
  value: event.data.value,
509
- })
511
+ });
510
512
  iframe.current?.contentWindow?.postMessage({
511
513
  type: 'quickEditEnabled',
512
514
  value: cms.state.sidebarDisplayState === 'open',
513
- })
515
+ });
514
516
  }
515
517
  if (event?.data?.type === 'isEditMode') {
516
518
  iframe?.current?.contentWindow?.postMessage({
517
519
  type: 'tina:editMode',
518
- })
520
+ });
519
521
  }
520
522
  if (event.data.type === 'field:selected') {
521
- const [queryId, eventFieldName] = event.data.fieldName.split('---')
522
- const result = results.find((res) => res.id === queryId)
523
+ const [queryId, eventFieldName] = event.data.fieldName.split('---');
524
+ const result = results.find((res) => res.id === queryId);
523
525
  if (result?.data) {
524
526
  cms.dispatch({
525
527
  type: 'forms:set-active-field-name',
526
528
  value: getFormAndFieldNameFromMetadata(result.data, eventFieldName),
527
- })
529
+ });
528
530
  }
529
531
  cms.dispatch({
530
532
  type: 'sidebar:set-display-state',
531
533
  value: 'openOrFull',
532
- })
534
+ });
533
535
  }
534
536
  if (event.data.type === 'close') {
535
- const payloadSchema = z.object({ id: z.string() })
536
- const { id } = payloadSchema.parse(event.data)
537
+ const payloadSchema = z.object({ id: z.string() });
538
+ const { id } = payloadSchema.parse(event.data);
537
539
  setPayloads((previous) =>
538
540
  previous.filter((payload) => payload.id !== id)
539
- )
540
- setResults((previous) => previous.filter((result) => result.id !== id))
541
+ );
542
+ setResults((previous) => previous.filter((result) => result.id !== id));
541
543
  cms.forms.all().map((form) => {
542
- form.removeQuery(id)
543
- })
544
- cms.removeOrphanedForms()
545
- cms.dispatch({ type: 'form-lists:remove', value: id })
544
+ form.removeQuery(id);
545
+ });
546
+ cms.removeOrphanedForms();
547
+ cms.dispatch({ type: 'form-lists:remove', value: id });
546
548
  }
547
549
  if (event.data.type === 'open') {
548
550
  const payloadSchema = z.object({
@@ -550,60 +552,60 @@ export const useGraphQLReducer = (
550
552
  query: z.string(),
551
553
  variables: z.record(z.unknown()),
552
554
  data: z.record(z.unknown()),
553
- })
554
- const payload = payloadSchema.parse(event.data)
555
+ });
556
+ const payload = payloadSchema.parse(event.data);
555
557
  setPayloads((payloads) => [
556
558
  ...payloads.filter(({ id }) => id !== payload.id),
557
559
  payload,
558
- ])
560
+ ]);
559
561
  }
560
562
  },
561
563
  [cms, JSON.stringify(results)]
562
- )
564
+ );
563
565
 
564
566
  React.useEffect(() => {
565
567
  payloads.forEach((payload) => {
566
568
  if (payload.expandedData) {
567
- processPayload(payload)
569
+ processPayload(payload);
568
570
  }
569
- })
570
- }, [operationIndex])
571
+ });
572
+ }, [operationIndex]);
571
573
 
572
574
  React.useEffect(() => {
573
575
  return () => {
574
- setPayloads([])
575
- setResults([])
576
- cms.removeAllForms()
577
- cms.dispatch({ type: 'form-lists:clear' })
578
- }
579
- }, [url])
576
+ setPayloads([]);
577
+ setResults([]);
578
+ cms.removeAllForms();
579
+ cms.dispatch({ type: 'form-lists:clear' });
580
+ };
581
+ }, [url]);
580
582
 
581
583
  React.useEffect(() => {
582
584
  iframe.current?.contentWindow?.postMessage({
583
585
  type: 'quickEditEnabled',
584
586
  value: cms.state.sidebarDisplayState === 'open',
585
- })
586
- }, [cms.state.sidebarDisplayState])
587
+ });
588
+ }, [cms.state.sidebarDisplayState]);
587
589
 
588
590
  React.useEffect(() => {
589
- cms.dispatch({ type: 'set-edit-mode', value: 'visual' })
591
+ cms.dispatch({ type: 'set-edit-mode', value: 'visual' });
590
592
  if (iframe) {
591
- window.addEventListener('message', handleMessage)
593
+ window.addEventListener('message', handleMessage);
592
594
  }
593
595
 
594
596
  return () => {
595
- window.removeEventListener('message', handleMessage)
596
- cms.removeAllForms()
597
- cms.dispatch({ type: 'set-edit-mode', value: 'basic' })
598
- }
599
- }, [iframe.current, JSON.stringify(results)])
597
+ window.removeEventListener('message', handleMessage);
598
+ cms.removeAllForms();
599
+ cms.dispatch({ type: 'set-edit-mode', value: 'basic' });
600
+ };
601
+ }, [iframe.current, JSON.stringify(results)]);
600
602
 
601
603
  React.useEffect(() => {
602
604
  if (requestErrors.length) {
603
- showErrorModal('Unexpected error querying content', requestErrors, cms)
605
+ showErrorModal('Unexpected error querying content', requestErrors, cms);
604
606
  }
605
- }, [requestErrors])
606
- }
607
+ }, [requestErrors]);
608
+ };
607
609
 
608
610
  const onSubmit = async (
609
611
  collection: Collection<true>,
@@ -611,7 +613,7 @@ const onSubmit = async (
611
613
  payload: Record<string, unknown>,
612
614
  cms: TinaCMS
613
615
  ) => {
614
- const tinaSchema = cms.api.tina.schema
616
+ const tinaSchema = cms.api.tina.schema;
615
617
  try {
616
618
  const mutationString = `#graphql
617
619
  mutation UpdateDocument($collection: String!, $relativePath: String!, $params: DocumentUpdateMutation!) {
@@ -619,7 +621,7 @@ const onSubmit = async (
619
621
  __typename
620
622
  }
621
623
  }
622
- `
624
+ `;
623
625
 
624
626
  await cms.api.tina.request(mutationString, {
625
627
  variables: {
@@ -627,8 +629,8 @@ const onSubmit = async (
627
629
  relativePath: relativePath,
628
630
  params: tinaSchema.transformPayload(collection.name, payload),
629
631
  },
630
- })
631
- cms.alerts.success('Document saved!')
632
+ });
633
+ cms.alerts.success('Document saved!');
632
634
  } catch (e) {
633
635
  cms.alerts.error(() =>
634
636
  ErrorDialog({
@@ -636,12 +638,12 @@ const onSubmit = async (
636
638
  message: 'Tina caught an error while updating the page',
637
639
  error: e,
638
640
  })
639
- )
640
- console.error(e)
641
+ );
642
+ console.error(e);
641
643
  }
642
- }
644
+ };
643
645
 
644
- type Path = (string | number)[]
646
+ type Path = (string | number)[];
645
647
 
646
648
  const resolveDocument = (
647
649
  doc: ResolvedDocument,
@@ -650,20 +652,20 @@ const resolveDocument = (
650
652
  pathToDocument: string
651
653
  ): ResolvedDocument => {
652
654
  // @ts-ignore AnyField and TinaField don't mix
653
- const fields = form.fields as TinaField<true>[]
654
- const id = doc._internalSys.path
655
- const path: Path = []
655
+ const fields = form.fields as TinaField<true>[];
656
+ const id = doc._internalSys.path;
657
+ const path: Path = [];
656
658
  const formValues = resolveFormValue({
657
659
  fields: fields,
658
660
  values: form.values,
659
661
  path,
660
662
  id,
661
663
  pathToDocument,
662
- })
663
- const metadataFields: Record<string, string> = {}
664
+ });
665
+ const metadataFields: Record<string, string> = {};
664
666
  Object.keys(formValues).forEach((key) => {
665
- metadataFields[key] = [...path, key].join('.')
666
- })
667
+ metadataFields[key] = [...path, key].join('.');
668
+ });
667
669
 
668
670
  return {
669
671
  ...formValues,
@@ -679,8 +681,8 @@ const resolveDocument = (
679
681
  _internalSys: doc._internalSys,
680
682
  _internalValues: doc._internalValues,
681
683
  __typename: NAMER.dataTypeName(template.namespace),
682
- }
683
- }
684
+ };
685
+ };
684
686
 
685
687
  const resolveFormValue = <T extends Record<string, unknown>>({
686
688
  fields,
@@ -690,21 +692,21 @@ const resolveFormValue = <T extends Record<string, unknown>>({
690
692
  pathToDocument,
691
693
  }: // tinaSchema,
692
694
  {
693
- fields: TinaField<true>[]
694
- values: T
695
- path: Path
696
- id: string
697
- pathToDocument: string
695
+ fields: TinaField<true>[];
696
+ values: T;
697
+ path: Path;
698
+ id: string;
699
+ pathToDocument: string;
698
700
  // tinaSchema: TinaSchema
699
701
  }): T & { __typename?: string } => {
700
- const accum: Record<string, unknown> = {}
702
+ const accum: Record<string, unknown> = {};
701
703
  fields.forEach((field) => {
702
- const v = values[field.name]
704
+ const v = values[field.name];
703
705
  if (typeof v === 'undefined') {
704
- return
706
+ return;
705
707
  }
706
708
  if (v === null) {
707
- return
709
+ return;
708
710
  }
709
711
  accum[field.name] = resolveFieldValue({
710
712
  field,
@@ -712,10 +714,10 @@ const resolveFormValue = <T extends Record<string, unknown>>({
712
714
  path,
713
715
  id,
714
716
  pathToDocument,
715
- })
716
- })
717
- return accum as T & { __typename?: string }
718
- }
717
+ });
718
+ });
719
+ return accum as T & { __typename?: string };
720
+ };
719
721
  const resolveFieldValue = ({
720
722
  field,
721
723
  value,
@@ -723,11 +725,11 @@ const resolveFieldValue = ({
723
725
  id,
724
726
  pathToDocument,
725
727
  }: {
726
- field: TinaField<true>
727
- value: unknown
728
- path: Path
729
- id: string
730
- pathToDocument: string
728
+ field: TinaField<true>;
729
+ value: unknown;
730
+ path: Path;
731
+ id: string;
732
+ pathToDocument: string;
731
733
  }) => {
732
734
  switch (field.type) {
733
735
  case 'object': {
@@ -735,15 +737,17 @@ const resolveFieldValue = ({
735
737
  if (field.list) {
736
738
  if (Array.isArray(value)) {
737
739
  return value.map((item, index) => {
738
- const template = field.templates[item._template]
740
+ const template = field.templates[item._template];
739
741
  if (typeof template === 'string') {
740
- throw new Error('Global templates not supported')
742
+ throw new Error('Global templates not supported');
741
743
  }
742
- const nextPath = [...path, field.name, index]
743
- const metadataFields: Record<string, string> = {}
744
+ const nextPath = [...path, field.name, index];
745
+ const metadataFields: Record<string, string> = {};
744
746
  template.fields.forEach((field) => {
745
- metadataFields[field.name] = [...nextPath, field.name].join('.')
746
- })
747
+ metadataFields[field.name] = [...nextPath, field.name].join(
748
+ '.'
749
+ );
750
+ });
747
751
  return {
748
752
  __typename: NAMER.dataTypeName(template.namespace),
749
753
  _tina_metadata: {
@@ -759,29 +763,29 @@ const resolveFieldValue = ({
759
763
  id,
760
764
  pathToDocument,
761
765
  }),
762
- }
763
- })
766
+ };
767
+ });
764
768
  }
765
769
  } else {
766
770
  // not implemented
767
771
  }
768
772
  }
769
773
 
770
- const templateFields = field.fields
774
+ const templateFields = field.fields;
771
775
  if (typeof templateFields === 'string') {
772
- throw new Error('Global templates not supported')
776
+ throw new Error('Global templates not supported');
773
777
  }
774
778
  if (!templateFields) {
775
- throw new Error(`Expected to find sub-fields on field ${field.name}`)
779
+ throw new Error(`Expected to find sub-fields on field ${field.name}`);
776
780
  }
777
781
  if (field.list) {
778
782
  if (Array.isArray(value)) {
779
783
  return value.map((item, index) => {
780
- const nextPath = [...path, field.name, index]
781
- const metadataFields: Record<string, string> = {}
784
+ const nextPath = [...path, field.name, index];
785
+ const metadataFields: Record<string, string> = {};
782
786
  templateFields.forEach((field) => {
783
- metadataFields[field.name] = [...nextPath, field.name].join('.')
784
- })
787
+ metadataFields[field.name] = [...nextPath, field.name].join('.');
788
+ });
785
789
  return {
786
790
  __typename: NAMER.dataTypeName(field.namespace),
787
791
  _tina_metadata: {
@@ -797,15 +801,15 @@ const resolveFieldValue = ({
797
801
  id,
798
802
  pathToDocument,
799
803
  }),
800
- }
801
- })
804
+ };
805
+ });
802
806
  }
803
807
  } else {
804
- const nextPath = [...path, field.name]
805
- const metadataFields: Record<string, string> = {}
808
+ const nextPath = [...path, field.name];
809
+ const metadataFields: Record<string, string> = {};
806
810
  templateFields.forEach((field) => {
807
- metadataFields[field.name] = [...nextPath, field.name].join('.')
808
- })
811
+ metadataFields[field.name] = [...nextPath, field.name].join('.');
812
+ });
809
813
  return {
810
814
  __typename: NAMER.dataTypeName(field.namespace),
811
815
  _tina_metadata: {
@@ -821,18 +825,21 @@ const resolveFieldValue = ({
821
825
  id,
822
826
  pathToDocument,
823
827
  }),
824
- }
828
+ };
825
829
  }
826
830
  }
827
831
  default: {
828
- return value
832
+ return value;
829
833
  }
830
834
  }
831
- }
835
+ };
832
836
 
833
837
  const getDocument = async (id: string, tina: Client) => {
834
838
  const response = await tina.request<{
835
- node: { _internalSys: SystemInfo; _internalValues: Record<string, unknown> }
839
+ node: {
840
+ _internalSys: SystemInfo;
841
+ _internalValues: Record<string, unknown>;
842
+ };
836
843
  }>(
837
844
  `query GetNode($id: String!) {
838
845
  node(id: $id) {
@@ -865,29 +872,29 @@ _internalSys: _sys {
865
872
  }
866
873
  }`,
867
874
  { variables: { id: id } }
868
- )
869
- return response.node
870
- }
875
+ );
876
+ return response.node;
877
+ };
871
878
 
872
879
  const expandPayload = async (
873
880
  payload: Payload,
874
881
  cms: TinaCMS
875
882
  ): Promise<Payload> => {
876
- const { query, variables } = payload
877
- const documentNode = G.parse(query)
878
- const expandedDocumentNode = expandQuery({ schema, documentNode })
879
- const expandedQuery = G.print(expandedDocumentNode)
883
+ const { query, variables } = payload;
884
+ const documentNode = G.parse(query);
885
+ const expandedDocumentNode = expandQuery({ schema, documentNode });
886
+ const expandedQuery = G.print(expandedDocumentNode);
880
887
  const expandedData = await cms.api.tina.request<object>(expandedQuery, {
881
888
  variables,
882
- })
889
+ });
883
890
 
884
891
  const expandedDocumentNodeForResolver = expandQuery({
885
892
  schema: schemaForResolver,
886
893
  documentNode,
887
- })
888
- const expandedQueryForResolver = G.print(expandedDocumentNodeForResolver)
889
- return { ...payload, expandedQuery, expandedData, expandedQueryForResolver }
890
- }
894
+ });
895
+ const expandedQueryForResolver = G.print(expandedDocumentNodeForResolver);
896
+ return { ...payload, expandedQuery, expandedData, expandedQueryForResolver };
897
+ };
891
898
 
892
899
  /**
893
900
  * When we resolve the graphql data we check for these errors,
@@ -895,11 +902,11 @@ const expandPayload = async (
895
902
  * process it once we have that document
896
903
  */
897
904
  class NoFormError extends Error {
898
- id: string
905
+ id: string;
899
906
  constructor(msg: string, id: string) {
900
- super(msg)
901
- this.id = id
902
- Object.setPrototypeOf(this, NoFormError.prototype)
907
+ super(msg);
908
+ this.id = id;
909
+ Object.setPrototypeOf(this, NoFormError.prototype);
903
910
  }
904
911
  }
905
912
 
@@ -907,22 +914,22 @@ const getTemplateForDocument = (
907
914
  resolvedDocument: ResolvedDocument,
908
915
  tinaSchema: TinaSchema
909
916
  ) => {
910
- const id = resolvedDocument._internalSys.path
911
- let collection: Collection<true> | undefined
917
+ const id = resolvedDocument._internalSys.path;
918
+ let collection: Collection<true> | undefined;
912
919
  try {
913
- collection = tinaSchema.getCollectionByFullPath(id)
920
+ collection = tinaSchema.getCollectionByFullPath(id);
914
921
  } catch (e) {}
915
922
 
916
923
  if (!collection) {
917
- throw new Error(`Unable to determine collection for path ${id}`)
924
+ throw new Error(`Unable to determine collection for path ${id}`);
918
925
  }
919
926
 
920
927
  const template = tinaSchema.getTemplateForData({
921
928
  data: resolvedDocument._internalValues,
922
929
  collection,
923
- })
924
- return { template, collection }
925
- }
930
+ });
931
+ return { template, collection };
932
+ };
926
933
 
927
934
  const buildForm = ({
928
935
  resolvedDocument,
@@ -930,18 +937,18 @@ const buildForm = ({
930
937
  payloadId,
931
938
  cms,
932
939
  }: {
933
- resolvedDocument: ResolvedDocument
934
- tinaSchema: TinaSchema
935
- payloadId: string
936
- cms: TinaCMS
940
+ resolvedDocument: ResolvedDocument;
941
+ tinaSchema: TinaSchema;
942
+ payloadId: string;
943
+ cms: TinaCMS;
937
944
  }) => {
938
945
  const { template, collection } = getTemplateForDocument(
939
946
  resolvedDocument,
940
947
  tinaSchema
941
- )
942
- const id = resolvedDocument._internalSys.path
943
- let form: Form | undefined
944
- let shouldRegisterForm = true
948
+ );
949
+ const id = resolvedDocument._internalSys.path;
950
+ let form: Form | undefined;
951
+ let shouldRegisterForm = true;
945
952
  const formConfig: FormOptions<any> = {
946
953
  id,
947
954
  initialValues: resolvedDocument._internalValues,
@@ -954,10 +961,10 @@ const buildForm = ({
954
961
  cms
955
962
  ),
956
963
  label: collection.label || collection.name,
957
- }
964
+ };
958
965
  if (tinaSchema.config.config?.formifyCallback) {
959
966
  const callback = tinaSchema.config.config
960
- ?.formifyCallback as FormifyCallback
967
+ ?.formifyCallback as FormifyCallback;
961
968
  form =
962
969
  callback(
963
970
  {
@@ -967,30 +974,30 @@ const buildForm = ({
967
974
  formConfig,
968
975
  },
969
976
  cms
970
- ) || undefined
977
+ ) || undefined;
971
978
  if (!form) {
972
979
  // If the form isn't created from formify, we still
973
980
  // need it, just don't show it to the user.
974
- shouldRegisterForm = false
975
- form = new Form(formConfig)
981
+ shouldRegisterForm = false;
982
+ form = new Form(formConfig);
976
983
  }
977
984
  } else {
978
985
  if (collection.ui?.global) {
979
- form = createGlobalForm(formConfig)
986
+ form = createGlobalForm(formConfig);
980
987
  } else {
981
- form = createForm(formConfig)
988
+ form = createForm(formConfig);
982
989
  }
983
990
  }
984
991
  if (form) {
985
992
  if (shouldRegisterForm) {
986
993
  if (collection.ui?.global) {
987
- cms.plugins.add(new GlobalFormPlugin(form))
994
+ cms.plugins.add(new GlobalFormPlugin(form));
988
995
  }
989
- cms.dispatch({ type: 'forms:add', value: form })
996
+ cms.dispatch({ type: 'forms:add', value: form });
990
997
  }
991
998
  }
992
999
  if (!form) {
993
- throw new Error(`No form registered for ${id}.`)
1000
+ throw new Error(`No form registered for ${id}.`);
994
1001
  }
995
- return { template, form }
996
- }
1002
+ return { template, form };
1003
+ };