@tinacms/app 0.0.0-71744f0-20241229224727 → 0.0.0-71fbfa7-20250315003438

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,57 +487,64 @@ 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>) => {
500
+ if (event.data.type === 'user-select-form') {
501
+ cms.dispatch({
502
+ type: 'forms:set-active-form-id',
503
+ value: event.data.formId,
504
+ });
505
+ }
506
+
498
507
  if (event?.data?.type === 'quick-edit') {
499
508
  cms.dispatch({
500
509
  type: 'set-quick-editing-supported',
501
510
  value: event.data.value,
502
- })
511
+ });
503
512
  iframe.current?.contentWindow?.postMessage({
504
513
  type: 'quickEditEnabled',
505
514
  value: cms.state.sidebarDisplayState === 'open',
506
- })
515
+ });
507
516
  }
508
517
  if (event?.data?.type === 'isEditMode') {
509
518
  iframe?.current?.contentWindow?.postMessage({
510
519
  type: 'tina:editMode',
511
- })
520
+ });
512
521
  }
513
522
  if (event.data.type === 'field:selected') {
514
- const [queryId, eventFieldName] = event.data.fieldName.split('---')
515
- 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);
516
525
  if (result?.data) {
517
526
  cms.dispatch({
518
527
  type: 'forms:set-active-field-name',
519
528
  value: getFormAndFieldNameFromMetadata(result.data, eventFieldName),
520
- })
529
+ });
521
530
  }
522
531
  cms.dispatch({
523
532
  type: 'sidebar:set-display-state',
524
533
  value: 'openOrFull',
525
- })
534
+ });
526
535
  }
527
536
  if (event.data.type === 'close') {
528
- const payloadSchema = z.object({ id: z.string() })
529
- const { id } = payloadSchema.parse(event.data)
537
+ const payloadSchema = z.object({ id: z.string() });
538
+ const { id } = payloadSchema.parse(event.data);
530
539
  setPayloads((previous) =>
531
540
  previous.filter((payload) => payload.id !== id)
532
- )
533
- setResults((previous) => previous.filter((result) => result.id !== id))
541
+ );
542
+ setResults((previous) => previous.filter((result) => result.id !== id));
534
543
  cms.forms.all().map((form) => {
535
- form.removeQuery(id)
536
- })
537
- cms.removeOrphanedForms()
538
- 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 });
539
548
  }
540
549
  if (event.data.type === 'open') {
541
550
  const payloadSchema = z.object({
@@ -543,60 +552,60 @@ export const useGraphQLReducer = (
543
552
  query: z.string(),
544
553
  variables: z.record(z.unknown()),
545
554
  data: z.record(z.unknown()),
546
- })
547
- const payload = payloadSchema.parse(event.data)
555
+ });
556
+ const payload = payloadSchema.parse(event.data);
548
557
  setPayloads((payloads) => [
549
558
  ...payloads.filter(({ id }) => id !== payload.id),
550
559
  payload,
551
- ])
560
+ ]);
552
561
  }
553
562
  },
554
563
  [cms, JSON.stringify(results)]
555
- )
564
+ );
556
565
 
557
566
  React.useEffect(() => {
558
567
  payloads.forEach((payload) => {
559
568
  if (payload.expandedData) {
560
- processPayload(payload)
569
+ processPayload(payload);
561
570
  }
562
- })
563
- }, [operationIndex])
571
+ });
572
+ }, [operationIndex]);
564
573
 
565
574
  React.useEffect(() => {
566
575
  return () => {
567
- setPayloads([])
568
- setResults([])
569
- cms.removeAllForms()
570
- cms.dispatch({ type: 'form-lists:clear' })
571
- }
572
- }, [url])
576
+ setPayloads([]);
577
+ setResults([]);
578
+ cms.removeAllForms();
579
+ cms.dispatch({ type: 'form-lists:clear' });
580
+ };
581
+ }, [url]);
573
582
 
574
583
  React.useEffect(() => {
575
584
  iframe.current?.contentWindow?.postMessage({
576
585
  type: 'quickEditEnabled',
577
586
  value: cms.state.sidebarDisplayState === 'open',
578
- })
579
- }, [cms.state.sidebarDisplayState])
587
+ });
588
+ }, [cms.state.sidebarDisplayState]);
580
589
 
581
590
  React.useEffect(() => {
582
- cms.dispatch({ type: 'set-edit-mode', value: 'visual' })
591
+ cms.dispatch({ type: 'set-edit-mode', value: 'visual' });
583
592
  if (iframe) {
584
- window.addEventListener('message', handleMessage)
593
+ window.addEventListener('message', handleMessage);
585
594
  }
586
595
 
587
596
  return () => {
588
- window.removeEventListener('message', handleMessage)
589
- cms.removeAllForms()
590
- cms.dispatch({ type: 'set-edit-mode', value: 'basic' })
591
- }
592
- }, [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)]);
593
602
 
594
603
  React.useEffect(() => {
595
604
  if (requestErrors.length) {
596
- showErrorModal('Unexpected error querying content', requestErrors, cms)
605
+ showErrorModal('Unexpected error querying content', requestErrors, cms);
597
606
  }
598
- }, [requestErrors])
599
- }
607
+ }, [requestErrors]);
608
+ };
600
609
 
601
610
  const onSubmit = async (
602
611
  collection: Collection<true>,
@@ -604,7 +613,7 @@ const onSubmit = async (
604
613
  payload: Record<string, unknown>,
605
614
  cms: TinaCMS
606
615
  ) => {
607
- const tinaSchema = cms.api.tina.schema
616
+ const tinaSchema = cms.api.tina.schema;
608
617
  try {
609
618
  const mutationString = `#graphql
610
619
  mutation UpdateDocument($collection: String!, $relativePath: String!, $params: DocumentUpdateMutation!) {
@@ -612,7 +621,7 @@ const onSubmit = async (
612
621
  __typename
613
622
  }
614
623
  }
615
- `
624
+ `;
616
625
 
617
626
  await cms.api.tina.request(mutationString, {
618
627
  variables: {
@@ -620,8 +629,8 @@ const onSubmit = async (
620
629
  relativePath: relativePath,
621
630
  params: tinaSchema.transformPayload(collection.name, payload),
622
631
  },
623
- })
624
- cms.alerts.success('Document saved!')
632
+ });
633
+ cms.alerts.success('Document saved!');
625
634
  } catch (e) {
626
635
  cms.alerts.error(() =>
627
636
  ErrorDialog({
@@ -629,12 +638,12 @@ const onSubmit = async (
629
638
  message: 'Tina caught an error while updating the page',
630
639
  error: e,
631
640
  })
632
- )
633
- console.error(e)
641
+ );
642
+ console.error(e);
634
643
  }
635
- }
644
+ };
636
645
 
637
- type Path = (string | number)[]
646
+ type Path = (string | number)[];
638
647
 
639
648
  const resolveDocument = (
640
649
  doc: ResolvedDocument,
@@ -643,20 +652,20 @@ const resolveDocument = (
643
652
  pathToDocument: string
644
653
  ): ResolvedDocument => {
645
654
  // @ts-ignore AnyField and TinaField don't mix
646
- const fields = form.fields as TinaField<true>[]
647
- const id = doc._internalSys.path
648
- const path: Path = []
655
+ const fields = form.fields as TinaField<true>[];
656
+ const id = doc._internalSys.path;
657
+ const path: Path = [];
649
658
  const formValues = resolveFormValue({
650
659
  fields: fields,
651
660
  values: form.values,
652
661
  path,
653
662
  id,
654
663
  pathToDocument,
655
- })
656
- const metadataFields: Record<string, string> = {}
664
+ });
665
+ const metadataFields: Record<string, string> = {};
657
666
  Object.keys(formValues).forEach((key) => {
658
- metadataFields[key] = [...path, key].join('.')
659
- })
667
+ metadataFields[key] = [...path, key].join('.');
668
+ });
660
669
 
661
670
  return {
662
671
  ...formValues,
@@ -672,8 +681,8 @@ const resolveDocument = (
672
681
  _internalSys: doc._internalSys,
673
682
  _internalValues: doc._internalValues,
674
683
  __typename: NAMER.dataTypeName(template.namespace),
675
- }
676
- }
684
+ };
685
+ };
677
686
 
678
687
  const resolveFormValue = <T extends Record<string, unknown>>({
679
688
  fields,
@@ -683,21 +692,21 @@ const resolveFormValue = <T extends Record<string, unknown>>({
683
692
  pathToDocument,
684
693
  }: // tinaSchema,
685
694
  {
686
- fields: TinaField<true>[]
687
- values: T
688
- path: Path
689
- id: string
690
- pathToDocument: string
695
+ fields: TinaField<true>[];
696
+ values: T;
697
+ path: Path;
698
+ id: string;
699
+ pathToDocument: string;
691
700
  // tinaSchema: TinaSchema
692
701
  }): T & { __typename?: string } => {
693
- const accum: Record<string, unknown> = {}
702
+ const accum: Record<string, unknown> = {};
694
703
  fields.forEach((field) => {
695
- const v = values[field.name]
704
+ const v = values[field.name];
696
705
  if (typeof v === 'undefined') {
697
- return
706
+ return;
698
707
  }
699
708
  if (v === null) {
700
- return
709
+ return;
701
710
  }
702
711
  accum[field.name] = resolveFieldValue({
703
712
  field,
@@ -705,10 +714,10 @@ const resolveFormValue = <T extends Record<string, unknown>>({
705
714
  path,
706
715
  id,
707
716
  pathToDocument,
708
- })
709
- })
710
- return accum as T & { __typename?: string }
711
- }
717
+ });
718
+ });
719
+ return accum as T & { __typename?: string };
720
+ };
712
721
  const resolveFieldValue = ({
713
722
  field,
714
723
  value,
@@ -716,11 +725,11 @@ const resolveFieldValue = ({
716
725
  id,
717
726
  pathToDocument,
718
727
  }: {
719
- field: TinaField<true>
720
- value: unknown
721
- path: Path
722
- id: string
723
- pathToDocument: string
728
+ field: TinaField<true>;
729
+ value: unknown;
730
+ path: Path;
731
+ id: string;
732
+ pathToDocument: string;
724
733
  }) => {
725
734
  switch (field.type) {
726
735
  case 'object': {
@@ -728,15 +737,17 @@ const resolveFieldValue = ({
728
737
  if (field.list) {
729
738
  if (Array.isArray(value)) {
730
739
  return value.map((item, index) => {
731
- const template = field.templates[item._template]
740
+ const template = field.templates[item._template];
732
741
  if (typeof template === 'string') {
733
- throw new Error('Global templates not supported')
742
+ throw new Error('Global templates not supported');
734
743
  }
735
- const nextPath = [...path, field.name, index]
736
- const metadataFields: Record<string, string> = {}
744
+ const nextPath = [...path, field.name, index];
745
+ const metadataFields: Record<string, string> = {};
737
746
  template.fields.forEach((field) => {
738
- metadataFields[field.name] = [...nextPath, field.name].join('.')
739
- })
747
+ metadataFields[field.name] = [...nextPath, field.name].join(
748
+ '.'
749
+ );
750
+ });
740
751
  return {
741
752
  __typename: NAMER.dataTypeName(template.namespace),
742
753
  _tina_metadata: {
@@ -752,29 +763,29 @@ const resolveFieldValue = ({
752
763
  id,
753
764
  pathToDocument,
754
765
  }),
755
- }
756
- })
766
+ };
767
+ });
757
768
  }
758
769
  } else {
759
770
  // not implemented
760
771
  }
761
772
  }
762
773
 
763
- const templateFields = field.fields
774
+ const templateFields = field.fields;
764
775
  if (typeof templateFields === 'string') {
765
- throw new Error('Global templates not supported')
776
+ throw new Error('Global templates not supported');
766
777
  }
767
778
  if (!templateFields) {
768
- 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}`);
769
780
  }
770
781
  if (field.list) {
771
782
  if (Array.isArray(value)) {
772
783
  return value.map((item, index) => {
773
- const nextPath = [...path, field.name, index]
774
- const metadataFields: Record<string, string> = {}
784
+ const nextPath = [...path, field.name, index];
785
+ const metadataFields: Record<string, string> = {};
775
786
  templateFields.forEach((field) => {
776
- metadataFields[field.name] = [...nextPath, field.name].join('.')
777
- })
787
+ metadataFields[field.name] = [...nextPath, field.name].join('.');
788
+ });
778
789
  return {
779
790
  __typename: NAMER.dataTypeName(field.namespace),
780
791
  _tina_metadata: {
@@ -790,15 +801,15 @@ const resolveFieldValue = ({
790
801
  id,
791
802
  pathToDocument,
792
803
  }),
793
- }
794
- })
804
+ };
805
+ });
795
806
  }
796
807
  } else {
797
- const nextPath = [...path, field.name]
798
- const metadataFields: Record<string, string> = {}
808
+ const nextPath = [...path, field.name];
809
+ const metadataFields: Record<string, string> = {};
799
810
  templateFields.forEach((field) => {
800
- metadataFields[field.name] = [...nextPath, field.name].join('.')
801
- })
811
+ metadataFields[field.name] = [...nextPath, field.name].join('.');
812
+ });
802
813
  return {
803
814
  __typename: NAMER.dataTypeName(field.namespace),
804
815
  _tina_metadata: {
@@ -814,18 +825,21 @@ const resolveFieldValue = ({
814
825
  id,
815
826
  pathToDocument,
816
827
  }),
817
- }
828
+ };
818
829
  }
819
830
  }
820
831
  default: {
821
- return value
832
+ return value;
822
833
  }
823
834
  }
824
- }
835
+ };
825
836
 
826
837
  const getDocument = async (id: string, tina: Client) => {
827
838
  const response = await tina.request<{
828
- node: { _internalSys: SystemInfo; _internalValues: Record<string, unknown> }
839
+ node: {
840
+ _internalSys: SystemInfo;
841
+ _internalValues: Record<string, unknown>;
842
+ };
829
843
  }>(
830
844
  `query GetNode($id: String!) {
831
845
  node(id: $id) {
@@ -858,29 +872,29 @@ _internalSys: _sys {
858
872
  }
859
873
  }`,
860
874
  { variables: { id: id } }
861
- )
862
- return response.node
863
- }
875
+ );
876
+ return response.node;
877
+ };
864
878
 
865
879
  const expandPayload = async (
866
880
  payload: Payload,
867
881
  cms: TinaCMS
868
882
  ): Promise<Payload> => {
869
- const { query, variables } = payload
870
- const documentNode = G.parse(query)
871
- const expandedDocumentNode = expandQuery({ schema, documentNode })
872
- 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);
873
887
  const expandedData = await cms.api.tina.request<object>(expandedQuery, {
874
888
  variables,
875
- })
889
+ });
876
890
 
877
891
  const expandedDocumentNodeForResolver = expandQuery({
878
892
  schema: schemaForResolver,
879
893
  documentNode,
880
- })
881
- const expandedQueryForResolver = G.print(expandedDocumentNodeForResolver)
882
- return { ...payload, expandedQuery, expandedData, expandedQueryForResolver }
883
- }
894
+ });
895
+ const expandedQueryForResolver = G.print(expandedDocumentNodeForResolver);
896
+ return { ...payload, expandedQuery, expandedData, expandedQueryForResolver };
897
+ };
884
898
 
885
899
  /**
886
900
  * When we resolve the graphql data we check for these errors,
@@ -888,11 +902,11 @@ const expandPayload = async (
888
902
  * process it once we have that document
889
903
  */
890
904
  class NoFormError extends Error {
891
- id: string
905
+ id: string;
892
906
  constructor(msg: string, id: string) {
893
- super(msg)
894
- this.id = id
895
- Object.setPrototypeOf(this, NoFormError.prototype)
907
+ super(msg);
908
+ this.id = id;
909
+ Object.setPrototypeOf(this, NoFormError.prototype);
896
910
  }
897
911
  }
898
912
 
@@ -900,22 +914,22 @@ const getTemplateForDocument = (
900
914
  resolvedDocument: ResolvedDocument,
901
915
  tinaSchema: TinaSchema
902
916
  ) => {
903
- const id = resolvedDocument._internalSys.path
904
- let collection: Collection<true> | undefined
917
+ const id = resolvedDocument._internalSys.path;
918
+ let collection: Collection<true> | undefined;
905
919
  try {
906
- collection = tinaSchema.getCollectionByFullPath(id)
920
+ collection = tinaSchema.getCollectionByFullPath(id);
907
921
  } catch (e) {}
908
922
 
909
923
  if (!collection) {
910
- throw new Error(`Unable to determine collection for path ${id}`)
924
+ throw new Error(`Unable to determine collection for path ${id}`);
911
925
  }
912
926
 
913
927
  const template = tinaSchema.getTemplateForData({
914
928
  data: resolvedDocument._internalValues,
915
929
  collection,
916
- })
917
- return { template, collection }
918
- }
930
+ });
931
+ return { template, collection };
932
+ };
919
933
 
920
934
  const buildForm = ({
921
935
  resolvedDocument,
@@ -923,18 +937,18 @@ const buildForm = ({
923
937
  payloadId,
924
938
  cms,
925
939
  }: {
926
- resolvedDocument: ResolvedDocument
927
- tinaSchema: TinaSchema
928
- payloadId: string
929
- cms: TinaCMS
940
+ resolvedDocument: ResolvedDocument;
941
+ tinaSchema: TinaSchema;
942
+ payloadId: string;
943
+ cms: TinaCMS;
930
944
  }) => {
931
945
  const { template, collection } = getTemplateForDocument(
932
946
  resolvedDocument,
933
947
  tinaSchema
934
- )
935
- const id = resolvedDocument._internalSys.path
936
- let form: Form | undefined
937
- let shouldRegisterForm = true
948
+ );
949
+ const id = resolvedDocument._internalSys.path;
950
+ let form: Form | undefined;
951
+ let shouldRegisterForm = true;
938
952
  const formConfig: FormOptions<any> = {
939
953
  id,
940
954
  initialValues: resolvedDocument._internalValues,
@@ -947,10 +961,10 @@ const buildForm = ({
947
961
  cms
948
962
  ),
949
963
  label: collection.label || collection.name,
950
- }
964
+ };
951
965
  if (tinaSchema.config.config?.formifyCallback) {
952
966
  const callback = tinaSchema.config.config
953
- ?.formifyCallback as FormifyCallback
967
+ ?.formifyCallback as FormifyCallback;
954
968
  form =
955
969
  callback(
956
970
  {
@@ -960,30 +974,30 @@ const buildForm = ({
960
974
  formConfig,
961
975
  },
962
976
  cms
963
- ) || undefined
977
+ ) || undefined;
964
978
  if (!form) {
965
979
  // If the form isn't created from formify, we still
966
980
  // need it, just don't show it to the user.
967
- shouldRegisterForm = false
968
- form = new Form(formConfig)
981
+ shouldRegisterForm = false;
982
+ form = new Form(formConfig);
969
983
  }
970
984
  } else {
971
985
  if (collection.ui?.global) {
972
- form = createGlobalForm(formConfig)
986
+ form = createGlobalForm(formConfig);
973
987
  } else {
974
- form = createForm(formConfig)
988
+ form = createForm(formConfig);
975
989
  }
976
990
  }
977
991
  if (form) {
978
992
  if (shouldRegisterForm) {
979
993
  if (collection.ui?.global) {
980
- cms.plugins.add(new GlobalFormPlugin(form))
994
+ cms.plugins.add(new GlobalFormPlugin(form));
981
995
  }
982
- cms.dispatch({ type: 'forms:add', value: form })
996
+ cms.dispatch({ type: 'forms:add', value: form });
983
997
  }
984
998
  }
985
999
  if (!form) {
986
- throw new Error(`No form registered for ${id}.`)
1000
+ throw new Error(`No form registered for ${id}.`);
987
1001
  }
988
- return { template, form }
989
- }
1002
+ return { template, form };
1003
+ };